From abe6536eba0653679e2159ce2bc5a119333cc15b Mon Sep 17 00:00:00 2001 From: Yamagi Burmeister Date: Tue, 4 Oct 2011 10:07:55 +0000 Subject: [PATCH] Add knightmares "extractfuncs" tool to generate the function- and structtables for his savegame system --- ChangeLog.txt | 27 + Conscript | 9 + ef_local.h | Bin 0 -> 4758 bytes extractfuncs.bat | 4 + extractfuncs.c | 834 ++++++++++ extractfuncs.exe | Bin 0 -> 111616 bytes extractfuncs.sln | 20 + extractfuncs_2008.vcproj | 352 +++++ l_log.c | 204 +++ l_log.h | 63 + l_memory.c | 455 ++++++ l_memory.h | 84 + l_precomp.c | 3225 ++++++++++++++++++++++++++++++++++++++ l_precomp.h | 162 ++ l_script.c | 1423 +++++++++++++++++ l_script.h | 270 ++++ 16 files changed, 7132 insertions(+) create mode 100644 ChangeLog.txt create mode 100644 Conscript create mode 100644 ef_local.h create mode 100644 extractfuncs.bat create mode 100644 extractfuncs.c create mode 100644 extractfuncs.exe create mode 100644 extractfuncs.sln create mode 100644 extractfuncs_2008.vcproj create mode 100644 l_log.c create mode 100644 l_log.h create mode 100644 l_memory.c create mode 100644 l_memory.h create mode 100644 l_precomp.c create mode 100644 l_precomp.h create mode 100644 l_script.c create mode 100644 l_script.h diff --git a/ChangeLog.txt b/ChangeLog.txt new file mode 100644 index 0000000..35c8d0f --- /dev/null +++ b/ChangeLog.txt @@ -0,0 +1,27 @@ +2011-07-19 Knightmare + +Added -t option to export data type + +Changed command line syntax to: + +Extractfuncs [ ..] [-t ] [-o ] [-d ] + +Added skipping the parsing of macros with parameters (not relevant to extraction) + +2001-12-07 Timothee Besset + +Imported from the Wolf MP version, Mac/Linux friendly version +Fixed argbase bug in *nix main +Escape BoxOnPlaneSide on linux (taken out by preprocessing) + +2001-11-02 Timothee Besset + +Modified extractfuncs to works on linux +Would still need to integrate it correctly with the build system +Changed the command line syntax of the linux ver: +screwup [-o ] [ ..] + +if none specified, is "g_funcs" + +on linux at least, those header files need to be tweaked by hand +because unresolved externs are not ignored by gcc (otherwise harmless on win32) diff --git a/Conscript b/Conscript new file mode 100644 index 0000000..f8be465 --- /dev/null +++ b/Conscript @@ -0,0 +1,9 @@ +# extractfuncs building + +$env = new cons( + CC => 'gcc', + CFLAGS => '-g -DSCREWUP ' + ); + +Program $env 'extractfuncs', 'extractfuncs.c', 'l_log.c', 'l_memory.c', 'l_precomp.c', 'l_script.c'; +Install $env '#', 'extractfuncs'; diff --git a/ef_local.h b/ef_local.h new file mode 100644 index 0000000000000000000000000000000000000000..8b83fc893ed372edacf1429ab27ebe207936d337 GIT binary patch literal 4758 zcmd6qYj0ac5Qg_F=T{u1UrGf|8-xVqA_c2SBiuB^jzYP}vXAZ9)IK)8)T9-^0)8*> zJ~LjO*zqN;Dp9SH_3qBj<(5w1lw)}7;U7Al&nx(n~jIF+?(=~p5N!!Q!{ zSUj#mSF+M@U%MY`eiXj6@o6{+54C!zy`Jtlw~lD%k~B7s6-gxNnq0T_2YZCttyuju!d8#H` zFQrRW=x)OHZ()daZr|PRfUjc!s`z{&v~D8(SbNvnn`zDr!_+(gx6Y+(^Sk&mVjQnvp4Hw!y7DaAU4S6gT7Bk82i)W@) z#b%FHEFqFp`90E1%#TD<(=>^4kIWu!5VJRJPs5RRT-S#5?rD`<%#jIx9}8cZPGkk= zY4}Wk%N8u_M%02$BJaXCA`CCGBT6DCJZe+RF-K@sUdiJ5?fkvYdm!Wa9g*ppHXy@} zYwG@+i?VM%Xv%i{InmtF+!sE{A|j|DCyL#t;ko=*6WyUa`9kxhew}Nuu4(5h?ZFc{ zAPy#N!_{k(XJF6r7=M%9+#plOhS5|$b<`7O{}b^pORn=j4BttQis^!MkIN%lpNh7s zfv=zI7CI7H=vNa(S(;aD#qNc6&qP<%Ezh;8bcqO2-`*`&I}4r26|Zkp%c(2_=ZR!d zvtAfBNhG zj2O{xmnGOrJw!*U$Zyg!6EFC&3)Z|8c!@Y&mF!ijsB3ufefkjOc1tCz2O9B3M4!IJ z15rm74}~)p;8RB`(Ogtxujl@fQrQb^-&d`k%lB0AuGy36SMg@YY+vug#H#k~PGfzn z(W|uOCr6G<`(2)GYmHZzv&DTnmxtY6$ETw;x^3=EyiP`KJtmMe(ctX?eGvDY_H2kR zY>~@_UjC|Z7gX{^)Y0t}9`jdxIWuCND>NA?x16RSJ-o-;6|+?HsNFyaQAb=4;@-1i zJyN?nw@kT99&PL1PR!zxRf?*7g)fNuN7f^f+vnnug_o*%{wB4JmQHXiZRt&k+0^L2 z2%lS5d7-E|TzKF^?a&_x9jg94^K>RlX0pR8CGkog4rN1E^=}&$9&c`?gJPh+sUmb2 zKeBzQRTE2Tiv2L}{Us}Eg zx?yh%?L@JK&2(kEXh)*th49+7i<&Cn_#mH(do3wcX*wzTAKpDG%^R)#E%Ml=>!xb` zsk2lStSL@Zaw=P{UJ7o!p!XKM@ECDJS3@^O_i|=E5^K7iy7p?CKhoF>zi6!2$vghp zBJZ)*zy5d$>Herrb@?li&WU(Y(aFP9-@>_m@d}~e>PcH(-h3Xv&^A$kSQdU(-**^( zlzeRZUiO`aU#+tQrP~99qRz2bNDVP^4XulwAC2#7yW85xza*Er&UzSC; z@lrPC_Km~YO^l1vx=y?|acW2UF1*(1T=1Qk!&kyuPiw&F6L+>M#-_q>y7kzoUh&R`vkQ;p-qse8wl9v8r zJ&`=#c%l~Vi<{W%C+J)=lY& SZV_#({_yWosssHFNd5. + +In addition, the RTCW SP Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the RTCW SP +Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, +you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +#include "ef_local.h" + +replacefunc_t *replacefuncs; +int numfuncs; + +#define MAX_TOKEN_LIST 64 +tokenList_t tokenList[MAX_TOKEN_LIST]; +int tokenListHead = 0; +int verbose = 0; + +// the function names +//#define DEFAULT_FUNCBASE "g_func" +static char *func_listfile = "g_func_list.h"; +static char *func_decsfile = "g_func_decs.h"; + +#define TEMP_LIST_NAME "g_func_list.tmp" +#define TEMP_DECS_NAME "g_func_decs.tmp" + +//=========================================================================== + +#if 0 +/* +================= +WriteWhiteSpace +================= +*/ +void WriteWhiteSpace (FILE *fp, script_t *script) +{ + int c; + //write out the white space + c = PS_NextWhiteSpaceChar( script ); + while ( c ) + { + //NOTE: do NOT write out carriage returns (for unix/linux compatibility + if ( c != 13 ) { + fputc( c, fp ); + } + c = PS_NextWhiteSpaceChar( script ); + } //end while +} //end of the function WriteWhiteSpace + +/* +================= +WriteString +================= +*/ +void WriteString (FILE *fp, script_t *script) +{ + char *ptr; + + ptr = script->endwhitespace_p; + while ( ptr < script->script_p ) + { + fputc( *ptr, fp ); + ptr++; + } //end while +} //end of the function WriteString + +/* +================= +ScrewUpFile +================= +*/ +void ScrewUpFile (char *oldfile, char *newfile) +{ + FILE *fp; + script_t *script; + token_t token; + replacefunc_t *f; + char *ptr; + + printf( "screwing up file %s\n", oldfile ); + script = LoadScriptFile( oldfile ); + if ( !script ) { + Error( "error opening %s\n", oldfile ); + } + fp = fopen( newfile, "wb" ); + if ( !fp ) { + Error( "error opening %s\n", newfile ); + } + // + while ( PS_ReadToken( script, &token ) ) + { + WriteWhiteSpace( fp, script ); + if ( token.type == TT_NAME ) + { + f = FindFunctionName( token.string ); + if ( f ) { + ptr = f->newname; + } else { ptr = token.string;} + while ( *ptr ) + { + fputc( *ptr, fp ); + ptr++; + } //end while + } //end if + else + { + WriteString( fp, script ); + } //end else + } //end while + WriteWhiteSpace( fp, script ); + FreeMemory( script ); + fclose( fp ); +} //end of the function ScrewUpFile +#endif + +/* +================= +Error +================= +*/ +void Error (char *error, ...) +{ + va_list argptr; + + va_start( argptr, error ); + vprintf( error, argptr ); + va_end( argptr ); + + exit( 1 ); +} + +/* +================= +DumpReplaceFunctions +================= +*/ +void DumpReplaceFunctions (char *typeName) +{ + replacefunc_t *rf; + char path[_MAX_PATH]; + FILE *f; + int len, newlen; + unsigned char *buf, *newbuf; + int updated; + + updated = 0; + + // dump the function header + strcpy( path, "." ); + strcat( path, PATHSEPERATOR_STR ); + strcat( path, TEMP_LIST_NAME ); + Log_Open( path ); + for ( rf = replacefuncs; rf; rf = rf->next ) + { + if (typeName) + Log_Print( "{\"%s\", &%s},\n", rf->name, rf->name ); + else + Log_Print( "{\"%s\", (byte *)%s},\n", rf->name, rf->name ); + } //end for + Log_Print( "{0, 0}\n" ); + Log_Close(); + + // if it's different, rename the file over the real header + strcpy( path, TEMP_LIST_NAME ); + f = fopen( path, "rb" ); + fseek( f, 0, SEEK_END ); + len = ftell( f ); + buf = (unsigned char *) malloc( len + 1 ); + fseek( f, 0, SEEK_SET ); + fread( buf, len, 1, f ); + buf[len] = 0; + fclose( f ); + + strcpy( path, func_listfile ); + if ( f = fopen( path, "rb" ) ) + { + fseek( f, 0, SEEK_END ); + newlen = ftell( f ); + newbuf = (unsigned char *) malloc( newlen + 1 ); + fseek( f, 0, SEEK_SET ); + fread( newbuf, newlen, 1, f ); + newbuf[newlen] = 0; + fclose( f ); + + if ( len != newlen || Q_stricmp( buf, newbuf ) ) + { + char newpath[_MAX_PATH]; + + // delete the old file, rename the new one + strcpy( path, func_listfile ); + remove( path ); + + strcpy( newpath, TEMP_LIST_NAME ); + rename( newpath, path ); + +#ifdef _WIN32 + // make g_save recompile itself + remove( "debug\\g_save.obj" ); + remove( "debug\\g_save.sbr" ); + remove( "release\\g_save.obj" ); + remove( "release\\g_save.sbr" ); +#endif + + updated = 1; + } + else { + // delete the old file + strcpy( path, TEMP_LIST_NAME ); + remove( path ); + } + } + else { + rename( TEMP_LIST_NAME, func_listfile ); + } + + free( buf ); + free( newbuf ); + + // dump the function declarations + strcpy( path, TEMP_DECS_NAME ); + Log_Open( path ); + for ( rf = replacefuncs; rf; rf = rf->next ) + { + if (typeName) + Log_Print( "extern %s %s;\n", typeName, rf->dec ); + else + Log_Print( "extern %s;\n", rf->dec ); + } //end for + Log_Close(); + + // if it's different, rename the file over the real header + strcpy( path, TEMP_DECS_NAME ); + f = fopen( path, "rb" ); + fseek( f, 0, SEEK_END ); + len = ftell( f ); + buf = (unsigned char *) malloc( len + 1 ); + fseek( f, 0, SEEK_SET ); + fread( buf, len, 1, f ); + buf[len] = 0; + fclose( f ); + + strcpy( path, func_decsfile ); + if ( f = fopen( path, "rb" ) ) + { + fseek( f, 0, SEEK_END ); + newlen = ftell( f ); + newbuf = (unsigned char *) malloc( newlen + 1 ); + fseek( f, 0, SEEK_SET ); + fread( newbuf, newlen, 1, f ); + newbuf[newlen] = 0; + fclose( f ); + + if ( len != newlen || Q_stricmp( buf, newbuf ) ) + { + char newpath[_MAX_PATH]; + + // delete the old file, rename the new one + strcpy( path, func_decsfile ); + remove( path ); + + strcpy( newpath, TEMP_DECS_NAME ); + rename( newpath, path ); + +#ifdef _WIN32 + // make g_save recompile itself + // NOTE TTimo win32 only? (harmless on *nix anyway) + remove( "debug\\g_save.obj" ); + remove( "debug\\g_save.sbr" ); + remove( "release\\g_save.obj" ); + remove( "release\\g_save.sbr" ); +#endif + + updated = 1; + } + else { + // delete the old file + strcpy( path, TEMP_DECS_NAME ); + remove( path ); + } + } + else { + rename( TEMP_DECS_NAME, func_decsfile ); + } + + free( buf ); + free( newbuf ); + +#ifdef _WIN32 + if ( updated ) { + printf( "Updated the function table, recompile required.\n" ); + } +#endif +} // end of the function DumpReplaceFunctions + +/* +================= +FindFunctionName +================= +*/ +replacefunc_t *FindFunctionName (char *funcname) +{ + replacefunc_t *f; + + for (f = replacefuncs; f; f = f->next) + { + if ( !strcmp(f->name, funcname) ) + { + return f; + } + } //end for + return NULL; +} //end of the function FindFunctionName + +/* +================= +MayScrewUp +================= +*/ +int MayScrewUp (char *funcname) +{ + if ( !strcmp(funcname, "GetBotAPI") ) + { + return false; + } + if ( !strcmp(funcname, "main") ) + { + return false; + } + if ( !strcmp(funcname, "WinMain") ) + { + return false; + } + return true; +} //end of the function MayScrewUp + +/* +================= +ConcatDec +================= +*/ +void ConcatDec (tokenList_t *list, char *str, int inc) +{ +/* if (!((list->token.type == TT_NAME) || (list->token.string[0] == '*'))) { + if (list->token.string[0] == ')' || list->token.string[0] == '(') { + if (inc++ >= 2) + return; + } else { + return; + } + }*/ + if (list->next) + { + ConcatDec (list->next, str, inc); + } + strcat(str, list->token.string); + strcat(str, " " ); +} + +/* +================= +AddFunctionName +================= +*/ +void AddFunctionName (char *funcname, char *filename, tokenList_t *head) +{ + replacefunc_t *f; + tokenList_t *list; + + if ( FindFunctionName(funcname) ) + { + return; + } + +#if defined( __linux__ ) + // the bad thing is, this doesn't preprocess .. on __linux__ this + // function is not implemented (q_math.c) + if ( !Q_stricmp( funcname, "BoxOnPlaneSide" ) ) { + return; + } +#endif + + // NERVE - SMF - workaround for Graeme's predifined MACOSX functions + // TTimo - looks like linux version needs to escape those too +#if defined( _WIN32 ) || defined( __linux__ ) + if ( !Q_stricmp(funcname, "qmax") ) { + return; + } + else if ( !Q_stricmp(funcname, "qmin") ) { + return; + } +#endif + // -NERVE - SMF + + f = (replacefunc_t *) GetMemory( sizeof( replacefunc_t ) + strlen( funcname ) + 1 + 6 + strlen( filename ) + 1 ); + f->name = (char *) f + sizeof( replacefunc_t ); + strcpy( f->name, funcname ); + f->newname = (char *) f + sizeof( replacefunc_t ) + strlen( funcname ) + 1; + sprintf( f->newname, "F%d", numfuncs++ ); + f->filename = (char *) f + sizeof( replacefunc_t ) + strlen( funcname ) + 1 + strlen( f->newname ) + 1; + strcpy( f->filename, filename ); + f->next = replacefuncs; + replacefuncs = f; + + // construct the declaration + list = head; + f->dec[0] = '\0'; + ConcatDec( list, f->dec, 0 ); + +} //end of the function AddFunctionName + +/* +================= +AddTokenToList +================= +*/ +void AddTokenToList (tokenList_t **head, token_t *token) +{ + tokenList_t *newhead; + + newhead = &tokenList[tokenListHead++]; //GetMemory( sizeof( tokenList_t ) ); + if ( tokenListHead == MAX_TOKEN_LIST ) { + tokenListHead = 0; + } + + newhead->next = *head; + newhead->token = *token; + + *head = newhead; +} + +#if 0 +/* +================= +KillTokenList +================= +*/ +void KillTokenList (tokenList_t *head) +{ + if (head->next) { + KillTokenList( head->next ); + FreeMemory( head->next ); + head->next = NULL; + } +} +#endif + +/* +================= +StripTokenList +================= +*/ +void StripTokenList (tokenList_t *head) +{ + tokenList_t *trav, *lastTrav; + + trav = head; + + // now go back to the start of the declaration + lastTrav = trav; + trav = trav->next; // should be on the function name now + while ( ( trav->token.type == TT_NAME ) || ( trav->token.string[0] == '*' ) ) + { + lastTrav = trav; + trav = trav->next; + if ( !trav ) { + return; + } + } + // now kill everything after lastTrav +// KillTokenList( lastTrav ); + lastTrav->next = NULL; +} + +/* +================= +GetTypeNamesFromFile + +Knightmare- this gets structs / vars of a given type +================= +*/ +void GetTypeNamesFromFile (char *filename, char *typeName) +{ + source_t *source; + token_t token, lasttoken; + int indent = 0;//, brace; + int isStatic = 0; + int isExtern = 0; + tokenList_t *listHead; + + listHead = NULL; + source = LoadSourceFile( filename ); + if ( !source ) { + Error( "error opening %s", filename ); + return; + } + + while ( 1 ) + { + if ( !PC_ReadToken( source, &token ) ) { + break; + } + if ( token.type == TT_PUNCTUATION ) + { + switch ( token.string[0] ) + { + case ';': + isStatic = 0; + isExtern = 0; + break; + case '{': + indent++; + break; + case '}': + indent--; + if ( indent < 0 ) + indent = 0; + break; + } + } + if ( token.type == TT_NAME ) + { // type declarations for pointer table must be non-static, non-extern, and global in scope + if ( token.string[0] == 's' && !strcmp( token.string, "static" ) ) { + isStatic = 1; + } + if ( token.string[0] == 'e' && !strcmp( token.string, "extern" ) ) { + isExtern = 1; + } + if ( !isStatic && !isExtern && indent == 0 && !strcmp(token.string, typeName) ) + { + if ( PC_ReadToken( source, &token ) ) + { + if ( token.type == TT_NAME ) + { + if (listHead) + listHead->next = NULL; + listHead = NULL; + AddTokenToList( &listHead, &token ); + AddFunctionName( token.string, filename, listHead ); + } + } + } + } + memcpy( &lasttoken, &token, sizeof( token_t ) ); + } + FreeSource( source ); +} + +/* +================= +GetFunctionNamesFromFile +================= +*/ +void GetFunctionNamesFromFile (char *filename) +{ + source_t *source; + token_t token, lasttoken; + int indent = 0, brace; + int isStatic = 0; + tokenList_t *listHead; + + // filter some files out + if ( !Q_stricmp( filename, "bg_lib.c" ) ) { + return; + } + + listHead = NULL; + source = LoadSourceFile( filename ); + if ( !source ) { + Error( "error opening %s", filename ); + return; + } + +// printf("loaded %s\n", filename); +// if (!PC_ReadToken(source, &lasttoken)) +// { +// FreeSource(source); +// return; +// } //end if + while ( 1 ) + { + if ( !PC_ReadToken( source, &token ) ) { + break; + } + AddTokenToList( &listHead, &token ); + if ( token.type == TT_PUNCTUATION ) + { + switch ( token.string[0] ) + { + case ';': + isStatic = 0; + break; + case '{': + indent++; + break; + case '}': + indent--; + if ( indent < 0 ) + indent = 0; + break; + case '(': + if ( indent <= 0 && lasttoken.type == TT_NAME ) + { + StripTokenList( listHead ); + + brace = 1; + while ( PC_ReadToken( source, &token ) ) + { + AddTokenToList( &listHead, &token ); + if ( token.string[0] == '(' ) { + brace++; + } + else if ( token.string[0] == ')' ) + { + brace--; + if ( brace <= 0 ) + { + if ( !PC_ReadToken( source, &token ) ) { + break; + } + if ( token.string[0] == '{' ) { + indent++; + if ( !isStatic && MayScrewUp( lasttoken.string ) ) { + AddFunctionName( lasttoken.string, filename, listHead ); + } + } //end if + break; + } //end if + } //end if + } //end while + } //end if + break; + } //end switch + } //end if + if ( token.type == TT_NAME ) + { + if ( token.string[0] == 's' && !strcmp( token.string, "static" ) ) { + isStatic = 1; + } + } + memcpy( &lasttoken, &token, sizeof( token_t ) ); + } //end while + FreeSource( source ); +} //end of the function GetFunctionNamesFromFile + +/* +================= +Usage +================= +*/ +void Usage (void) +{ +// Error( "USAGE SCREWUP: extractfuncs \n" ); + Error( "USAGE SCREWUP: extractfuncs [ ..] [-t ] [-o ] [-d ]\n" + "no -o defaults to g_func_list.h g_func_decs.h or g__list.h g__decs.h\n" ); +} + +/* +================= +main +================= +*/ +#ifdef _WIN32 +void main (int argc, char *argv[]) +{ + WIN32_FIND_DATA filedata; + HWND handle; + int i, firstParm, done; + char typeName[128]; + qboolean typeExtract = false; + qboolean firstParmSet = false; + +// if ( argc < 2 ) +// Usage (); + + // Knightmare- check for command line switches + for (i=0; iS1Na9|M0HtqWKlsKe~I_ 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/l_log.c b/l_log.c new file mode 100644 index 0000000..82e7ed7 --- /dev/null +++ b/l_log.c @@ -0,0 +1,204 @@ +/* +=========================================================================== + +Return to Castle Wolfenstein single player GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). + +RTCW SP 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 3 of the License, or +(at your option) any later version. + +RTCW SP 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 RTCW SP Source Code. If not, see . + +In addition, the RTCW SP Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the RTCW SP +Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, +you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +//=========================================================================== +// +// Name: l_log.c +// Function: log file stuff +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-31 +// Tab Size: 3 +//=========================================================================== + +#include +#include +#include +#include "ef_local.h" + +#define MAX_QPATH 64 +//#include "qbsp.h" + +#define MAX_LOGFILENAMESIZE 1024 + +typedef struct logfile_s +{ + char filename[MAX_LOGFILENAMESIZE]; + FILE *fp; + int numwrites; +} logfile_t; + +logfile_t logfile; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Open( char *filename ) { + if ( !filename || !strlen( filename ) ) { + printf( "openlog \n" ); + return; + } //end if + if ( logfile.fp ) { + printf( "log file %s is already opened\n", logfile.filename ); + return; + } //end if + logfile.fp = fopen( filename, "wb" ); + if ( !logfile.fp ) { + printf( "can't open the log file %s\n", filename ); + return; + } //end if + strncpy( logfile.filename, filename, MAX_LOGFILENAMESIZE ); +// printf("Opened log %s\n", logfile.filename); +} //end of the function Log_Create +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Close( void ) { + if ( !logfile.fp ) { + printf( "no log file to close\n" ); + return; + } //end if + if ( fclose( logfile.fp ) ) { + printf( "can't close log file %s\n", logfile.filename ); + return; + } //end if + logfile.fp = NULL; +// printf("Closed log %s\n", logfile.filename); +} //end of the function Log_Close +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Shutdown( void ) { + if ( logfile.fp ) { + Log_Close(); + } +} //end of the function Log_Shutdown +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Print( char *fmt, ... ) { + va_list ap; +#ifdef WINBSPC + char buf[2048]; +#endif //WINBSPC + + if ( verbose ) { + va_start( ap, fmt ); +#ifdef WINBSPC + vsprintf( buf, fmt, ap ); + WinBSPCPrint( buf ); +#else + vprintf( fmt, ap ); +#endif //WINBSPS + va_end( ap ); + } //end if + + va_start( ap, fmt ); + if ( logfile.fp ) { + vfprintf( logfile.fp, fmt, ap ); + fflush( logfile.fp ); + } //end if + va_end( ap ); +} //end of the function Log_Print +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Write( char *fmt, ... ) { + va_list ap; + + if ( !logfile.fp ) { + return; + } + va_start( ap, fmt ); + vfprintf( logfile.fp, fmt, ap ); + va_end( ap ); + fflush( logfile.fp ); +} //end of the function Log_Write +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_WriteTimeStamped( char *fmt, ... ) { + va_list ap; + + if ( !logfile.fp ) { + return; + } +/* fprintf(logfile.fp, "%d %02d:%02d:%02d:%02d ", + logfile.numwrites, + (int) (botlibglobals.time / 60 / 60), + (int) (botlibglobals.time / 60), + (int) (botlibglobals.time), + (int) ((int) (botlibglobals.time * 100)) - + ((int) botlibglobals.time) * 100);*/ + va_start( ap, fmt ); + vfprintf( logfile.fp, fmt, ap ); + va_end( ap ); + logfile.numwrites++; + fflush( logfile.fp ); +} //end of the function Log_Write +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +FILE *Log_FileStruct( void ) { + return logfile.fp; +} //end of the function Log_FileStruct +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void Log_Flush( void ) { + if ( logfile.fp ) { + fflush( logfile.fp ); + } +} //end of the function Log_Flush diff --git a/l_log.h b/l_log.h new file mode 100644 index 0000000..73ba24d --- /dev/null +++ b/l_log.h @@ -0,0 +1,63 @@ +/* +=========================================================================== + +Return to Castle Wolfenstein single player GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). + +RTCW SP 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 3 of the License, or +(at your option) any later version. + +RTCW SP 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 RTCW SP Source Code. If not, see . + +In addition, the RTCW SP Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the RTCW SP +Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, +you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + +//=========================================================================== +// +// Name: l_log.h +// Function: log file stuff +// Programmer: Mr Elusive (MrElusive@demigod.demon.nl) +// Last update: 1997-12-31 +// Tab Size: 3 +//=========================================================================== + +//open a log file +void Log_Open( char *filename ); +//close the current log file +void Log_Close( void ); +//close log file if present +void Log_Shutdown( void ); +//print on stdout and write to the current opened log file +void Log_Print( char *fmt, ... ); +//write to the current opened log file +void Log_Write( char *fmt, ... ); +//write to the current opened log file with a time stamp +void Log_WriteTimeStamped( char *fmt, ... ); +//returns the log file structure +FILE *Log_FileStruct( void ); +//flush log file +void Log_Flush( void ); + +int Log_Written( void ); + +#ifdef WINBSPC +void WinBSPCPrint( char *str ); +#endif //WINBSPC diff --git a/l_memory.c b/l_memory.c new file mode 100644 index 0000000..428f3f1 --- /dev/null +++ b/l_memory.c @@ -0,0 +1,455 @@ +/* +=========================================================================== + +Return to Castle Wolfenstein single player GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). + +RTCW SP 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 3 of the License, or +(at your option) any later version. + +RTCW SP 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 RTCW SP Source Code. If not, see . + +In addition, the RTCW SP Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the RTCW SP +Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, +you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_memory.c + * + * desc: memory allocation + * + * + *****************************************************************************/ + +//#include "q_shared.h" +//#include "../game/botlib.h" +//#include "../../src/botlib/be_interface.h" +#include +#include +#include +#include +#include +#include +#include +#include "l_log.h" + +//#define MEMDEBUG +//#define MEMORYMANEGER + +#define MEM_ID 0x12345678l +#define HUNK_ID 0x87654321l + +int allocatedmemory; +int totalmemorysize; +int numblocks; + +#ifdef MEMORYMANEGER + +typedef struct memoryblock_s +{ + unsigned long int id; + void *ptr; + int size; +#ifdef MEMDEBUG + char *label; + char *file; + int line; +#endif //MEMDEBUG + struct memoryblock_s *prev, *next; +} memoryblock_t; + +memoryblock_t *memory; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void LinkMemoryBlock( memoryblock_t *block ) { + block->prev = NULL; + block->next = memory; + if ( memory ) { + memory->prev = block; + } + memory = block; +} //end of the function LinkMemoryBlock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void UnlinkMemoryBlock( memoryblock_t *block ) { + if ( block->prev ) { + block->prev->next = block->next; + } else { memory = block->next;} + if ( block->next ) { + block->next->prev = block->prev; + } +} //end of the function UnlinkMemoryBlock +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetMemoryDebug( unsigned long size, char *label, char *file, int line ) +#else +void *GetMemory( unsigned long size ) +#endif //MEMDEBUG +{ + void *ptr; + memoryblock_t *block; + + ptr = malloc( size + sizeof( memoryblock_t ) ); + block = (memoryblock_t *) ptr; + block->id = MEM_ID; + block->ptr = (char *) ptr + sizeof( memoryblock_t ); + block->size = size + sizeof( memoryblock_t ); +#ifdef MEMDEBUG + block->label = label; + block->file = file; + block->line = line; +#endif //MEMDEBUG + LinkMemoryBlock( block ); + allocatedmemory += block->size; + totalmemorysize += block->size + sizeof( memoryblock_t ); + numblocks++; + return block->ptr; +} //end of the function GetMemoryDebug +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedMemoryDebug( unsigned long size, char *label, char *file, int line ) +#else +void *GetClearedMemory( unsigned long size ) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetMemoryDebug( size, label, file, line ); +#else + ptr = GetMemory( size ); +#endif //MEMDEBUG + memset( ptr, 0, size ); + return ptr; +} //end of the function GetClearedMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetHunkMemoryDebug( unsigned long size, char *label, char *file, int line ) +#else +void *GetHunkMemory( unsigned long size ) +#endif //MEMDEBUG +{ + void *ptr; + memoryblock_t *block; + + ptr = malloc( size + sizeof( memoryblock_t ) ); + block = (memoryblock_t *) ptr; + block->id = HUNK_ID; + block->ptr = (char *) ptr + sizeof( memoryblock_t ); + block->size = size + sizeof( memoryblock_t ); +#ifdef MEMDEBUG + block->label = label; + block->file = file; + block->line = line; +#endif //MEMDEBUG + LinkMemoryBlock( block ); + allocatedmemory += block->size; + totalmemorysize += block->size + sizeof( memoryblock_t ); + numblocks++; + return block->ptr; +} //end of the function GetHunkMemoryDebug +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedHunkMemoryDebug( unsigned long size, char *label, char *file, int line ) +#else +void *GetClearedHunkMemory( unsigned long size ) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetHunkMemoryDebug( size, label, file, line ); +#else + ptr = GetHunkMemory( size ); +#endif //MEMDEBUG + memset( ptr, 0, size ); + return ptr; +} //end of the function GetClearedHunkMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +memoryblock_t *BlockFromPointer( void *ptr, char *str ) { + memoryblock_t *block; + + if ( !ptr ) { +#ifdef MEMDEBUG + //char *crash = (char *) NULL; + //crash[0] = 1; + printf( PRT_FATAL, "%s: NULL pointer\n", str ); +#endif MEMDEBUG + return NULL; + } //end if + block = ( memoryblock_t * )( (char *) ptr - sizeof( memoryblock_t ) ); + if ( block->id != MEM_ID && block->id != HUNK_ID ) { + printf( PRT_FATAL, "%s: invalid memory block\n", str ); + return NULL; + } //end if + if ( block->ptr != ptr ) { + printf( PRT_FATAL, "%s: memory block pointer invalid\n", str ); + return NULL; + } //end if + return block; +} //end of the function BlockFromPointer +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeMemory( void *ptr ) { + memoryblock_t *block; + + block = BlockFromPointer( ptr, "FreeMemory" ); + if ( !block ) { + return; + } + UnlinkMemoryBlock( block ); + allocatedmemory -= block->size; + totalmemorysize -= block->size + sizeof( memoryblock_t ); + numblocks--; + // + if ( block->id == MEM_ID ) { + free( block ); + } //end if +} //end of the function FreeMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +int MemoryByteSize( void *ptr ) { + memoryblock_t *block; + + block = BlockFromPointer( ptr, "MemoryByteSize" ); + if ( !block ) { + return 0; + } + return block->size; +} //end of the function MemoryByteSize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintUsedMemorySize( void ) { + printf( PRT_MESSAGE, "total allocated memory: %d KB\n", allocatedmemory >> 10 ); + printf( PRT_MESSAGE, "total botlib memory: %d KB\n", totalmemorysize >> 10 ); + printf( PRT_MESSAGE, "total memory blocks: %d\n", numblocks ); +} //end of the function PrintUsedMemorySize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintMemoryLabels( void ) { + memoryblock_t *block; + int i; + + PrintUsedMemorySize(); + i = 0; + Log_Write( "\r\n" ); + for ( block = memory; block; block = block->next ) + { +#ifdef MEMDEBUG + if ( block->id == HUNK_ID ) { + Log_Write( "%6d, hunk %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label ); + } //end if + else + { + Log_Write( "%6d, %p, %8d: %24s line %6d: %s\r\n", i, block->ptr, block->size, block->file, block->line, block->label ); + } //end else +#endif //MEMDEBUG + i++; + } //end for +} //end of the function PrintMemoryLabels +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void DumpMemory( void ) { + memoryblock_t *block; + + for ( block = memory; block; block = memory ) + { + FreeMemory( block->ptr ); + } //end for + totalmemorysize = 0; + allocatedmemory = 0; +} //end of the function DumpMemory + +#else + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetMemoryDebug( unsigned long size, char *label, char *file, int line ) +#else +void *GetMemory( unsigned long size ) +#endif //MEMDEBUG +{ + void *ptr; + unsigned long int *memid; + + ptr = malloc( size + sizeof( unsigned long int ) ); + if ( !ptr ) { + return NULL; + } + memid = (unsigned long int *) ptr; + *memid = MEM_ID; + return (unsigned long int *) ( (char *) ptr + sizeof( unsigned long int ) ); +} //end of the function GetMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedMemoryDebug( unsigned long size, char *label, char *file, int line ) +#else +void *GetClearedMemory( unsigned long size ) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetMemoryDebug( size, label, file, line ); +#else +ptr = GetMemory( size ); +#endif //MEMDEBUG +memset( ptr, 0, size ); +return ptr; +} //end of the function GetClearedMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetHunkMemoryDebug( unsigned long size, char *label, char *file, int line ) +#else +void *GetHunkMemory( unsigned long size ) +#endif //MEMDEBUG +{ + void *ptr; + unsigned long int *memid; + + ptr = malloc( size + sizeof( unsigned long int ) ); + if ( !ptr ) { + return NULL; + } + memid = (unsigned long int *) ptr; + *memid = HUNK_ID; + return (unsigned long int *) ( (char *) ptr + sizeof( unsigned long int ) ); +} //end of the function GetHunkMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +#ifdef MEMDEBUG +void *GetClearedHunkMemoryDebug( unsigned long size, char *label, char *file, int line ) +#else +void *GetClearedHunkMemory( unsigned long size ) +#endif //MEMDEBUG +{ + void *ptr; +#ifdef MEMDEBUG + ptr = GetHunkMemoryDebug( size, label, file, line ); +#else +ptr = GetHunkMemory( size ); +#endif //MEMDEBUG +memset( ptr, 0, size ); +return ptr; +} //end of the function GetClearedHunkMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void FreeMemory( void *ptr ) { + unsigned long int *memid; + + memid = (unsigned long int *) ( (char *) ptr - sizeof( unsigned long int ) ); + + if ( *memid == MEM_ID ) { + free( memid ); + } //end if +} //end of the function FreeMemory +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintUsedMemorySize( void ) { +} //end of the function PrintUsedMemorySize +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PrintMemoryLabels( void ) { +} //end of the function PrintMemoryLabels + +#endif diff --git a/l_memory.h b/l_memory.h new file mode 100644 index 0000000..946ef7f --- /dev/null +++ b/l_memory.h @@ -0,0 +1,84 @@ +/* +=========================================================================== + +Return to Castle Wolfenstein single player GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). + +RTCW SP 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 3 of the License, or +(at your option) any later version. + +RTCW SP 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 RTCW SP Source Code. If not, see . + +In addition, the RTCW SP Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the RTCW SP +Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, +you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_memory.h + * + * desc: memory management + * + * + *****************************************************************************/ + +//#define MEMDEBUG + +#ifdef MEMDEBUG +#define GetMemory( size ) GetMemoryDebug( size, # size, __FILE__, __LINE__ ); +#define GetClearedMemory( size ) GetClearedMemoryDebug( size, # size, __FILE__, __LINE__ ); +//allocate a memory block of the given size +void *GetMemoryDebug( unsigned long size, char *label, char *file, int line ); +//allocate a memory block of the given size and clear it +void *GetClearedMemoryDebug( unsigned long size, char *label, char *file, int line ); +// +#define GetHunkMemory( size ) GetHunkMemoryDebug( size, # size, __FILE__, __LINE__ ); +#define GetClearedHunkMemory( size ) GetClearedHunkMemoryDebug( size, # size, __FILE__, __LINE__ ); +//allocate a memory block of the given size +void *GetHunkMemoryDebug( unsigned long size, char *label, char *file, int line ); +//allocate a memory block of the given size and clear it +void *GetClearedHunkMemoryDebug( unsigned long size, char *label, char *file, int line ); +#else +//allocate a memory block of the given size +void *GetMemory( unsigned long size ); +//allocate a memory block of the given size and clear it +void *GetClearedMemory( unsigned long size ); +// +#ifdef BSPC +#define GetHunkMemory GetMemory +#define GetClearedHunkMemory GetClearedMemory +#else +//allocate a memory block of the given size +void *GetHunkMemory( unsigned long size ); +//allocate a memory block of the given size and clear it +void *GetClearedHunkMemory( unsigned long size ); +#endif +#endif + +//free the given memory block +void FreeMemory( void *ptr ); +//prints the total used memory size +void PrintUsedMemorySize( void ); +//print all memory blocks with label +void PrintMemoryLabels( void ); +//returns the size of the memory block in bytes +int MemoryByteSize( void *ptr ); +//free all allocated memory +void DumpMemory( void ); diff --git a/l_precomp.c b/l_precomp.c new file mode 100644 index 0000000..b356224 --- /dev/null +++ b/l_precomp.c @@ -0,0 +1,3225 @@ +/* +=========================================================================== + +Return to Castle Wolfenstein single player GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). + +RTCW SP 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 3 of the License, or +(at your option) any later version. + +RTCW SP 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 RTCW SP Source Code. If not, see . + +In addition, the RTCW SP Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the RTCW SP +Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, +you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_precomp.c + * + * desc: pre compiler + * + * + *****************************************************************************/ + +//Notes: fix: PC_StringizeTokens + +#ifdef SCREWUP +#include +#include +#include +#include +#include +#include +#include +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_log.h" + +typedef enum {qfalse, qtrue} qboolean; + +extern void Error( char *error, ... ); + +// Ridah, ripped from q_shared.c +/* +============= +Q_strncpyz + +Safe strncpy that ensures a trailing zero +============= +*/ +void Q_strncpyz( char *dest, const char *src, int destsize ) { + if ( !src ) { + Error( "Q_strncpyz: NULL src" ); + } + if ( destsize < 1 ) { + Error( "Q_strncpyz: destsize < 1" ); + } + + strncpy( dest, src, destsize - 1 ); + dest[destsize - 1] = 0; +} + +/* +================= +Com_sprintf +================= +*/ +void Com_sprintf (char *dest, int size, const char *fmt, ...) +{ + int len; + va_list argptr; + char bigbuffer[32000]; // big, but small enough to fit in PPC stack + + va_start( argptr,fmt ); + len = vsprintf( bigbuffer,fmt,argptr ); + va_end( argptr ); + if ( len >= sizeof( bigbuffer ) ) + { + Error( "Com_sprintf: overflowed bigbuffer" ); + } + if ( len >= size ) + { + printf( "Com_sprintf: overflow of %i in %i\n", len, size ); + } + Q_strncpyz( dest, bigbuffer, size ); +} + +int Q_stricmpn( const char *s1, const char *s2, int n ) { + int c1, c2; + + do { + c1 = *s1++; + c2 = *s2++; + + if ( !n-- ) { + return 0; // strings are equal until end point + } + + if ( c1 != c2 ) { + if ( c1 >= 'a' && c1 <= 'z' ) { + c1 -= ( 'a' - 'A' ); + } + if ( c2 >= 'a' && c2 <= 'z' ) { + c2 -= ( 'a' - 'A' ); + } + if ( c1 != c2 ) { + return c1 < c2 ? -1 : 1; + } + } + } while ( c1 ); + + return 0; // strings are equal +} + +int Q_strncmp( const char *s1, const char *s2, int n ) { + int c1, c2; + + do { + c1 = *s1++; + c2 = *s2++; + + if ( !n-- ) { + return 0; // strings are equal until end point + } + + if ( c1 != c2 ) { + return c1 < c2 ? -1 : 1; + } + } while ( c1 ); + + return 0; // strings are equal +} + +int Q_stricmp( const char *s1, const char *s2 ) { + return Q_stricmpn( s1, s2, 99999 ); +} + + +char *Q_strlwr( char *s1 ) { + char *s; + + s = s1; + while ( *s ) { + *s = tolower( *s ); + s++; + } + return s1; +} + +char *Q_strupr( char *s1 ) { + char *s; + + s = s1; + while ( *s ) { + *s = toupper( *s ); + s++; + } + return s1; +} + +#endif //SCREWUP + +#ifdef BOTLIB +#include "../game/q_shared.h" +#include "botlib.h" +#include "be_interface.h" +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_log.h" +#endif //BOTLIB + +#ifdef MEQCC +#include "qcc.h" +#include "time.h" //time & ctime +#include "math.h" //fabs +#include "l_memory.h" +#include "l_script.h" +#include "l_precomp.h" +#include "l_log.h" + +#define qtrue true +#define qfalse false +#endif //MEQCC + +#ifdef BSPC +//include files for usage in the BSP Converter +#include "../bspc/qbsp.h" +#include "../bspc/l_log.h" +#include "../bspc/l_mem.h" +#include "l_precomp.h" + +#define qtrue true +#define qfalse false +#define Q_stricmp stricmp +#endif //BSPC + +#if defined( QUAKE ) && !defined( BSPC ) +#include "l_utils.h" +#endif //QUAKE + +//#define DEBUG_EVAL + +#define MAX_DEFINEPARMS 128 + +#define DEFINEHASHING 1 + +//directive name with parse function +typedef struct directive_s +{ + char *name; + int ( *func )( source_t *source ); +} directive_t; + +#define DEFINEHASHSIZE 1024 + +#define TOKEN_HEAP_SIZE 4096 + +int numtokens; +/* +int tokenheapinitialized; //true when the token heap is initialized +token_t token_heap[TOKEN_HEAP_SIZE]; //heap with tokens +token_t *freetokens; //free tokens from the heap +*/ + +//list with global defines added to every source loaded +define_t *globaldefines; + +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void QDECL SourceError( source_t *source, char *str, ... ) { + char text[1024]; + va_list ap; + + va_start( ap, str ); + vsprintf( text, str, ap ); + va_end( ap ); +#ifdef BOTLIB + botimport.Print( PRT_ERROR, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text ); +#endif //BOTLIB +#ifdef MEQCC + printf( "error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text ); +#endif //MEQCC +#ifdef BSPC + Log_Print( "error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text ); +#endif //BSPC +} //end of the function SourceError +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL SourceWarning( source_t *source, char *str, ... ) { + char text[1024]; + va_list ap; + + va_start( ap, str ); + vsprintf( text, str, ap ); + va_end( ap ); +#ifdef BOTLIB + botimport.Print( PRT_WARNING, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text ); +#endif //BOTLIB +#ifdef MEQCC + printf( "warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text ); +#endif //MEQCC +#ifdef BSPC + Log_Print( "warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text ); +#endif //BSPC +} //end of the function ScriptWarning +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PushIndent( source_t *source, int type, int skip ) { + indent_t *indent; + + indent = (indent_t *) GetMemory( sizeof( indent_t ) ); + indent->type = type; + indent->script = source->scriptstack; + indent->skip = ( skip != 0 ); + source->skip += indent->skip; + indent->next = source->indentstack; + source->indentstack = indent; +} //end of the function PC_PushIndent +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PopIndent( source_t *source, int *type, int *skip ) { + indent_t *indent; + + *type = 0; + *skip = 0; + + indent = source->indentstack; + if ( !indent ) { + return; + } + + //must be an indent from the current script + if ( source->indentstack->script != source->scriptstack ) { + return; + } + + *type = indent->type; + *skip = indent->skip; + source->indentstack = source->indentstack->next; + source->skip -= indent->skip; + FreeMemory( indent ); +} //end of the function PC_PopIndent +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PushScript( source_t *source, script_t *script ) { + script_t *s; + + for ( s = source->scriptstack; s; s = s->next ) + { + if ( !Q_stricmp( s->filename, script->filename ) ) { + SourceError( source, "%s recursively included", script->filename ); + return; + } //end if + } //end for + //push the script on the script stack + script->next = source->scriptstack; + source->scriptstack = script; +} //end of the function PC_PushScript +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_InitTokenHeap( void ) { + /* + int i; + + if (tokenheapinitialized) return; + freetokens = NULL; + for (i = 0; i < TOKEN_HEAP_SIZE; i++) + { + token_heap[i].next = freetokens; + freetokens = &token_heap[i]; + } //end for + tokenheapinitialized = qtrue; + */ +} //end of the function PC_InitTokenHeap +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +token_t *PC_CopyToken( token_t *token ) { + token_t *t; + +// t = (token_t *) malloc(sizeof(token_t)); + t = (token_t *) GetMemory( sizeof( token_t ) ); +// t = freetokens; + if ( !t ) { +#ifdef BSPC + Error( "out of token space\n" ); +#else +#ifdef SCREWUP + Error( "out of token space\n" ); +#else + Com_Error( ERR_FATAL, "out of token space\n" ); +#endif +#endif + return NULL; + } //end if +// freetokens = freetokens->next; + memcpy( t, token, sizeof( token_t ) ); + t->next = NULL; + numtokens++; + return t; +} //end of the function PC_CopyToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_FreeToken( token_t *token ) { + //free(token); + FreeMemory( token ); +// token->next = freetokens; +// freetokens = token; + numtokens--; +} //end of the function PC_FreeToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadSourceToken( source_t *source, token_t *token ) { + token_t *t; + script_t *script; + int type, skip; + + //if there's no token already available + while ( !source->tokens ) + { + //if there's a token to read from the script + if ( PS_ReadToken( source->scriptstack, token ) ) { + return qtrue; + } + //if at the end of the script + if ( EndOfScript( source->scriptstack ) ) { + //remove all indents of the script + while ( source->indentstack && + source->indentstack->script == source->scriptstack ) + { + SourceWarning( source, "missing #endif" ); + PC_PopIndent( source, &type, &skip ); + } //end if + } //end if + //if this was the initial script + if ( !source->scriptstack->next ) { + return qfalse; + } + //remove the script and return to the last one + script = source->scriptstack; + source->scriptstack = source->scriptstack->next; + FreeScript( script ); + } //end while + //copy the already available token + memcpy( token, source->tokens, sizeof( token_t ) ); + //free the read token + t = source->tokens; + source->tokens = source->tokens->next; + PC_FreeToken( t ); + return qtrue; +} //end of the function PC_ReadSourceToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_UnreadSourceToken( source_t *source, token_t *token ) { + token_t *t; + + t = PC_CopyToken( token ); + t->next = source->tokens; + source->tokens = t; + return qtrue; +} //end of the function PC_UnreadSourceToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadDefineParms( source_t *source, define_t *define, token_t **parms, int maxparms ) { + token_t token, *t, *last; + int i, done, lastcomma, numparms, indent; + + if ( !PC_ReadSourceToken( source, &token ) ) { + // Knightmare- debug output + // printf("PC_ReadDefineParms: PC_ReadSourceToken failed, define %s missing parms in file %s\n", define->name, source->filename); + SourceError( source, "define %s missing parms", define->name ); + return qfalse; + } //end if + // + if ( define->numparms > maxparms ) { + // Knightmare- debug output + // printf("PC_ReadDefineParms: define %s with more than %d parameters in file %s\n", define->name, maxparms, source->filename); + SourceError( source, "define with more than %d parameters", maxparms ); + return qfalse; + } //end if + // + for ( i = 0; i < define->numparms; i++ ) parms[i] = NULL; + //if no leading "(" + if ( strcmp( token.string, "(" ) ) { + PC_UnreadSourceToken( source, &token ); + // Knightmare- debug output + // printf("PC_ReadDefineParms: %s instead of leading \"(\" for define %s in file %s\n", token.string, define->name, source->filename); + SourceError( source, "define %s missing parms", define->name ); + return qfalse; + } //end if + //read the define parameters + for ( done = 0, numparms = 0, indent = 0; !done; ) + { + if ( numparms >= maxparms ) { + // Knightmare- debug output + // printf("PC_ReadDefineParms: define %s with too many parms in file %s\n", define->name, source->filename); + SourceError( source, "define %s with too many parms", define->name ); + return qfalse; + } //end if + if ( numparms >= define->numparms ) { + // Knightmare- debug output + // printf("PC_ReadDefineParms: define %s has too many parms in file %s\n", define->name, source->filename); + SourceWarning( source, "define %s has too many parms", define->name ); + return qfalse; + } //end if + parms[numparms] = NULL; + lastcomma = 1; + last = NULL; + while ( !done ) + { + // + if ( !PC_ReadSourceToken( source, &token ) ) { + // Knightmare- debug output + // printf("PC_ReadDefineParms: define %s incomplete in file %s\n", define->name, source->filename); + SourceError( source, "define %s incomplete", define->name ); + return qfalse; + } //end if + // + if ( !strcmp( token.string, "," ) ) { + if ( indent <= 0 ) { + if ( lastcomma ) { + SourceWarning( source, "too many comma's" ); + } + lastcomma = 1; + break; + } //end if + } //end if + lastcomma = 0; + // + if ( !strcmp( token.string, "(" ) ) { + indent++; + continue; + } //end if + else if ( !strcmp( token.string, ")" ) ) { + if ( --indent <= 0 ) { + if ( !parms[define->numparms - 1] ) { + SourceWarning( source, "too few define parms" ); + } //end if + done = 1; + break; + } //end if + } //end if + // + if ( numparms < define->numparms ) { + // + t = PC_CopyToken( &token ); + t->next = NULL; + if ( last ) { + last->next = t; + } else { parms[numparms] = t;} + last = t; + } //end if + } //end while + numparms++; + } //end for + return qtrue; +} //end of the function PC_ReadDefineParms +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_StringizeTokens( token_t *tokens, token_t *token ) { + token_t *t; + + token->type = TT_STRING; + token->whitespace_p = NULL; + token->endwhitespace_p = NULL; + token->string[0] = '\0'; + strcat( token->string, "\"" ); + for ( t = tokens; t; t = t->next ) + { + strncat( token->string, t->string, MAX_TOKEN - strlen( token->string ) ); + } //end for + strncat( token->string, "\"", MAX_TOKEN - strlen( token->string ) ); + return qtrue; +} //end of the function PC_StringizeTokens +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_MergeTokens( token_t *t1, token_t *t2 ) { + //merging of a name with a name or number + if ( t1->type == TT_NAME && ( t2->type == TT_NAME || t2->type == TT_NUMBER ) ) { + strcat( t1->string, t2->string ); + return qtrue; + } //end if + //merging of two strings + if ( t1->type == TT_STRING && t2->type == TT_STRING ) { + //remove trailing double quote + t1->string[strlen( t1->string ) - 1] = '\0'; + //concat without leading double quote + strcat( t1->string, &t2->string[1] ); + return qtrue; + } //end if + //FIXME: merging of two number of the same sub type + return qfalse; +} //end of the function PC_MergeTokens +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +/* +void PC_PrintDefine(define_t *define) +{ + printf("define->name = %s\n", define->name); + printf("define->flags = %d\n", define->flags); + printf("define->builtin = %d\n", define->builtin); + printf("define->numparms = %d\n", define->numparms); +// token_t *parms; //define parameters +// token_t *tokens; //macro tokens (possibly containing parm tokens) +// struct define_s *next; //next defined macro in a list +} //end of the function PC_PrintDefine*/ +#if DEFINEHASHING +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_PrintDefineHashTable( define_t **definehash ) { + int i; + define_t *d; + + for ( i = 0; i < DEFINEHASHSIZE; i++ ) + { + Log_Write( "%4d:", i ); + for ( d = definehash[i]; d; d = d->hashnext ) + { + Log_Write( " %s", d->name ); + } //end for + Log_Write( "\n" ); + } //end for +} //end of the function PC_PrintDefineHashTable +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +//char primes[16] = {1, 3, 5, 7, 11, 13, 17, 19, 23, 27, 29, 31, 37, 41, 43, 47}; + +int PC_NameHash( char *name ) { + int register hash, i; + + hash = 0; + for ( i = 0; name[i] != '\0'; i++ ) + { + hash += name[i] * ( 119 + i ); + //hash += (name[i] << 7) + i; + //hash += (name[i] << (i&15)); + } //end while + hash = ( hash ^ ( hash >> 10 ) ^ ( hash >> 20 ) ) & ( DEFINEHASHSIZE - 1 ); + return hash; +} //end of the function PC_NameHash +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_AddDefineToHash( define_t *define, define_t **definehash ) { + int hash; + + hash = PC_NameHash( define->name ); + define->hashnext = definehash[hash]; + definehash[hash] = define; +} //end of the function PC_AddDefineToHash +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_FindHashedDefine( define_t **definehash, char *name ) { + define_t *d; + int hash; + + hash = PC_NameHash( name ); + for ( d = definehash[hash]; d; d = d->hashnext ) + { + if ( !strcmp( d->name, name ) ) { + return d; + } + } //end for + return NULL; +} //end of the function PC_FindHashedDefine +#endif //DEFINEHASHING +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_FindDefine( define_t *defines, char *name ) { + define_t *d; + + for ( d = defines; d; d = d->next ) + { + if ( !strcmp( d->name, name ) ) { + return d; + } + } //end for + return NULL; +} //end of the function PC_FindDefine +//============================================================================ +// +// Parameter: - +// Returns: number of the parm +// if no parm found with the given name -1 is returned +// Changes Globals: - +//============================================================================ +int PC_FindDefineParm( define_t *define, char *name ) { + token_t *p; + int i; + + i = 0; + for ( p = define->parms; p; p = p->next ) + { + if ( !strcmp( p->string, name ) ) { + return i; + } + i++; + } //end for + return -1; +} //end of the function PC_FindDefineParm +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_FreeDefine( define_t *define ) { + token_t *t, *next; + + //free the define parameters + for ( t = define->parms; t; t = next ) + { + next = t->next; + PC_FreeToken( t ); + } //end for + //free the define tokens + for ( t = define->tokens; t; t = next ) + { + next = t->next; + PC_FreeToken( t ); + } //end for + //free the define + FreeMemory( define ); +} //end of the function PC_FreeDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_AddBuiltinDefines( source_t *source ) { + int i; + define_t *define; + struct builtin + { + char *string; + int builtin; + } builtin[] = { + "__LINE__", BUILTIN_LINE, + "__FILE__", BUILTIN_FILE, + "__DATE__", BUILTIN_DATE, + "__TIME__", BUILTIN_TIME, +// "__STDC__", BUILTIN_STDC, + NULL, 0 + }; + + for ( i = 0; builtin[i].string; i++ ) + { + define = (define_t *) GetMemory( sizeof( define_t ) + strlen( builtin[i].string ) + 1 ); + memset( define, 0, sizeof( define_t ) ); + define->name = (char *) define + sizeof( define_t ); + strcpy( define->name, builtin[i].string ); + define->flags |= DEFINE_FIXED; + define->builtin = builtin[i].builtin; + //add the define to the source +#if DEFINEHASHING + PC_AddDefineToHash( define, source->definehash ); +#else + define->next = source->defines; + source->defines = define; +#endif //DEFINEHASHING + } //end for +} //end of the function PC_AddBuiltinDefines +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpandBuiltinDefine( source_t *source, define_t *define, + token_t **firsttoken, token_t **lasttoken ) { + token_t token; + unsigned long t; // time_t t; //to prevent LCC warning + char *curtime; + + memcpy( &token, &source->token, sizeof( token_t ) ); + switch ( define->builtin ) + { + case BUILTIN_LINE: + { + sprintf( token.string, "%d", source->token.line ); +#ifdef NUMBERVALUE + token.intvalue = source->token.line; + token.floatvalue = source->token.line; +#endif //NUMBERVALUE + token.type = TT_NUMBER; + token.subtype = TT_DECIMAL | TT_INTEGER; + *firsttoken = &token; + *lasttoken = &token; + break; + } //end case + case BUILTIN_FILE: + { + strcpy( token.string, source->scriptstack->filename ); + token.type = TT_NAME; + token.subtype = strlen( token.string ); + *firsttoken = &token; + *lasttoken = &token; + break; + } //end case + case BUILTIN_DATE: + { + t = time( NULL ); + curtime = ctime( &t ); + strcpy( token.string, "\"" ); + strncat( token.string, curtime + 4, 7 ); + strncat( token.string + 7, curtime + 20, 4 ); + strcat( token.string, "\"" ); + free( curtime ); + token.type = TT_NAME; + token.subtype = strlen( token.string ); + *firsttoken = &token; + *lasttoken = &token; + break; + } //end case + case BUILTIN_TIME: + { + t = time( NULL ); + curtime = ctime( &t ); + strcpy( token.string, "\"" ); + strncat( token.string, curtime + 11, 8 ); + strcat( token.string, "\"" ); + free( curtime ); + token.type = TT_NAME; + token.subtype = strlen( token.string ); + *firsttoken = &token; + *lasttoken = &token; + break; + } //end case + case BUILTIN_STDC: + default: + { + *firsttoken = NULL; + *lasttoken = NULL; + break; + } //end case + } //end switch + return qtrue; +} //end of the function PC_ExpandBuiltinDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpandDefine( source_t *source, define_t *define, + token_t **firsttoken, token_t **lasttoken ) { + token_t *parms[MAX_DEFINEPARMS], *dt, *pt, *t; + token_t *t1, *t2, *first, *last, *nextpt, token; + int parmnum, i; + + //if it is a builtin define + if ( define->builtin ) { + return PC_ExpandBuiltinDefine( source, define, firsttoken, lasttoken ); + } //end if + //if the define has parameters + if ( define->numparms ) { + if ( !PC_ReadDefineParms( source, define, parms, MAX_DEFINEPARMS ) ) { + return qfalse; + } +#ifdef DEBUG_EVAL + for ( i = 0; i < define->numparms; i++ ) + { + Log_Write( "define parms %d:", i ); + for ( pt = parms[i]; pt; pt = pt->next ) + { + Log_Write( "%s", pt->string ); + } //end for + } //end for +#endif //DEBUG_EVAL + } //end if + //empty list at first + first = NULL; + last = NULL; + //create a list with tokens of the expanded define + for ( dt = define->tokens; dt; dt = dt->next ) + { + parmnum = -1; + //if the token is a name, it could be a define parameter + if ( dt->type == TT_NAME ) { + parmnum = PC_FindDefineParm( define, dt->string ); + } //end if + //if it is a define parameter + if ( parmnum >= 0 ) { + for ( pt = parms[parmnum]; pt; pt = pt->next ) + { + t = PC_CopyToken( pt ); + //add the token to the list + t->next = NULL; + if ( last ) { + last->next = t; + } else { first = t;} + last = t; + } //end for + } //end if + else + { + //if stringizing operator + if ( dt->string[0] == '#' && dt->string[1] == '\0' ) { + //the stringizing operator must be followed by a define parameter + if ( dt->next ) { + parmnum = PC_FindDefineParm( define, dt->next->string ); + } else { parmnum = -1;} + // + if ( parmnum >= 0 ) { + //step over the stringizing operator + dt = dt->next; + //stringize the define parameter tokens + if ( !PC_StringizeTokens( parms[parmnum], &token ) ) { + SourceError( source, "can't stringize tokens" ); + return qfalse; + } //end if + t = PC_CopyToken( &token ); + } //end if + else + { + SourceWarning( source, "stringizing operator without define parameter" ); + continue; + } //end if + } //end if + else + { + t = PC_CopyToken( dt ); + } //end else + //add the token to the list + t->next = NULL; + if ( last ) { + last->next = t; + } else { first = t;} + last = t; + } //end else + } //end for + //check for the merging operator + for ( t = first; t; ) + { + if ( t->next ) { + //if the merging operator + if ( t->next->string[0] == '#' && t->next->string[1] == '#' ) { + t1 = t; + t2 = t->next->next; + if ( t2 ) { + if ( !PC_MergeTokens( t1, t2 ) ) { + SourceError( source, "can't merge %s with %s", t1->string, t2->string ); + return qfalse; + } //end if + PC_FreeToken( t1->next ); + t1->next = t2->next; + if ( t2 == last ) { + last = t1; + } + PC_FreeToken( t2 ); + continue; + } //end if + } //end if + } //end if + t = t->next; + } //end for + //store the first and last token of the list + *firsttoken = first; + *lasttoken = last; + //free all the parameter tokens + for ( i = 0; i < define->numparms; i++ ) + { + for ( pt = parms[i]; pt; pt = nextpt ) + { + nextpt = pt->next; + PC_FreeToken( pt ); + } //end for + } //end for + // + return qtrue; +} //end of the function PC_ExpandDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpandDefineIntoSource( source_t *source, define_t *define ) { + token_t *firsttoken, *lasttoken; + + if ( !PC_ExpandDefine( source, define, &firsttoken, &lasttoken ) ) { + return qfalse; + } + + if ( firsttoken && lasttoken ) { + lasttoken->next = source->tokens; + source->tokens = firsttoken; + return qtrue; + } //end if + return qfalse; +} //end of the function PC_ExpandDefineIntoSource +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_ConvertPath( char *path ) { + char *ptr; + + //remove double path seperators + for ( ptr = path; *ptr; ) + { + if ( ( *ptr == '\\' || *ptr == '/' ) && + ( *( ptr + 1 ) == '\\' || *( ptr + 1 ) == '/' ) ) { + strcpy( ptr, ptr + 1 ); + } //end if + else + { + ptr++; + } //end else + } //end while + //set OS dependent path seperators + for ( ptr = path; *ptr; ) + { + if ( *ptr == '/' || *ptr == '\\' ) { + *ptr = PATHSEPERATOR_CHAR; + } + ptr++; + } //end while +} //end of the function PC_ConvertPath +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_include( source_t *source ) { + script_t *script; + token_t token; + char path[_MAX_PATH]; +#ifdef QUAKE + foundfile_t file; +#endif //QUAKE + + if ( source->skip > 0 ) { + return qtrue; + } + // + if ( !PC_ReadSourceToken( source, &token ) ) { + SourceError( source, "#include without file name" ); + return qfalse; + } //end if + if ( token.linescrossed > 0 ) { + SourceError( source, "#include without file name" ); + return qfalse; + } //end if + + if ( token.type == TT_STRING ) { + StripDoubleQuotes( token.string ); + PC_ConvertPath( token.string ); + script = LoadScriptFile( token.string ); + if ( !script ) { + strcpy( path, source->includepath ); + strcat( path, token.string ); + script = LoadScriptFile( path ); + } //end if + } //end if + else if ( token.type == TT_PUNCTUATION && *token.string == '<' ) { + strcpy( path, source->includepath ); + while ( PC_ReadSourceToken( source, &token ) ) + { + if ( token.linescrossed > 0 ) { + PC_UnreadSourceToken( source, &token ); + break; + } //end if + if ( token.type == TT_PUNCTUATION && *token.string == '>' ) { + break; + } + strncat( path, token.string, _MAX_PATH ); + } //end while + if ( *token.string != '>' ) { + SourceWarning( source, "#include missing trailing >" ); + } //end if + if ( !strlen( path ) ) { + SourceError( source, "#include without file name between < >" ); + return qfalse; + } //end if + PC_ConvertPath( path ); + script = LoadScriptFile( path ); + } //end if + else + { + SourceError( source, "#include without file name" ); + return qfalse; + } //end else +#ifdef QUAKE + if ( !script ) { + memset( &file, 0, sizeof( foundfile_t ) ); + script = LoadScriptFile( path ); + if ( script ) { + strncpy( script->filename, path, _MAX_PATH ); + } + } //end if +#endif //QUAKE + if ( !script ) { +#ifdef SCREWUP + SourceWarning( source, "file %s not found", path ); + return qtrue; +#else + SourceError( source, "file %s not found", path ); + return qfalse; +#endif //SCREWUP + } //end if + PC_PushScript( source, script ); + return qtrue; +} //end of the function PC_Directive_include +//============================================================================ +// reads a token from the current line, continues reading on the next +// line only if a backslash '\' is encountered. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadLine( source_t *source, token_t *token ) { + int crossline; + + crossline = 0; + do + { + if ( !PC_ReadSourceToken( source, token ) ) { + return qfalse; + } + + if ( token->linescrossed > crossline ) { + PC_UnreadSourceToken( source, token ); + return qfalse; + } //end if + crossline = 1; + } while ( !strcmp( token->string, "\\" ) ); + return qtrue; +} //end of the function PC_ReadLine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_WhiteSpaceBeforeToken( token_t *token ) { + return token->endwhitespace_p - token->whitespace_p > 0; +} //end of the function PC_WhiteSpaceBeforeToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_ClearTokenWhiteSpace( token_t *token ) { + token->whitespace_p = NULL; + token->endwhitespace_p = NULL; + token->linescrossed = 0; +} //end of the function PC_ClearTokenWhiteSpace +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_undef( source_t *source ) { + token_t token; + define_t *define, *lastdefine; + int hash; + + if ( source->skip > 0 ) { + return qtrue; + } + // + if ( !PC_ReadLine( source, &token ) ) { + SourceError( source, "undef without name" ); + return qfalse; + } //end if + if ( token.type != TT_NAME ) { + PC_UnreadSourceToken( source, &token ); + SourceError( source, "expected name, found %s", token.string ); + return qfalse; + } //end if +#if DEFINEHASHING + hash = PC_NameHash( token.string ); + for ( lastdefine = NULL, define = source->definehash[hash]; define; define = define->hashnext ) + { + if ( !strcmp( define->name, token.string ) ) { + if ( define->flags & DEFINE_FIXED ) { + SourceWarning( source, "can't undef %s", token.string ); + } //end if + else + { + if ( lastdefine ) { + lastdefine->hashnext = define->hashnext; + } else { source->definehash[hash] = define->hashnext;} + PC_FreeDefine( define ); + } //end else + break; + } //end if + lastdefine = define; + } //end for +#else //DEFINEHASHING + for ( lastdefine = NULL, define = source->defines; define; define = define->next ) + { + if ( !strcmp( define->name, token.string ) ) { + if ( define->flags & DEFINE_FIXED ) { + SourceWarning( source, "can't undef %s", token.string ); + } //end if + else + { + if ( lastdefine ) { + lastdefine->next = define->next; + } else { source->defines = define->next;} + PC_FreeDefine( define ); + } //end else + break; + } //end if + lastdefine = define; + } //end for +#endif //DEFINEHASHING + return qtrue; +} //end of the function PC_Directive_undef +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_define( source_t *source ) { + token_t token, *t, *last; + define_t *define; + + if ( source->skip > 0 ) { + return qtrue; + } + // + if ( !PC_ReadLine( source, &token ) ) { + SourceError( source, "#define without name" ); + return qfalse; + } //end if + if ( token.type != TT_NAME ) { + PC_UnreadSourceToken( source, &token ); + SourceError( source, "expected name after #define, found %s", token.string ); + return qfalse; + } //end if + //check if the define already exists +#if DEFINEHASHING + define = PC_FindHashedDefine( source->definehash, token.string ); +#else + define = PC_FindDefine( source->defines, token.string ); +#endif //DEFINEHASHING + if ( define ) { + if ( define->flags & DEFINE_FIXED ) { + SourceError( source, "can't redefine %s", token.string ); + return qfalse; + } //end if + SourceWarning( source, "redefinition of %s", token.string ); + //unread the define name before executing the #undef directive + PC_UnreadSourceToken( source, &token ); + if ( !PC_Directive_undef( source ) ) { + return qfalse; + } + //if the define was not removed (define->flags & DEFINE_FIXED) +#if DEFINEHASHING + define = PC_FindHashedDefine( source->definehash, token.string ); +#else + define = PC_FindDefine( source->defines, token.string ); +#endif //DEFINEHASHING + } //end if + //allocate define + define = (define_t *) GetMemory( sizeof( define_t ) + strlen( token.string ) + 1 ); + memset( define, 0, sizeof( define_t ) ); + define->name = (char *) define + sizeof( define_t ); + strcpy( define->name, token.string ); + //add the define to the source +#if DEFINEHASHING + PC_AddDefineToHash( define, source->definehash ); +#else //DEFINEHASHING + define->next = source->defines; + source->defines = define; +#endif //DEFINEHASHING + //if nothing is defined, just return + if ( !PC_ReadLine( source, &token ) ) { + return qtrue; + } + //if it is a define with parameters + if ( !PC_WhiteSpaceBeforeToken( &token ) && !strcmp( token.string, "(" ) ) { + //read the define parameters + last = NULL; + if ( !PC_CheckTokenString( source, ")" ) ) { + while ( 1 ) + { + if ( !PC_ReadLine( source, &token ) ) { + SourceError( source, "expected define parameter" ); + return qfalse; + } //end if + //if it isn't a name + if ( token.type != TT_NAME ) { + SourceError( source, "invalid define parameter" ); + return qfalse; + } //end if + // + if ( PC_FindDefineParm( define, token.string ) >= 0 ) { + SourceError( source, "two the same define parameters" ); + return qfalse; + } //end if + //add the define parm + t = PC_CopyToken( &token ); + PC_ClearTokenWhiteSpace( t ); + t->next = NULL; + if ( last ) { + last->next = t; + } else { define->parms = t;} + last = t; + define->numparms++; + //read next token + if ( !PC_ReadLine( source, &token ) ) { + SourceError( source, "define parameters not terminated" ); + return qfalse; + } //end if + // + if ( !strcmp( token.string, ")" ) ) { + break; + } + //then it must be a comma + if ( strcmp( token.string, "," ) ) { + SourceError( source, "define not terminated" ); + return qfalse; + } //end if + } //end while + } //end if + if ( !PC_ReadLine( source, &token ) ) { + return qtrue; + } + } //end if + //read the defined stuff + last = NULL; + do + { + t = PC_CopyToken( &token ); + if ( t->type == TT_NAME && !strcmp( t->string, define->name ) ) { + SourceError( source, "recursive define (removed recursion)" ); + continue; + } //end if + PC_ClearTokenWhiteSpace( t ); + t->next = NULL; + if ( last ) { + last->next = t; + } else { define->tokens = t;} + last = t; + } while ( PC_ReadLine( source, &token ) ); + // + if ( last ) { + //check for merge operators at the beginning or end + if ( !strcmp( define->tokens->string, "##" ) || + !strcmp( last->string, "##" ) ) { + SourceError( source, "define with misplaced ##" ); + return qfalse; + } //end if + } //end if + return qtrue; +} //end of the function PC_Directive_define +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_DefineFromString( char *string ) { + script_t *script; + source_t src; + token_t *t; + int res, i; + define_t *def; + + PC_InitTokenHeap(); + + script = LoadScriptMemory( string, strlen( string ), "*extern" ); + //create a new source + memset( &src, 0, sizeof( source_t ) ); + strncpy( src.filename, "*extern", _MAX_PATH ); + src.scriptstack = script; +#if DEFINEHASHING + src.definehash = GetClearedMemory( DEFINEHASHSIZE * sizeof( define_t * ) ); +#endif //DEFINEHASHING + //create a define from the source + res = PC_Directive_define( &src ); + //free any tokens if left + for ( t = src.tokens; t; t = src.tokens ) + { + src.tokens = src.tokens->next; + PC_FreeToken( t ); + } //end for +#ifdef DEFINEHASHING + def = NULL; + for ( i = 0; i < DEFINEHASHSIZE; i++ ) + { + if ( src.definehash[i] ) { + def = src.definehash[i]; + break; + } //end if + } //end for +#else + def = src.defines; +#endif //DEFINEHASHING + // +#if DEFINEHASHING + FreeMemory( src.definehash ); +#endif //DEFINEHASHING + // + FreeScript( script ); + //if the define was created succesfully + if ( res > 0 ) { + return def; + } + //free the define if created + if ( src.defines ) { + PC_FreeDefine( def ); + } + // + return NULL; +} //end of the function PC_DefineFromString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_AddDefine( source_t *source, char *string ) { + define_t *define; + + define = PC_DefineFromString( string ); + if ( !define ) { + return qfalse; + } +#if DEFINEHASHING + PC_AddDefineToHash( define, source->definehash ); +#else //DEFINEHASHING + define->next = source->defines; + source->defines = define; +#endif //DEFINEHASHING + return qtrue; +} //end of the function PC_AddDefine +//============================================================================ +// add a globals define that will be added to all opened sources +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_AddGlobalDefine( char *string ) { + define_t *define; + + define = PC_DefineFromString( string ); + if ( !define ) { + return qfalse; + } + define->next = globaldefines; + globaldefines = define; + return qtrue; +} //end of the function PC_AddGlobalDefine +//============================================================================ +// remove the given global define +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_RemoveGlobalDefine( char *name ) { + define_t *define; + + define = PC_FindDefine( globaldefines, name ); + if ( define ) { + PC_FreeDefine( define ); + return qtrue; + } //end if + return qfalse; +} //end of the function PC_RemoveGlobalDefine +//============================================================================ +// remove all globals defines +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_RemoveAllGlobalDefines( void ) { + define_t *define; + + for ( define = globaldefines; define; define = globaldefines ) + { + globaldefines = globaldefines->next; + PC_FreeDefine( define ); + } //end for +} //end of the function PC_RemoveAllGlobalDefines +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +define_t *PC_CopyDefine( source_t *source, define_t *define ) { + define_t *newdefine; + token_t *token, *newtoken, *lasttoken; + + newdefine = (define_t *) GetMemory( sizeof( define_t ) + strlen( define->name ) + 1 ); + //copy the define name + newdefine->name = (char *) newdefine + sizeof( define_t ); + strcpy( newdefine->name, define->name ); + newdefine->flags = define->flags; + newdefine->builtin = define->builtin; + newdefine->numparms = define->numparms; + //the define is not linked + newdefine->next = NULL; + newdefine->hashnext = NULL; + //copy the define tokens + newdefine->tokens = NULL; + for ( lasttoken = NULL, token = define->tokens; token; token = token->next ) + { + newtoken = PC_CopyToken( token ); + newtoken->next = NULL; + if ( lasttoken ) { + lasttoken->next = newtoken; + } else { newdefine->tokens = newtoken;} + lasttoken = newtoken; + } //end for + //copy the define parameters + newdefine->parms = NULL; + for ( lasttoken = NULL, token = define->parms; token; token = token->next ) + { + newtoken = PC_CopyToken( token ); + newtoken->next = NULL; + if ( lasttoken ) { + lasttoken->next = newtoken; + } else { newdefine->parms = newtoken;} + lasttoken = newtoken; + } //end for + return newdefine; +} //end of the function PC_CopyDefine +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_AddGlobalDefinesToSource( source_t *source ) { + define_t *define, *newdefine; + + for ( define = globaldefines; define; define = define->next ) + { + newdefine = PC_CopyDefine( source, define ); +#if DEFINEHASHING + PC_AddDefineToHash( newdefine, source->definehash ); +#else //DEFINEHASHING + newdefine->next = source->defines; + source->defines = newdefine; +#endif //DEFINEHASHING + } //end for +} //end of the function PC_AddGlobalDefinesToSource +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_if_def( source_t *source, int type ) { + token_t token; + define_t *d; + int skip; + + if ( !PC_ReadLine( source, &token ) ) { + SourceError( source, "#ifdef without name" ); + return qfalse; + } //end if + if ( token.type != TT_NAME ) { + PC_UnreadSourceToken( source, &token ); + SourceError( source, "expected name after #ifdef, found %s", token.string ); + return qfalse; + } //end if +#if DEFINEHASHING + d = PC_FindHashedDefine( source->definehash, token.string ); +#else + d = PC_FindDefine( source->defines, token.string ); +#endif //DEFINEHASHING + skip = ( type == INDENT_IFDEF ) == ( d == NULL ); + PC_PushIndent( source, type, skip ); + return qtrue; +} //end of the function PC_Directiveif_def +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_ifdef( source_t *source ) { + return PC_Directive_if_def( source, INDENT_IFDEF ); +} //end of the function PC_Directive_ifdef +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_ifndef( source_t *source ) { + return PC_Directive_if_def( source, INDENT_IFNDEF ); +} //end of the function PC_Directive_ifndef +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_else( source_t *source ) { + int type, skip; + + PC_PopIndent( source, &type, &skip ); + if ( !type ) { + SourceError( source, "misplaced #else" ); + return qfalse; + } //end if + if ( type == INDENT_ELSE ) { + SourceError( source, "#else after #else" ); + return qfalse; + } //end if + PC_PushIndent( source, INDENT_ELSE, !skip ); + return qtrue; +} //end of the function PC_Directive_else +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_endif( source_t *source ) { + int type, skip; + + PC_PopIndent( source, &type, &skip ); + if ( !type ) { + SourceError( source, "misplaced #endif" ); + return qfalse; + } //end if + return qtrue; +} //end of the function PC_Directive_endif +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +typedef struct operator_s +{ + int operator; + int priority; + int parentheses; + struct operator_s *prev, *next; +} operator_t; + +typedef struct value_s +{ + signed long int intvalue; + double floatvalue; + int parentheses; + struct value_s *prev, *next; +} value_t; + +int PC_OperatorPriority( int op ) { + switch ( op ) + { + case P_MUL: return 15; + case P_DIV: return 15; + case P_MOD: return 15; + case P_ADD: return 14; + case P_SUB: return 14; + + case P_LOGIC_AND: return 7; + case P_LOGIC_OR: return 6; + case P_LOGIC_GEQ: return 12; + case P_LOGIC_LEQ: return 12; + case P_LOGIC_EQ: return 11; + case P_LOGIC_UNEQ: return 11; + + case P_LOGIC_NOT: return 16; + case P_LOGIC_GREATER: return 12; + case P_LOGIC_LESS: return 12; + + case P_RSHIFT: return 13; + case P_LSHIFT: return 13; + + case P_BIN_AND: return 10; + case P_BIN_OR: return 8; + case P_BIN_XOR: return 9; + case P_BIN_NOT: return 16; + + case P_COLON: return 5; + case P_QUESTIONMARK: return 5; + } //end switch + return qfalse; +} //end of the function PC_OperatorPriority + +//#define AllocValue() GetClearedMemory(sizeof(value_t)); +//#define FreeValue(val) FreeMemory(val) +//#define AllocOperator(op) op = (operator_t *) GetClearedMemory(sizeof(operator_t)); +//#define FreeOperator(op) FreeMemory(op); + +#define MAX_VALUES 64 +#define MAX_OPERATORS 64 +#define AllocValue( val ) \ + if ( numvalues >= MAX_VALUES ) { \ + SourceError( source, "out value space\n" ); \ + error = 1; \ + break; \ + } \ + else { \ + val = &value_heap[numvalues++];} +#define FreeValue( val ) +// +#define AllocOperator( op ) \ + if ( numoperators >= MAX_OPERATORS ) { \ + SourceError( source, "out operator space\n" ); \ + error = 1; \ + break; \ + } \ + else { \ + op = &operator_heap[numoperators++];} +#define FreeOperator( op ) + +int PC_EvaluateTokens( source_t *source, token_t *tokens, signed long int *intvalue, + double *floatvalue, int integer ) { + operator_t *o, *firstoperator, *lastoperator; + value_t *v, *firstvalue, *lastvalue, *v1, *v2; + token_t *t; + int brace = 0; + int parentheses = 0; + int error = 0; + int lastwasvalue = 0; + int negativevalue = 0; + int questmarkintvalue = 0; + double questmarkfloatvalue = 0; + int gotquestmarkvalue = qfalse; + int lastoperatortype = 0; + // + operator_t operator_heap[MAX_OPERATORS]; + int numoperators = 0; + value_t value_heap[MAX_VALUES]; + int numvalues = 0; + + firstoperator = lastoperator = NULL; + firstvalue = lastvalue = NULL; + if ( intvalue ) { + *intvalue = 0; + } + if ( floatvalue ) { + *floatvalue = 0; + } + for ( t = tokens; t; t = t->next ) + { + switch ( t->type ) + { + case TT_NAME: + { + if ( lastwasvalue || negativevalue ) { + SourceError( source, "syntax error in #if/#elif" ); + error = 1; + break; + } //end if + if ( strcmp( t->string, "defined" ) ) { + SourceError( source, "undefined name %s in #if/#elif", t->string ); + error = 1; + break; + } //end if + t = t->next; + if ( !strcmp( t->string, "(" ) ) { + brace = qtrue; + t = t->next; + } //end if + if ( !t || t->type != TT_NAME ) { + SourceError( source, "defined without name in #if/#elif" ); + error = 1; + break; + } //end if + //v = (value_t *) GetClearedMemory(sizeof(value_t)); + AllocValue( v ); +#if DEFINEHASHING + if ( PC_FindHashedDefine( source->definehash, t->string ) ) +#else + if ( PC_FindDefine( source->defines, t->string ) ) +#endif //DEFINEHASHING + { + v->intvalue = 1; + v->floatvalue = 1; + } //end if + else + { + v->intvalue = 0; + v->floatvalue = 0; + } //end else + v->parentheses = parentheses; + v->next = NULL; + v->prev = lastvalue; + if ( lastvalue ) { + lastvalue->next = v; + } else { firstvalue = v;} + lastvalue = v; + if ( brace ) { + t = t->next; + if ( !t || strcmp( t->string, ")" ) ) { + SourceError( source, "defined without ) in #if/#elif" ); + error = 1; + break; + } //end if + } //end if + brace = qfalse; + // defined() creates a value + lastwasvalue = 1; + break; + } //end case + case TT_NUMBER: + { + if ( lastwasvalue ) { + SourceError( source, "syntax error in #if/#elif" ); + error = 1; + break; + } //end if + //v = (value_t *) GetClearedMemory(sizeof(value_t)); + AllocValue( v ); + if ( negativevalue ) { + v->intvalue = -(signed int) t->intvalue; + v->floatvalue = -t->floatvalue; + } //end if + else + { + v->intvalue = t->intvalue; + v->floatvalue = t->floatvalue; + } //end else + v->parentheses = parentheses; + v->next = NULL; + v->prev = lastvalue; + if ( lastvalue ) { + lastvalue->next = v; + } else { firstvalue = v;} + lastvalue = v; + //last token was a value + lastwasvalue = 1; + // + negativevalue = 0; + break; + } //end case + case TT_PUNCTUATION: + { + if ( negativevalue ) { + SourceError( source, "misplaced minus sign in #if/#elif" ); + error = 1; + break; + } //end if + if ( t->subtype == P_PARENTHESESOPEN ) { + parentheses++; + break; + } //end if + else if ( t->subtype == P_PARENTHESESCLOSE ) { + parentheses--; + if ( parentheses < 0 ) { + SourceError( source, "too many ) in #if/#elsif" ); + error = 1; + } //end if + break; + } //end else if + //check for invalid operators on floating point values + if ( !integer ) { + if ( t->subtype == P_BIN_NOT || t->subtype == P_MOD || + t->subtype == P_RSHIFT || t->subtype == P_LSHIFT || + t->subtype == P_BIN_AND || t->subtype == P_BIN_OR || + t->subtype == P_BIN_XOR ) { + SourceError( source, "illigal operator %s on floating point operands\n", t->string ); + error = 1; + break; + } //end if + } //end if + switch ( t->subtype ) + { + case P_LOGIC_NOT: + case P_BIN_NOT: + { + if ( lastwasvalue ) { + SourceError( source, "! or ~ after value in #if/#elif" ); + error = 1; + break; + } //end if + break; + } //end case + case P_SUB: + { + if ( !lastwasvalue ) { + negativevalue = 1; + break; + } //end if + } //end case + + case P_MUL: + case P_DIV: + case P_MOD: + case P_ADD: + + case P_LOGIC_AND: + case P_LOGIC_OR: + case P_LOGIC_GEQ: + case P_LOGIC_LEQ: + case P_LOGIC_EQ: + case P_LOGIC_UNEQ: + + case P_LOGIC_GREATER: + case P_LOGIC_LESS: + + case P_RSHIFT: + case P_LSHIFT: + + case P_BIN_AND: + case P_BIN_OR: + case P_BIN_XOR: + + case P_COLON: + case P_QUESTIONMARK: + { + if ( !lastwasvalue ) { + SourceError( source, "operator %s after operator in #if/#elif", t->string ); + error = 1; + break; + } //end if + break; + } //end case + default: + { + SourceError( source, "invalid operator %s in #if/#elif", t->string ); + error = 1; + break; + } //end default + } //end switch + if ( !error && !negativevalue ) { + //o = (operator_t *) GetClearedMemory(sizeof(operator_t)); + AllocOperator( o ); + o->operator = t->subtype; + o->priority = PC_OperatorPriority( t->subtype ); + o->parentheses = parentheses; + o->next = NULL; + o->prev = lastoperator; + if ( lastoperator ) { + lastoperator->next = o; + } else { firstoperator = o;} + lastoperator = o; + lastwasvalue = 0; + } //end if + break; + } //end case + default: + { + SourceError( source, "unknown %s in #if/#elif", t->string ); + error = 1; + break; + } //end default + } //end switch + if ( error ) { + break; + } + } //end for + if ( !error ) { + if ( !lastwasvalue ) { + SourceError( source, "trailing operator in #if/#elif" ); + error = 1; + } //end if + else if ( parentheses ) { + SourceError( source, "too many ( in #if/#elif" ); + error = 1; + } //end else if + } //end if + // + gotquestmarkvalue = qfalse; + questmarkintvalue = 0; + questmarkfloatvalue = 0; + //while there are operators + while ( !error && firstoperator ) + { + v = firstvalue; + for ( o = firstoperator; o->next; o = o->next ) + { + //if the current operator is nested deeper in parentheses + //than the next operator + if ( o->parentheses > o->next->parentheses ) { + break; + } + //if the current and next operator are nested equally deep in parentheses + if ( o->parentheses == o->next->parentheses ) { + //if the priority of the current operator is equal or higher + //than the priority of the next operator + if ( o->priority >= o->next->priority ) { + break; + } + } //end if + //if the arity of the operator isn't equal to 1 + if ( o->operator != P_LOGIC_NOT + && o->operator != P_BIN_NOT ) { + v = v->next; + } + //if there's no value or no next value + if ( !v ) { + SourceError( source, "mising values in #if/#elif" ); + error = 1; + break; + } //end if + } //end for + if ( error ) { + break; + } + v1 = v; + v2 = v->next; +#ifdef DEBUG_EVAL + if ( integer ) { + Log_Write( "operator %s, value1 = %d", PunctuationFromNum( source->scriptstack, o->operator ), v1->intvalue ); + if ( v2 ) { + Log_Write( "value2 = %d", v2->intvalue ); + } + } //end if + else + { + Log_Write( "operator %s, value1 = %f", PunctuationFromNum( source->scriptstack, o->operator ), v1->floatvalue ); + if ( v2 ) { + Log_Write( "value2 = %f", v2->floatvalue ); + } + } //end else +#endif //DEBUG_EVAL + switch ( o->operator ) + { + case P_LOGIC_NOT: v1->intvalue = !v1->intvalue; + v1->floatvalue = !v1->floatvalue; break; + case P_BIN_NOT: v1->intvalue = ~v1->intvalue; + break; + case P_MUL: v1->intvalue *= v2->intvalue; + v1->floatvalue *= v2->floatvalue; break; + case P_DIV: v1->intvalue /= v2->intvalue; + v1->floatvalue /= v2->floatvalue; break; + case P_MOD: v1->intvalue %= v2->intvalue; + break; + case P_ADD: v1->intvalue += v2->intvalue; + v1->floatvalue += v2->floatvalue; break; + case P_SUB: v1->intvalue -= v2->intvalue; + v1->floatvalue -= v2->floatvalue; break; + case P_LOGIC_AND: v1->intvalue = v1->intvalue && v2->intvalue; + v1->floatvalue = v1->floatvalue && v2->floatvalue; break; + case P_LOGIC_OR: v1->intvalue = v1->intvalue || v2->intvalue; + v1->floatvalue = v1->floatvalue || v2->floatvalue; break; + case P_LOGIC_GEQ: v1->intvalue = v1->intvalue >= v2->intvalue; + v1->floatvalue = v1->floatvalue >= v2->floatvalue; break; + case P_LOGIC_LEQ: v1->intvalue = v1->intvalue <= v2->intvalue; + v1->floatvalue = v1->floatvalue <= v2->floatvalue; break; + case P_LOGIC_EQ: v1->intvalue = v1->intvalue == v2->intvalue; + v1->floatvalue = v1->floatvalue == v2->floatvalue; break; + case P_LOGIC_UNEQ: v1->intvalue = v1->intvalue != v2->intvalue; + v1->floatvalue = v1->floatvalue != v2->floatvalue; break; + case P_LOGIC_GREATER: v1->intvalue = v1->intvalue > v2->intvalue; + v1->floatvalue = v1->floatvalue > v2->floatvalue; break; + case P_LOGIC_LESS: v1->intvalue = v1->intvalue < v2->intvalue; + v1->floatvalue = v1->floatvalue < v2->floatvalue; break; + case P_RSHIFT: v1->intvalue >>= v2->intvalue; + break; + case P_LSHIFT: v1->intvalue <<= v2->intvalue; + break; + case P_BIN_AND: v1->intvalue &= v2->intvalue; + break; + case P_BIN_OR: v1->intvalue |= v2->intvalue; + break; + case P_BIN_XOR: v1->intvalue ^= v2->intvalue; + break; + case P_COLON: + { + if ( !gotquestmarkvalue ) { + SourceError( source, ": without ? in #if/#elif" ); + error = 1; + break; + } //end if + if ( integer ) { + if ( !questmarkintvalue ) { + v1->intvalue = v2->intvalue; + } + } //end if + else + { + if ( !questmarkfloatvalue ) { + v1->floatvalue = v2->floatvalue; + } + } //end else + gotquestmarkvalue = qfalse; + break; + } //end case + case P_QUESTIONMARK: + { + if ( gotquestmarkvalue ) { + SourceError( source, "? after ? in #if/#elif" ); + error = 1; + break; + } //end if + questmarkintvalue = v1->intvalue; + questmarkfloatvalue = v1->floatvalue; + gotquestmarkvalue = qtrue; + break; + } //end if + } //end switch +#ifdef DEBUG_EVAL + if ( integer ) { + Log_Write( "result value = %d", v1->intvalue ); + } else { Log_Write( "result value = %f", v1->floatvalue );} +#endif //DEBUG_EVAL + if ( error ) { + break; + } + lastoperatortype = o->operator; + //if not an operator with arity 1 + if ( o->operator != P_LOGIC_NOT + && o->operator != P_BIN_NOT ) { + //remove the second value if not question mark operator + if ( o->operator != P_QUESTIONMARK ) { + v = v->next; + } + // + if ( v->prev ) { + v->prev->next = v->next; + } else { firstvalue = v->next;} + if ( v->next ) { + v->next->prev = v->prev; + } else { lastvalue = v->prev;} + //FreeMemory(v); + FreeValue( v ); + } //end if + //remove the operator + if ( o->prev ) { + o->prev->next = o->next; + } else { firstoperator = o->next;} + if ( o->next ) { + o->next->prev = o->prev; + } else { lastoperator = o->prev;} + //FreeMemory(o); + FreeOperator( o ); + } //end while + if ( firstvalue ) { + if ( intvalue ) { + *intvalue = firstvalue->intvalue; + } + if ( floatvalue ) { + *floatvalue = firstvalue->floatvalue; + } + } //end if + for ( o = firstoperator; o; o = lastoperator ) + { + lastoperator = o->next; + //FreeMemory(o); + FreeOperator( o ); + } //end for + for ( v = firstvalue; v; v = lastvalue ) + { + lastvalue = v->next; + //FreeMemory(v); + FreeValue( v ); + } //end for + if ( !error ) { + return qtrue; + } + if ( intvalue ) { + *intvalue = 0; + } + if ( floatvalue ) { + *floatvalue = 0; + } + return qfalse; +} //end of the function PC_EvaluateTokens +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Evaluate( source_t *source, signed long int *intvalue, + double *floatvalue, int integer ) { + token_t token, *firsttoken, *lasttoken; + token_t *t, *nexttoken; + define_t *define; + int defined = qfalse; + + if ( intvalue ) { + *intvalue = 0; + } + if ( floatvalue ) { + *floatvalue = 0; + } + // + if ( !PC_ReadLine( source, &token ) ) { + SourceError( source, "no value after #if/#elif" ); + return qfalse; + } //end if + firsttoken = NULL; + lasttoken = NULL; + do + { + //if the token is a name + if ( token.type == TT_NAME ) { + if ( defined ) { + defined = qfalse; + t = PC_CopyToken( &token ); + t->next = NULL; + if ( lasttoken ) { + lasttoken->next = t; + } else { firsttoken = t;} + lasttoken = t; + } //end if + else if ( !strcmp( token.string, "defined" ) ) { + defined = qtrue; + t = PC_CopyToken( &token ); + t->next = NULL; + if ( lasttoken ) { + lasttoken->next = t; + } else { firsttoken = t;} + lasttoken = t; + } //end if + else + { + //then it must be a define +#if DEFINEHASHING + define = PC_FindHashedDefine( source->definehash, token.string ); +#else + define = PC_FindDefine( source->defines, token.string ); +#endif //DEFINEHASHING + if ( !define ) { + SourceError( source, "can't evaluate %s, not defined", token.string ); + return qfalse; + } //end if + if ( !PC_ExpandDefineIntoSource( source, define ) ) { + return qfalse; + } + } //end else + } //end if + //if the token is a number or a punctuation + else if ( token.type == TT_NUMBER || token.type == TT_PUNCTUATION ) { + t = PC_CopyToken( &token ); + t->next = NULL; + if ( lasttoken ) { + lasttoken->next = t; + } else { firsttoken = t;} + lasttoken = t; + } //end else + else //can't evaluate the token + { + SourceError( source, "can't evaluate %s", token.string ); + return qfalse; + } //end else + } while ( PC_ReadLine( source, &token ) ); + // + if ( !PC_EvaluateTokens( source, firsttoken, intvalue, floatvalue, integer ) ) { + return qfalse; + } + // +#ifdef DEBUG_EVAL + Log_Write( "eval:" ); +#endif //DEBUG_EVAL + for ( t = firsttoken; t; t = nexttoken ) + { +#ifdef DEBUG_EVAL + Log_Write( " %s", t->string ); +#endif //DEBUG_EVAL + nexttoken = t->next; + PC_FreeToken( t ); + } //end for +#ifdef DEBUG_EVAL + if ( integer ) { + Log_Write( "eval result: %d", *intvalue ); + } else { Log_Write( "eval result: %f", *floatvalue );} +#endif //DEBUG_EVAL + // + return qtrue; +} //end of the function PC_Evaluate +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_DollarEvaluate( source_t *source, signed long int *intvalue, + double *floatvalue, int integer ) { + int indent, defined = qfalse; + token_t token, *firsttoken, *lasttoken; + token_t *t, *nexttoken; + define_t *define; + + if ( intvalue ) { + *intvalue = 0; + } + if ( floatvalue ) { + *floatvalue = 0; + } + // + if ( !PC_ReadSourceToken( source, &token ) ) { + SourceError( source, "no leading ( after $evalint/$evalfloat" ); + return qfalse; + } //end if + if ( !PC_ReadSourceToken( source, &token ) ) { + SourceError( source, "nothing to evaluate" ); + return qfalse; + } //end if + indent = 1; + firsttoken = NULL; + lasttoken = NULL; + do + { + //if the token is a name + if ( token.type == TT_NAME ) { + if ( defined ) { + defined = qfalse; + t = PC_CopyToken( &token ); + t->next = NULL; + if ( lasttoken ) { + lasttoken->next = t; + } else { firsttoken = t;} + lasttoken = t; + } //end if + else if ( !strcmp( token.string, "defined" ) ) { + defined = qtrue; + t = PC_CopyToken( &token ); + t->next = NULL; + if ( lasttoken ) { + lasttoken->next = t; + } else { firsttoken = t;} + lasttoken = t; + } //end if + else + { + //then it must be a define +#if DEFINEHASHING + define = PC_FindHashedDefine( source->definehash, token.string ); +#else + define = PC_FindDefine( source->defines, token.string ); +#endif //DEFINEHASHING + if ( !define ) { + SourceError( source, "can't evaluate %s, not defined", token.string ); + return qfalse; + } //end if + if ( !PC_ExpandDefineIntoSource( source, define ) ) { + return qfalse; + } + } //end else + } //end if + //if the token is a number or a punctuation + else if ( token.type == TT_NUMBER || token.type == TT_PUNCTUATION ) { + if ( *token.string == '(' ) { + indent++; + } else if ( *token.string == ')' ) { + indent--; + } + if ( indent <= 0 ) { + break; + } + t = PC_CopyToken( &token ); + t->next = NULL; + if ( lasttoken ) { + lasttoken->next = t; + } else { firsttoken = t;} + lasttoken = t; + } //end else + else //can't evaluate the token + { + SourceError( source, "can't evaluate %s", token.string ); + return qfalse; + } //end else + } while ( PC_ReadSourceToken( source, &token ) ); + // + if ( !PC_EvaluateTokens( source, firsttoken, intvalue, floatvalue, integer ) ) { + return qfalse; + } + // +#ifdef DEBUG_EVAL + Log_Write( "$eval:" ); +#endif //DEBUG_EVAL + for ( t = firsttoken; t; t = nexttoken ) + { +#ifdef DEBUG_EVAL + Log_Write( " %s", t->string ); +#endif //DEBUG_EVAL + nexttoken = t->next; + PC_FreeToken( t ); + } //end for +#ifdef DEBUG_EVAL + if ( integer ) { + Log_Write( "$eval result: %d", *intvalue ); + } else { Log_Write( "$eval result: %f", *floatvalue );} +#endif //DEBUG_EVAL + // + return qtrue; +} //end of the function PC_DollarEvaluate +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_elif( source_t *source ) { + signed long int value; + int type, skip; + + PC_PopIndent( source, &type, &skip ); + if ( !type || type == INDENT_ELSE ) { + SourceError( source, "misplaced #elif" ); + return qfalse; + } //end if + if ( !PC_Evaluate( source, &value, NULL, qtrue ) ) { + return qfalse; + } + skip = ( value == 0 ); + PC_PushIndent( source, INDENT_ELIF, skip ); + return qtrue; +} //end of the function PC_Directive_elif +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_if( source_t *source ) { + signed long int value; + int skip; + + if ( !PC_Evaluate( source, &value, NULL, qtrue ) ) { + return qfalse; + } + skip = ( value == 0 ); + PC_PushIndent( source, INDENT_IF, skip ); + return qtrue; +} //end of the function PC_Directive +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_line( source_t *source ) { + SourceError( source, "#line directive not supported" ); + return qfalse; +} //end of the function PC_Directive_line +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_error( source_t *source ) { + token_t token; + + strcpy( token.string, "" ); + PC_ReadSourceToken( source, &token ); + SourceError( source, "#error directive: %s", token.string ); + return qfalse; +} //end of the function PC_Directive_error +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_pragma( source_t *source ) { + token_t token; + SourceWarning( source, "#pragma directive not supported" ); + while ( PC_ReadLine( source, &token ) ) ; + return qtrue; +} //end of the function PC_Directive_pragma +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void UnreadSignToken( source_t *source ) { + token_t token; + + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + strcpy( token.string, "-" ); + token.type = TT_PUNCTUATION; + token.subtype = P_SUB; + PC_UnreadSourceToken( source, &token ); +} //end of the function UnreadSignToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_eval( source_t *source ) { + signed long int value; + token_t token; + + if ( !PC_Evaluate( source, &value, NULL, qtrue ) ) { + return qfalse; + } + // + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf( token.string, "%d", abs( value ) ); + token.type = TT_NUMBER; + token.subtype = TT_INTEGER | TT_LONG | TT_DECIMAL; + PC_UnreadSourceToken( source, &token ); + if ( value < 0 ) { + UnreadSignToken( source ); + } + return qtrue; +} //end of the function PC_Directive_eval +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_Directive_evalfloat( source_t *source ) { + double value; + token_t token; + + if ( !PC_Evaluate( source, NULL, &value, qfalse ) ) { + return qfalse; + } + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf( token.string, "%1.2f", fabs( value ) ); + token.type = TT_NUMBER; + token.subtype = TT_FLOAT | TT_LONG | TT_DECIMAL; + PC_UnreadSourceToken( source, &token ); + if ( value < 0 ) { + UnreadSignToken( source ); + } + return qtrue; +} //end of the function PC_Directive_evalfloat +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +directive_t directives[20] = +{ + {"if", PC_Directive_if}, + {"ifdef", PC_Directive_ifdef}, + {"ifndef", PC_Directive_ifndef}, + {"elif", PC_Directive_elif}, + {"else", PC_Directive_else}, + {"endif", PC_Directive_endif}, + {"include", PC_Directive_include}, + {"define", PC_Directive_define}, + {"undef", PC_Directive_undef}, + {"line", PC_Directive_line}, + {"error", PC_Directive_error}, + {"pragma", PC_Directive_pragma}, + {"eval", PC_Directive_eval}, + {"evalfloat", PC_Directive_evalfloat}, + {NULL, NULL} +}; + +int PC_ReadDirective( source_t *source ) { + token_t token; + int i; + + //read the directive name + if ( !PC_ReadSourceToken( source, &token ) ) { + SourceError( source, "found # without name" ); + return qfalse; + } //end if + //directive name must be on the same line + if ( token.linescrossed > 0 ) { + PC_UnreadSourceToken( source, &token ); + SourceError( source, "found # at end of line" ); + return qfalse; + } //end if + //if if is a name + if ( token.type == TT_NAME ) { + //find the precompiler directive + for ( i = 0; directives[i].name; i++ ) + { + if ( !strcmp( directives[i].name, token.string ) ) { + return directives[i].func( source ); + } //end if + } //end for + } //end if + SourceError( source, "unknown precompiler directive %s", token.string ); + return qfalse; +} //end of the function PC_ReadDirective +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_DollarDirective_evalint( source_t *source ) { + signed long int value; + token_t token; + + if ( !PC_DollarEvaluate( source, &value, NULL, qtrue ) ) { + return qfalse; + } + // + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf( token.string, "%d", abs( value ) ); + token.type = TT_NUMBER; + token.subtype = TT_INTEGER | TT_LONG | TT_DECIMAL; +#ifdef NUMBERVALUE + token.intvalue = value; + token.floatvalue = value; +#endif //NUMBERVALUE + PC_UnreadSourceToken( source, &token ); + if ( value < 0 ) { + UnreadSignToken( source ); + } + return qtrue; +} //end of the function PC_DollarDirective_evalint +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_DollarDirective_evalfloat( source_t *source ) { + double value; + token_t token; + + if ( !PC_DollarEvaluate( source, NULL, &value, qfalse ) ) { + return qfalse; + } + token.line = source->scriptstack->line; + token.whitespace_p = source->scriptstack->script_p; + token.endwhitespace_p = source->scriptstack->script_p; + token.linescrossed = 0; + sprintf( token.string, "%1.2f", fabs( value ) ); + token.type = TT_NUMBER; + token.subtype = TT_FLOAT | TT_LONG | TT_DECIMAL; +#ifdef NUMBERVALUE + token.intvalue = (unsigned long) value; + token.floatvalue = value; +#endif //NUMBERVALUE + PC_UnreadSourceToken( source, &token ); + if ( value < 0 ) { + UnreadSignToken( source ); + } + return qtrue; +} //end of the function PC_DollarDirective_evalfloat +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +directive_t dollardirectives[20] = +{ + {"evalint", PC_DollarDirective_evalint}, + {"evalfloat", PC_DollarDirective_evalfloat}, + {NULL, NULL} +}; + +int PC_ReadDollarDirective( source_t *source ) { + token_t token; + int i; + + //read the directive name + if ( !PC_ReadSourceToken( source, &token ) ) { + SourceError( source, "found $ without name" ); + return qfalse; + } //end if + //directive name must be on the same line + if ( token.linescrossed > 0 ) { + PC_UnreadSourceToken( source, &token ); + SourceError( source, "found $ at end of line" ); + return qfalse; + } //end if + //if if is a name + if ( token.type == TT_NAME ) { + //find the precompiler directive + for ( i = 0; dollardirectives[i].name; i++ ) + { + if ( !strcmp( dollardirectives[i].name, token.string ) ) { + return dollardirectives[i].func( source ); + } //end if + } //end for + } //end if + PC_UnreadSourceToken( source, &token ); + SourceError( source, "unknown precompiler directive %s", token.string ); + return qfalse; +} //end of the function PC_ReadDollarDirective + +#ifdef QUAKEC +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int BuiltinFunction( source_t *source ) { + token_t token; + + if ( !PC_ReadSourceToken( source, &token ) ) { + return qfalse; + } + if ( token.type == TT_NUMBER ) { + PC_UnreadSourceToken( source, &token ); + return qtrue; + } //end if + else + { + PC_UnreadSourceToken( source, &token ); + return qfalse; + } //end else +} //end of the function BuiltinFunction +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int QuakeCMacro( source_t *source ) { + int i; + token_t token; + + if ( !PC_ReadSourceToken( source, &token ) ) { + return qtrue; + } + if ( token.type != TT_NAME ) { + PC_UnreadSourceToken( source, &token ); + return qtrue; + } //end if + //find the precompiler directive + for ( i = 0; dollardirectives[i].name; i++ ) + { + if ( !strcmp( dollardirectives[i].name, token.string ) ) { + PC_UnreadSourceToken( source, &token ); + return qfalse; + } //end if + } //end for + PC_UnreadSourceToken( source, &token ); + return qtrue; +} //end of the function QuakeCMacro +#endif //QUAKEC +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ReadToken( source_t *source, token_t *token ) { + define_t *define; + + while ( 1 ) + { + if ( !PC_ReadSourceToken( source, token ) ) { + return qfalse; + } + + //check for precompiler directives + if ( token->type == TT_PUNCTUATION && *token->string == '#' ) { +/* // Knightmare- re-enabled this +#ifdef SCREWUP // Ridah, skip all # directives + while ( PC_ReadLine( source, token ) ) ; + continue; +#endif // SCREWUP +*/ +#ifdef QUAKEC + if ( !BuiltinFunction( source ) ) +#endif //QUAKC + { + //read the precompiler directive + if ( !PC_ReadDirective( source ) ) { + // Knightmare- debug output + printf("PC_ReadToken: PC_ReadDirective failed on directive %s on line %i in file %s\n", token->string, token->line, source->filename); + return qfalse; + } + continue; + } //end if + } //end if + if ( token->type == TT_PUNCTUATION && *token->string == '$' ) { +#ifdef QUAKEC + if ( !QuakeCMacro( source ) ) +#endif //QUAKEC + { + //read the precompiler directive + if ( !PC_ReadDollarDirective( source ) ) { + // Knightmare- debug output + printf("PC_ReadToken: PC_ReadDollarDirective failed on directive %s on line %i in file %s\n", token->string, token->line, source->filename); + return qfalse; + } + continue; + } //end if + } //end if + //if skipping source because of conditional compilation + if ( source->skip ) { + continue; + } + //if the token is a name + if ( token->type == TT_NAME ) { + + //check if the name is a define macro +#if DEFINEHASHING + define = PC_FindHashedDefine( source->definehash, token->string ); +#else + define = PC_FindDefine( source->defines, token->string ); +#endif //DEFINEHASHING + + //if it is a define macro + if ( define ) { +#ifdef SCREWUP + // Knightmare- skip defines w/ parameters + // They're not relevant to extracting functions & global types + // And are hard to parse. + if (!define->builtin && define->numparms) { + continue; + } +#endif + //expand the defined macro + if ( !PC_ExpandDefineIntoSource( source, define ) ) { + // Knightmare- debug output + printf("PC_ReadToken: PC_ExpandDefineIntoSource failed on define %s on line %i in file %s\n", define->name, token->line, source->filename); + return qfalse; + } + continue; + } //end if + } //end if + //copy token for unreading + memcpy( &source->token, token, sizeof( token_t ) ); + //found a token + return qtrue; + } //end while +} //end of the function PC_ReadToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpectTokenString( source_t *source, char *string ) { + token_t token; + + if ( !PC_ReadToken( source, &token ) ) { + SourceError( source, "couldn't find expected %s", string ); + return qfalse; + } //end if + + if ( strcmp( token.string, string ) ) { + SourceError( source, "expected %s, found %s", string, token.string ); + return qfalse; + } //end if + return qtrue; +} //end of the function PC_ExpectTokenString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpectTokenType( source_t *source, int type, int subtype, token_t *token ) { + char str[MAX_TOKEN]; + + if ( !PC_ReadToken( source, token ) ) { + SourceError( source, "couldn't read expected token" ); + return qfalse; + } //end if + + if ( token->type != type ) { + strcpy( str, "" ); + if ( type == TT_STRING ) { + strcpy( str, "string" ); + } + if ( type == TT_LITERAL ) { + strcpy( str, "literal" ); + } + if ( type == TT_NUMBER ) { + strcpy( str, "number" ); + } + if ( type == TT_NAME ) { + strcpy( str, "name" ); + } + if ( type == TT_PUNCTUATION ) { + strcpy( str, "punctuation" ); + } + SourceError( source, "expected a %s, found %s", str, token->string ); + return qfalse; + } //end if + if ( token->type == TT_NUMBER ) { + if ( ( token->subtype & subtype ) != subtype ) { + if ( subtype & TT_DECIMAL ) { + strcpy( str, "decimal" ); + } + if ( subtype & TT_HEX ) { + strcpy( str, "hex" ); + } + if ( subtype & TT_OCTAL ) { + strcpy( str, "octal" ); + } + if ( subtype & TT_BINARY ) { + strcpy( str, "binary" ); + } + if ( subtype & TT_LONG ) { + strcat( str, " long" ); + } + if ( subtype & TT_UNSIGNED ) { + strcat( str, " unsigned" ); + } + if ( subtype & TT_FLOAT ) { + strcat( str, " float" ); + } + if ( subtype & TT_INTEGER ) { + strcat( str, " integer" ); + } + SourceError( source, "expected %s, found %s", str, token->string ); + return qfalse; + } //end if + } //end if + else if ( token->type == TT_PUNCTUATION ) { + if ( token->subtype != subtype ) { + SourceError( source, "found %s", token->string ); + return qfalse; + } //end if + } //end else if + return qtrue; +} //end of the function PC_ExpectTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_ExpectAnyToken( source_t *source, token_t *token ) { + if ( !PC_ReadToken( source, token ) ) { + SourceError( source, "couldn't read expected token" ); + return qfalse; + } //end if + else + { + return qtrue; + } //end else +} //end of the function PC_ExpectAnyToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_CheckTokenString( source_t *source, char *string ) { + token_t tok; + + if ( !PC_ReadToken( source, &tok ) ) { + return qfalse; + } + //if the token is available + if ( !strcmp( tok.string, string ) ) { + return qtrue; + } + // + PC_UnreadSourceToken( source, &tok ); + return qfalse; +} //end of the function PC_CheckTokenString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_CheckTokenType( source_t *source, int type, int subtype, token_t *token ) { + token_t tok; + + if ( !PC_ReadToken( source, &tok ) ) { + return qfalse; + } + //if the type matches + if ( tok.type == type && + ( tok.subtype & subtype ) == subtype ) { + memcpy( token, &tok, sizeof( token_t ) ); + return qtrue; + } //end if + // + PC_UnreadSourceToken( source, &tok ); + return qfalse; +} //end of the function PC_CheckTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PC_SkipUntilString( source_t *source, char *string ) { + token_t token; + + while ( PC_ReadToken( source, &token ) ) + { + if ( !strcmp( token.string, string ) ) { + return qtrue; + } + } //end while + return qfalse; +} //end of the function PC_SkipUntilString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_UnreadLastToken( source_t *source ) { + PC_UnreadSourceToken( source, &source->token ); +} //end of the function PC_UnreadLastToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_UnreadToken( source_t *source, token_t *token ) { + PC_UnreadSourceToken( source, token ); +} //end of the function PC_UnreadToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_SetIncludePath( source_t *source, char *path ) { + strncpy( source->includepath, path, _MAX_PATH ); + //add trailing path seperator + if ( source->includepath[strlen( source->includepath ) - 1] != '\\' && + source->includepath[strlen( source->includepath ) - 1] != '/' ) { + strcat( source->includepath, PATHSEPERATOR_STR ); + } //end if +} //end of the function PC_SetIncludePath +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PC_SetPunctuations( source_t *source, punctuation_t *p ) { + source->punctuations = p; +} //end of the function PC_SetPunctuations +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +source_t *LoadSourceFile( char *filename ) { + source_t *source; + script_t *script; + + PC_InitTokenHeap(); + + script = LoadScriptFile( filename ); + if ( !script ) { + return NULL; + } + + script->next = NULL; + + source = (source_t *) GetMemory( sizeof( source_t ) ); + memset( source, 0, sizeof( source_t ) ); + + strncpy( source->filename, filename, _MAX_PATH ); + source->scriptstack = script; + source->tokens = NULL; + source->defines = NULL; + source->indentstack = NULL; + source->skip = 0; + +#if DEFINEHASHING + source->definehash = GetClearedMemory( DEFINEHASHSIZE * sizeof( define_t * ) ); +#endif //DEFINEHASHING + PC_AddGlobalDefinesToSource( source ); + return source; +} //end of the function LoadSourceFile +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +source_t *LoadSourceMemory( char *ptr, int length, char *name ) { + source_t *source; + script_t *script; + + PC_InitTokenHeap(); + + script = LoadScriptMemory( ptr, length, name ); + if ( !script ) { + return NULL; + } + script->next = NULL; + + source = (source_t *) GetMemory( sizeof( source_t ) ); + memset( source, 0, sizeof( source_t ) ); + + strncpy( source->filename, name, _MAX_PATH ); + source->scriptstack = script; + source->tokens = NULL; + source->defines = NULL; + source->indentstack = NULL; + source->skip = 0; + +#if DEFINEHASHING + source->definehash = GetClearedMemory( DEFINEHASHSIZE * sizeof( define_t * ) ); +#endif //DEFINEHASHING + PC_AddGlobalDefinesToSource( source ); + return source; +} //end of the function LoadSourceMemory +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void FreeSource( source_t *source ) { + script_t *script; + token_t *token; + define_t *define; + indent_t *indent; + int i; + + //PC_PrintDefineHashTable(source->definehash); + //free all the scripts + while ( source->scriptstack ) + { + script = source->scriptstack; + source->scriptstack = source->scriptstack->next; + FreeScript( script ); + } //end for + //free all the tokens + while ( source->tokens ) + { + token = source->tokens; + source->tokens = source->tokens->next; + PC_FreeToken( token ); + } //end for +#if DEFINEHASHING + for ( i = 0; i < DEFINEHASHSIZE; i++ ) + { + while ( source->definehash[i] ) + { + define = source->definehash[i]; + source->definehash[i] = source->definehash[i]->hashnext; + PC_FreeDefine( define ); + } //end while + } //end for +#else //DEFINEHASHING + //free all defines + while ( source->defines ) + { + define = source->defines; + source->defines = source->defines->next; + PC_FreeDefine( define ); + } //end for +#endif //DEFINEHASHING + //free all indents + while ( source->indentstack ) + { + indent = source->indentstack; + source->indentstack = source->indentstack->next; + FreeMemory( indent ); + } //end for +#if DEFINEHASHING + // + if ( source->definehash ) { + FreeMemory( source->definehash ); + } +#endif //DEFINEHASHING + //free the source itself + FreeMemory( source ); +} //end of the function FreeSource + diff --git a/l_precomp.h b/l_precomp.h new file mode 100644 index 0000000..b2f4c13 --- /dev/null +++ b/l_precomp.h @@ -0,0 +1,162 @@ +/* +=========================================================================== + +Return to Castle Wolfenstein single player GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). + +RTCW SP 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 3 of the License, or +(at your option) any later version. + +RTCW SP 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 RTCW SP Source Code. If not, see . + +In addition, the RTCW SP Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the RTCW SP +Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, +you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_precomp.h + * + * desc: pre compiler + * + * + *****************************************************************************/ + +#ifndef _MAX_PATH + #define MAX_PATH MAX_QPATH +#endif + +#ifndef PATH_SEPERATORSTR + #if defined( WIN32 ) | defined( _WIN32 ) | defined( __NT__ ) | defined( __WINDOWS__ ) | defined( __WINDOWS_386__ ) + #define PATHSEPERATOR_STR "\\" + #else + #define PATHSEPERATOR_STR "/" + #endif +#endif +#ifndef PATH_SEPERATORCHAR + #if defined( WIN32 ) | defined( _WIN32 ) | defined( __NT__ ) | defined( __WINDOWS__ ) | defined( __WINDOWS_386__ ) + #define PATHSEPERATOR_CHAR '\\' + #else + #define PATHSEPERATOR_CHAR '/' + #endif +#endif + + +#define DEFINE_FIXED 0x0001 + +#define BUILTIN_LINE 1 +#define BUILTIN_FILE 2 +#define BUILTIN_DATE 3 +#define BUILTIN_TIME 4 +#define BUILTIN_STDC 5 + +#define INDENT_IF 0x0001 +#define INDENT_ELSE 0x0002 +#define INDENT_ELIF 0x0004 +#define INDENT_IFDEF 0x0008 +#define INDENT_IFNDEF 0x0010 + +//macro definitions +typedef struct define_s +{ + char *name; //define name + int flags; //define flags + int builtin; // > 0 if builtin define + int numparms; //number of define parameters + token_t *parms; //define parameters + token_t *tokens; //macro tokens (possibly containing parm tokens) + struct define_s *next; //next defined macro in a list + struct define_s *hashnext; //next define in the hash chain +} define_t; + +//indents +//used for conditional compilation directives: +//#if, #else, #elif, #ifdef, #ifndef +typedef struct indent_s +{ + int type; //indent type + int skip; //true if skipping current indent + script_t *script; //script the indent was in + struct indent_s *next; //next indent on the indent stack +} indent_t; + +//source file +typedef struct source_s +{ + char filename[_MAX_PATH]; //file name of the script + char includepath[_MAX_PATH]; //path to include files + punctuation_t *punctuations; //punctuations to use + script_t *scriptstack; //stack with scripts of the source + token_t *tokens; //tokens to read first + define_t *defines; //list with macro definitions + define_t **definehash; //hash chain with defines + indent_t *indentstack; //stack with indents + int skip; // > 0 if skipping conditional code + token_t token; //last read token +} source_t; + + +//read a token from the source +int PC_ReadToken( source_t *source, token_t *token ); +//expect a certain token +int PC_ExpectTokenString( source_t *source, char *string ); +//expect a certain token type +int PC_ExpectTokenType( source_t *source, int type, int subtype, token_t *token ); +//expect a token +int PC_ExpectAnyToken( source_t *source, token_t *token ); +//returns true when the token is available +int PC_CheckTokenString( source_t *source, char *string ); +//returns true an reads the token when a token with the given type is available +int PC_CheckTokenType( source_t *source, int type, int subtype, token_t *token ); +//skip tokens until the given token string is read +int PC_SkipUntilString( source_t *source, char *string ); +//unread the last token read from the script +void PC_UnreadLastToken( source_t *source ); +//unread the given token +void PC_UnreadToken( source_t *source, token_t *token ); +//read a token only if on the same line, lines are concatenated with a slash +int PC_ReadLine( source_t *source, token_t *token ); +//returns true if there was a white space in front of the token +int PC_WhiteSpaceBeforeToken( token_t *token ); +//add a define to the source +int PC_AddDefine( source_t *source, char *string ); +//add a globals define that will be added to all opened sources +int PC_AddGlobalDefine( char *string ); +//remove the given global define +int PC_RemoveGlobalDefine( char *name ); +//remove all globals defines +void PC_RemoveAllGlobalDefines( void ); +//add builtin defines +void PC_AddBuiltinDefines( source_t *source ); +//set the source include path +void PC_SetIncludePath( source_t *source, char *path ); +//set the punction set +void PC_SetPunctuations( source_t *source, punctuation_t *p ); +//load a source file +source_t *LoadSourceFile( char *filename ); +//load a source from memory +source_t *LoadSourceMemory( char *ptr, int length, char *name ); +//free the given source +void FreeSource( source_t *source ); +//print a source error +void QDECL SourceError( source_t *source, char *str, ... ); +//print a source warning +void QDECL SourceWarning( source_t *source, char *str, ... ); + diff --git a/l_script.c b/l_script.c new file mode 100644 index 0000000..eaddc6b --- /dev/null +++ b/l_script.c @@ -0,0 +1,1423 @@ +/* +=========================================================================== + +Return to Castle Wolfenstein single player GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). + +RTCW SP 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 3 of the License, or +(at your option) any later version. + +RTCW SP 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 RTCW SP Source Code. If not, see . + +In addition, the RTCW SP Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the RTCW SP +Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, +you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_script.c + * + * desc: lexicographical parser + * + * + *****************************************************************************/ + +//#define SCREWUP +//#define BOTLIB +//#define MEQCC +//#define BSPC + +#ifdef SCREWUP +#include +#include +#include +#include +#include +#include "l_memory.h" +#include "l_script.h" + +typedef enum {qfalse, qtrue} qboolean; + +#endif //SCREWUP + +#ifdef BOTLIB +//include files for usage in the bot library +#include "../game/q_shared.h" +#include "botlib.h" +#include "be_interface.h" +#include "l_script.h" +#include "l_memory.h" +#include "l_log.h" +#include "l_libvar.h" +#endif //BOTLIB + +#ifdef MEQCC +//include files for usage in MrElusive's QuakeC Compiler +#include "qcc.h" +#include "l_script.h" +#include "l_memory.h" +#include "l_log.h" + +#define qtrue true +#define qfalse false +#endif //MEQCC + +#ifdef BSPC +//include files for usage in the BSP Converter +#include "../bspc/qbsp.h" +#include "../bspc/l_log.h" +#include "../bspc/l_mem.h" + +#define qtrue true +#define qfalse false +#endif //BSPC + + +#define PUNCTABLE + +//longer punctuations first +punctuation_t default_punctuations[] = +{ + //binary operators + {">>=",P_RSHIFT_ASSIGN, NULL}, + {"<<=",P_LSHIFT_ASSIGN, NULL}, + // + {"...",P_PARMS, NULL}, + //define merge operator + {"##",P_PRECOMPMERGE, NULL}, + //logic operators + {"&&",P_LOGIC_AND, NULL}, + {"||",P_LOGIC_OR, NULL}, + {">=",P_LOGIC_GEQ, NULL}, + {"<=",P_LOGIC_LEQ, NULL}, + {"==",P_LOGIC_EQ, NULL}, + {"!=",P_LOGIC_UNEQ, NULL}, + //arithmatic operators + {"*=",P_MUL_ASSIGN, NULL}, + {"/=",P_DIV_ASSIGN, NULL}, + {"%=",P_MOD_ASSIGN, NULL}, + {"+=",P_ADD_ASSIGN, NULL}, + {"-=",P_SUB_ASSIGN, NULL}, + {"++",P_INC, NULL}, + {"--",P_DEC, NULL}, + //binary operators + {"&=",P_BIN_AND_ASSIGN, NULL}, + {"|=",P_BIN_OR_ASSIGN, NULL}, + {"^=",P_BIN_XOR_ASSIGN, NULL}, + {">>",P_RSHIFT, NULL}, + {"<<",P_LSHIFT, NULL}, + //reference operators + {"->",P_POINTERREF, NULL}, + //C++ + {"::",P_CPP1, NULL}, + {".*",P_CPP2, NULL}, + //arithmatic operators + {"*",P_MUL, NULL}, + {"/",P_DIV, NULL}, + {"%",P_MOD, NULL}, + {"+",P_ADD, NULL}, + {"-",P_SUB, NULL}, + {"=",P_ASSIGN, NULL}, + //binary operators + {"&",P_BIN_AND, NULL}, + {"|",P_BIN_OR, NULL}, + {"^",P_BIN_XOR, NULL}, + {"~",P_BIN_NOT, NULL}, + //logic operators + {"!",P_LOGIC_NOT, NULL}, + {">",P_LOGIC_GREATER, NULL}, + {"<",P_LOGIC_LESS, NULL}, + //reference operator + {".",P_REF, NULL}, + //seperators + {",",P_COMMA, NULL}, + {";",P_SEMICOLON, NULL}, + //label indication + {":",P_COLON, NULL}, + //if statement + {"?",P_QUESTIONMARK, NULL}, + //embracements + {"(",P_PARENTHESESOPEN, NULL}, + {")",P_PARENTHESESCLOSE, NULL}, + {"{",P_BRACEOPEN, NULL}, + {"}",P_BRACECLOSE, NULL}, + {"[",P_SQBRACKETOPEN, NULL}, + {"]",P_SQBRACKETCLOSE, NULL}, + // + {"\\",P_BACKSLASH, NULL}, + //precompiler operator + {"#",P_PRECOMP, NULL}, +#ifdef DOLLAR + {"$",P_DOLLAR, NULL}, +#endif //DOLLAR + {NULL, 0} +}; + +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void PS_CreatePunctuationTable( script_t *script, punctuation_t *punctuations ) { + int i; + punctuation_t *p, *lastp, *newp; + + //get memory for the table + if ( !script->punctuationtable ) { + script->punctuationtable = (punctuation_t **) + GetMemory( 256 * sizeof( punctuation_t * ) ); + } + memset( script->punctuationtable, 0, 256 * sizeof( punctuation_t * ) ); + //add the punctuations in the list to the punctuation table + for ( i = 0; punctuations[i].p; i++ ) + { + newp = &punctuations[i]; + lastp = NULL; + //sort the punctuations in this table entry on length (longer punctuations first) + for ( p = script->punctuationtable[(unsigned int) newp->p[0]]; p; p = p->next ) + { + if ( strlen( p->p ) < strlen( newp->p ) ) { + newp->next = p; + if ( lastp ) { + lastp->next = newp; + } else { script->punctuationtable[(unsigned int) newp->p[0]] = newp;} + break; + } //end if + lastp = p; + } //end for + if ( !p ) { + newp->next = NULL; + if ( lastp ) { + lastp->next = newp; + } else { script->punctuationtable[(unsigned int) newp->p[0]] = newp;} + } //end if + } //end for +} //end of the function PS_CreatePunctuationTable +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +char *PunctuationFromNum( script_t *script, int num ) { + int i; + + for ( i = 0; script->punctuations[i].p; i++ ) + { + if ( script->punctuations[i].n == num ) { + return script->punctuations[i].p; + } + } //end for + return "unkown punctuation"; +} //end of the function PunctuationFromNum +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL ScriptError( script_t *script, char *str, ... ) { + char text[1024]; + va_list ap; + + if ( script->flags & SCFL_NOERRORS ) { + return; + } + + va_start( ap, str ); + vsprintf( text, str, ap ); + va_end( ap ); +#ifdef BOTLIB + botimport.Print( PRT_ERROR, "file %s, line %d: %s\n", script->filename, script->line, text ); +#endif //BOTLIB +#ifdef MEQCC + printf( "error: file %s, line %d: %s\n", script->filename, script->line, text ); +#endif //MEQCC +#ifdef BSPC + Log_Print( "error: file %s, line %d: %s\n", script->filename, script->line, text ); +#endif //BSPC +} //end of the function ScriptError +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void QDECL ScriptWarning( script_t *script, char *str, ... ) { + char text[1024]; + va_list ap; + + if ( script->flags & SCFL_NOWARNINGS ) { + return; + } + + va_start( ap, str ); + vsprintf( text, str, ap ); + va_end( ap ); +#ifdef BOTLIB + botimport.Print( PRT_WARNING, "file %s, line %d: %s\n", script->filename, script->line, text ); +#endif //BOTLIB +#ifdef MEQCC + printf( "warning: file %s, line %d: %s\n", script->filename, script->line, text ); +#endif //MEQCC +#ifdef BSPC + Log_Print( "warning: file %s, line %d: %s\n", script->filename, script->line, text ); +#endif //BSPC +} //end of the function ScriptWarning +//=========================================================================== +// +// Parameter: - +// Returns: - +// Changes Globals: - +//=========================================================================== +void SetScriptPunctuations( script_t *script, punctuation_t *p ) { +#ifdef PUNCTABLE + if ( p ) { + PS_CreatePunctuationTable( script, p ); + } else { PS_CreatePunctuationTable( script, default_punctuations );} +#endif //PUNCTABLE + if ( p ) { + script->punctuations = p; + } else { script->punctuations = default_punctuations;} +} //end of the function SetScriptPunctuations +//============================================================================ +// Reads spaces, tabs, C-like comments etc. +// When a newline character is found the scripts line counter is increased. +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadWhiteSpace( script_t *script ) { + while ( 1 ) + { + //skip white space + while ( *script->script_p <= ' ' ) + { + if ( !*script->script_p ) { + return 0; + } + if ( *script->script_p == '\n' ) { + script->line++; + } + script->script_p++; + } //end while + //skip comments + if ( *script->script_p == '/' ) { + //comments // + if ( *( script->script_p + 1 ) == '/' ) { + script->script_p++; + do + { + script->script_p++; + if ( !*script->script_p ) { + return 0; + } + } //end do + while ( *script->script_p != '\n' ); + script->line++; + script->script_p++; + if ( !*script->script_p ) { + return 0; + } + continue; + } //end if + //comments /* */ + else if ( *( script->script_p + 1 ) == '*' ) { + script->script_p++; + do + { + script->script_p++; + if ( !*script->script_p ) { + return 0; + } + if ( *script->script_p == '\n' ) { + script->line++; + } + } //end do + while ( !( *script->script_p == '*' && *( script->script_p + 1 ) == '/' ) ); + script->script_p++; + if ( !*script->script_p ) { + return 0; + } + script->script_p++; + if ( !*script->script_p ) { + return 0; + } + continue; + } //end if + } //end if + break; + } //end while + return 1; +} //end of the function PS_ReadWhiteSpace +//============================================================================ +// Reads an escape character. +// +// Parameter: script : script to read from +// ch : place to store the read escape character +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadEscapeCharacter( script_t *script, char *ch ) { + int c, val, i; + + //step over the leading '\\' + script->script_p++; + //determine the escape character + switch ( *script->script_p ) + { + case '\\': c = '\\'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'a': c = '\a'; break; + case '\'': c = '\''; break; + case '\"': c = '\"'; break; + case '\?': c = '\?'; break; + case 'x': + { + script->script_p++; + for ( i = 0, val = 0; ; i++, script->script_p++ ) + { + c = *script->script_p; + if ( c >= '0' && c <= '9' ) { + c = c - '0'; + } else if ( c >= 'A' && c <= 'Z' ) { + c = c - 'A' + 10; + } else if ( c >= 'a' && c <= 'z' ) { + c = c - 'a' + 10; + } else { break;} + val = ( val << 4 ) + c; + } //end for + script->script_p--; + if ( val > 0xFF ) { + ScriptWarning( script, "too large value in escape character" ); + val = 0xFF; + } //end if + c = val; + break; + } //end case + default: //NOTE: decimal ASCII code, NOT octal + { + if ( *script->script_p < '0' || *script->script_p > '9' ) { + ScriptError( script, "unknown escape char" ); + } + for ( i = 0, val = 0; ; i++, script->script_p++ ) + { + c = *script->script_p; + if ( c >= '0' && c <= '9' ) { + c = c - '0'; + } else { break;} + val = val * 10 + c; + } //end for + script->script_p--; + if ( val > 0xFF ) { + ScriptWarning( script, "too large value in escape character" ); + val = 0xFF; + } //end if + c = val; + break; + } //end default + } //end switch + //step over the escape character or the last digit of the number + script->script_p++; + //store the escape character + *ch = c; + //succesfully read escape character + return 1; +} //end of the function PS_ReadEscapeCharacter +//============================================================================ +// Reads C-like string. Escape characters are interpretted. +// Quotes are included with the string. +// Reads two strings with a white space between them as one string. +// +// Parameter: script : script to read from +// token : buffer to store the string +// Returns: qtrue when a string was read succesfully +// Changes Globals: - +//============================================================================ +int PS_ReadString( script_t *script, token_t *token, int quote ) { + int len, tmpline; + char *tmpscript_p; + + if ( quote == '\"' ) { + token->type = TT_STRING; + } else { token->type = TT_LITERAL;} + + len = 0; + //leading quote + token->string[len++] = *script->script_p++; + // + while ( 1 ) + { + //minus 2 because trailing double quote and zero have to be appended + if ( len >= MAX_TOKEN - 2 ) { + ScriptError( script, "string longer than MAX_TOKEN = %d", MAX_TOKEN ); + return 0; + } //end if + //if there is an escape character and + //if escape characters inside a string are allowed + if ( *script->script_p == '\\' && !( script->flags & SCFL_NOSTRINGESCAPECHARS ) ) { + if ( !PS_ReadEscapeCharacter( script, &token->string[len] ) ) { + token->string[len] = 0; + return 0; + } //end if + len++; + } //end if + //if a trailing quote + else if ( *script->script_p == quote ) { + //step over the double quote + script->script_p++; + //if white spaces in a string are not allowed + if ( script->flags & SCFL_NOSTRINGWHITESPACES ) { + break; + } + // + tmpscript_p = script->script_p; + tmpline = script->line; + //read unusefull stuff between possible two following strings + if ( !PS_ReadWhiteSpace( script ) ) { + script->script_p = tmpscript_p; + script->line = tmpline; + break; + } //end if + //if there's no leading double qoute + if ( *script->script_p != quote ) { + script->script_p = tmpscript_p; + script->line = tmpline; + break; + } //end if + //step over the new leading double quote + script->script_p++; + } //end if + else + { + if ( *script->script_p == '\0' ) { + token->string[len] = 0; + ScriptError( script, "missing trailing quote" ); + return 0; + } //end if + if ( *script->script_p == '\n' ) { + token->string[len] = 0; + ScriptError( script, "newline inside string %s", token->string ); + return 0; + } //end if + token->string[len++] = *script->script_p++; + } //end else + } //end while + //trailing quote + token->string[len++] = quote; + //end string with a zero + token->string[len] = '\0'; + //the sub type is the length of the string + token->subtype = len; + return 1; +} //end of the function PS_ReadString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadName( script_t *script, token_t *token ) { + int len = 0; + char c; + + token->type = TT_NAME; + do + { + token->string[len++] = *script->script_p++; + if ( len >= MAX_TOKEN ) { + ScriptError( script, "name longer than MAX_TOKEN = %d", MAX_TOKEN ); + return 0; + } //end if + c = *script->script_p; + } while ( ( c >= 'a' && c <= 'z' ) || + ( c >= 'A' && c <= 'Z' ) || + ( c >= '0' && c <= '9' ) || + c == '_' ); + token->string[len] = '\0'; + //the sub type is the length of the name + token->subtype = len; + return 1; +} //end of the function PS_ReadName +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void NumberValue( char *string, int subtype, unsigned long int *intvalue, + long double *floatvalue ) { + unsigned long int dotfound = 0; + + *intvalue = 0; + *floatvalue = 0; + //floating point number + if ( subtype & TT_FLOAT ) { + while ( *string ) + { + if ( *string == '.' ) { + if ( dotfound ) { + return; + } + dotfound = 10; + string++; + } //end if + if ( dotfound ) { + *floatvalue = *floatvalue + ( long double )( *string - '0' ) / + (long double) dotfound; + dotfound *= 10; + } //end if + else + { + *floatvalue = *floatvalue * 10.0 + ( long double )( *string - '0' ); + } //end else + string++; + } //end while + *intvalue = (unsigned long) *floatvalue; + } //end if + else if ( subtype & TT_DECIMAL ) { + while ( *string ) *intvalue = *intvalue * 10 + ( *string++ - '0' ); + *floatvalue = *intvalue; + } //end else if + else if ( subtype & TT_HEX ) { + //step over the leading 0x or 0X + string += 2; + while ( *string ) + { + *intvalue <<= 4; + if ( *string >= 'a' && *string <= 'f' ) { + *intvalue += *string - 'a' + 10; + } else if ( *string >= 'A' && *string <= 'F' ) { + *intvalue += *string - 'A' + 10; + } else { *intvalue += *string - '0';} + string++; + } //end while + *floatvalue = *intvalue; + } //end else if + else if ( subtype & TT_OCTAL ) { + //step over the first zero + string += 1; + while ( *string ) *intvalue = ( *intvalue << 3 ) + ( *string++ - '0' ); + *floatvalue = *intvalue; + } //end else if + else if ( subtype & TT_BINARY ) { + //step over the leading 0b or 0B + string += 2; + while ( *string ) *intvalue = ( *intvalue << 1 ) + ( *string++ - '0' ); + *floatvalue = *intvalue; + } //end else if +} //end of the function NumberValue +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadNumber( script_t *script, token_t *token ) { + int len = 0, i; + int octal, dot; + char c; +// unsigned long int intvalue = 0; +// long double floatvalue = 0; + + token->type = TT_NUMBER; + //check for a hexadecimal number + if ( *script->script_p == '0' && + ( *( script->script_p + 1 ) == 'x' || + *( script->script_p + 1 ) == 'X' ) ) { + token->string[len++] = *script->script_p++; + token->string[len++] = *script->script_p++; + c = *script->script_p; + //hexadecimal + while ( ( c >= '0' && c <= '9' ) || + ( c >= 'a' && c <= 'f' ) || + ( c >= 'A' && c <= 'A' ) ) + { + token->string[len++] = *script->script_p++; + if ( len >= MAX_TOKEN ) { + ScriptError( script, "hexadecimal number longer than MAX_TOKEN = %d", MAX_TOKEN ); + return 0; + } //end if + c = *script->script_p; + } //end while + token->subtype |= TT_HEX; + } //end if +#ifdef BINARYNUMBERS + //check for a binary number + else if ( *script->script_p == '0' && + ( *( script->script_p + 1 ) == 'b' || + *( script->script_p + 1 ) == 'B' ) ) { + token->string[len++] = *script->script_p++; + token->string[len++] = *script->script_p++; + c = *script->script_p; + //hexadecimal + while ( c == '0' || c == '1' ) + { + token->string[len++] = *script->script_p++; + if ( len >= MAX_TOKEN ) { + ScriptError( script, "binary number longer than MAX_TOKEN = %d", MAX_TOKEN ); + return 0; + } //end if + c = *script->script_p; + } //end while + token->subtype |= TT_BINARY; + } //end if +#endif //BINARYNUMBERS + else //decimal or octal integer or floating point number + { + octal = qfalse; + dot = qfalse; + if ( *script->script_p == '0' ) { + octal = qtrue; + } + while ( 1 ) + { + token->string[len++] = *script->script_p++; + if ( len >= MAX_TOKEN ) { + ScriptError( script, "number longer than MAX_TOKEN = %d", MAX_TOKEN ); + return 0; + } //end if + c = *script->script_p; + if ( c == '.' ) { + dot = qtrue; + } else if ( c == '8' || c == '9' ) { + octal = qfalse; + } else if ( c < '0' || c > '9' ) { + break; + } + } //end while + if ( octal ) { + token->subtype |= TT_OCTAL; + } else { token->subtype |= TT_DECIMAL;} + if ( dot ) { + token->subtype |= TT_FLOAT; + } + } //end else + for ( i = 0; i < 2; i++ ) + { + c = *script->script_p; + //check for a LONG number + if ( c == 'l' || c == 'L' && + !( token->subtype & TT_LONG ) ) { + script->script_p++; + token->subtype |= TT_LONG; + } //end if + //check for an UNSIGNED number + else if ( c == 'u' || c == 'U' && + !( token->subtype & ( TT_UNSIGNED | TT_FLOAT ) ) ) { + script->script_p++; + token->subtype |= TT_UNSIGNED; + } //end if + } //end for + token->string[len] = '\0'; +#ifdef NUMBERVALUE + NumberValue( token->string, token->subtype, &token->intvalue, &token->floatvalue ); +#endif //NUMBERVALUE + if ( !( token->subtype & TT_FLOAT ) ) { + token->subtype |= TT_INTEGER; + } + return 1; +} //end of the function PS_ReadNumber +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadLiteral( script_t *script, token_t *token ) { + token->type = TT_LITERAL; + //first quote + token->string[0] = *script->script_p++; + //check for end of file + if ( !*script->script_p ) { + ScriptError( script, "end of file before trailing \'" ); + return 0; + } //end if + //if it is an escape character + if ( *script->script_p == '\\' ) { + if ( !PS_ReadEscapeCharacter( script, &token->string[1] ) ) { + return 0; + } + } //end if + else + { + token->string[1] = *script->script_p++; + } //end else + //check for trailing quote + if ( *script->script_p != '\'' ) { + ScriptWarning( script, "too many characters in literal, ignored" ); + while ( *script->script_p && + *script->script_p != '\'' && + *script->script_p != '\n' ) + { + script->script_p++; + } //end while + if ( *script->script_p == '\'' ) { + script->script_p++; + } + } //end if + //store the trailing quote + token->string[2] = *script->script_p++; + //store trailing zero to end the string + token->string[3] = '\0'; + //the sub type is the integer literal value + token->subtype = token->string[1]; + // + return 1; +} //end of the function PS_ReadLiteral +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadPunctuation( script_t *script, token_t *token ) { + int len; + char *p; + punctuation_t *punc; + +#ifdef PUNCTABLE + for ( punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next ) + { +#else + int i; + + for ( i = 0; script->punctuations[i].p; i++ ) + { + punc = &script->punctuations[i]; +#endif //PUNCTABLE + p = punc->p; + len = strlen( p ); + //if the script contains at least as much characters as the punctuation + if ( script->script_p + len <= script->end_p ) { + //if the script contains the punctuation + if ( !strncmp( script->script_p, p, len ) ) { + strncpy( token->string, p, MAX_TOKEN ); + script->script_p += len; + token->type = TT_PUNCTUATION; + //sub type is the number of the punctuation + token->subtype = punc->n; + return 1; + } //end if + } //end if + } //end for + return 0; +} //end of the function PS_ReadPunctuation +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadPrimitive( script_t *script, token_t *token ) { + int len; + + len = 0; + while ( *script->script_p > ' ' && *script->script_p != ';' ) + { + if ( len >= MAX_TOKEN ) { + ScriptError( script, "primitive token longer than MAX_TOKEN = %d", MAX_TOKEN ); + return 0; + } //end if + token->string[len++] = *script->script_p++; + } //end while + token->string[len] = 0; + //copy the token into the script structure + memcpy( &script->token, token, sizeof( token_t ) ); + //primitive reading successfull + return 1; +} //end of the function PS_ReadPrimitive +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ReadToken( script_t *script, token_t *token ) { + //if there is a token available (from UnreadToken) + if ( script->tokenavailable ) { + script->tokenavailable = 0; + memcpy( token, &script->token, sizeof( token_t ) ); + return 1; + } //end if + //save script pointer + script->lastscript_p = script->script_p; + //save line counter + script->lastline = script->line; + //clear the token stuff + memset( token, 0, sizeof( token_t ) ); + //start of the white space + script->whitespace_p = script->script_p; + token->whitespace_p = script->script_p; + //read unusefull stuff + if ( !PS_ReadWhiteSpace( script ) ) { + return 0; + } + //end of the white space + script->endwhitespace_p = script->script_p; + token->endwhitespace_p = script->script_p; + //line the token is on + token->line = script->line; + //number of lines crossed before token + token->linescrossed = script->line - script->lastline; + //if there is a leading double quote + if ( *script->script_p == '\"' ) { + if ( !PS_ReadString( script, token, '\"' ) ) { + return 0; + } + } //end if + //if an literal + else if ( *script->script_p == '\'' ) { + //if (!PS_ReadLiteral(script, token)) return 0; + if ( !PS_ReadString( script, token, '\'' ) ) { + return 0; + } + } //end if + //if there is a number + else if ( ( *script->script_p >= '0' && *script->script_p <= '9' ) || + ( *script->script_p == '.' && + ( *( script->script_p + 1 ) >= '0' && *( script->script_p + 1 ) <= '9' ) ) ) { + if ( !PS_ReadNumber( script, token ) ) { + return 0; + } + } //end if + //if this is a primitive script + else if ( script->flags & SCFL_PRIMITIVE ) { + return PS_ReadPrimitive( script, token ); + } //end else if + //if there is a name + else if ( ( *script->script_p >= 'a' && *script->script_p <= 'z' ) || + ( *script->script_p >= 'A' && *script->script_p <= 'Z' ) || + *script->script_p == '_' ) { + if ( !PS_ReadName( script, token ) ) { + return 0; + } + } //end if + //check for punctuations + else if ( !PS_ReadPunctuation( script, token ) ) { + ScriptError( script, "can't read token" ); + return 0; + } //end if + //copy the token into the script structure + memcpy( &script->token, token, sizeof( token_t ) ); + //succesfully read a token + return 1; +} //end of the function PS_ReadToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ExpectTokenString( script_t *script, char *string ) { + token_t token; + + if ( !PS_ReadToken( script, &token ) ) { + ScriptError( script, "couldn't find expected %s", string ); + return 0; + } //end if + + if ( strcmp( token.string, string ) ) { + ScriptError( script, "expected %s, found %s", string, token.string ); + return 0; + } //end if + return 1; +} //end of the function PS_ExpectToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ExpectTokenType( script_t *script, int type, int subtype, token_t *token ) { + char str[MAX_TOKEN]; + + if ( !PS_ReadToken( script, token ) ) { + ScriptError( script, "couldn't read expected token" ); + return 0; + } //end if + + if ( token->type != type ) { + if ( type == TT_STRING ) { + strcpy( str, "string" ); + } + if ( type == TT_LITERAL ) { + strcpy( str, "literal" ); + } + if ( type == TT_NUMBER ) { + strcpy( str, "number" ); + } + if ( type == TT_NAME ) { + strcpy( str, "name" ); + } + if ( type == TT_PUNCTUATION ) { + strcpy( str, "punctuation" ); + } + ScriptError( script, "expected a %s, found %s", str, token->string ); + return 0; + } //end if + if ( token->type == TT_NUMBER ) { + if ( ( token->subtype & subtype ) != subtype ) { + if ( subtype & TT_DECIMAL ) { + strcpy( str, "decimal" ); + } + if ( subtype & TT_HEX ) { + strcpy( str, "hex" ); + } + if ( subtype & TT_OCTAL ) { + strcpy( str, "octal" ); + } + if ( subtype & TT_BINARY ) { + strcpy( str, "binary" ); + } + if ( subtype & TT_LONG ) { + strcat( str, " long" ); + } + if ( subtype & TT_UNSIGNED ) { + strcat( str, " unsigned" ); + } + if ( subtype & TT_FLOAT ) { + strcat( str, " float" ); + } + if ( subtype & TT_INTEGER ) { + strcat( str, " integer" ); + } + ScriptError( script, "expected %s, found %s", str, token->string ); + return 0; + } //end if + } //end if + else if ( token->type == TT_PUNCTUATION ) { + if ( subtype < 0 ) { + ScriptError( script, "BUG: wrong punctuation subtype" ); + return 0; + } //end if + if ( token->subtype != subtype ) { + ScriptError( script, "expected %s, found %s", + script->punctuations[subtype], token->string ); + return 0; + } //end if + } //end else if + return 1; +} //end of the function PS_ExpectTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_ExpectAnyToken( script_t *script, token_t *token ) { + if ( !PS_ReadToken( script, token ) ) { + ScriptError( script, "couldn't read expected token" ); + return 0; + } //end if + else + { + return 1; + } //end else +} //end of the function PS_ExpectAnyToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_CheckTokenString( script_t *script, char *string ) { + token_t tok; + + if ( !PS_ReadToken( script, &tok ) ) { + return 0; + } + //if the token is available + if ( !strcmp( tok.string, string ) ) { + return 1; + } + //token not available + script->script_p = script->lastscript_p; + return 0; +} //end of the function PS_CheckTokenString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_CheckTokenType( script_t *script, int type, int subtype, token_t *token ) { + token_t tok; + + if ( !PS_ReadToken( script, &tok ) ) { + return 0; + } + //if the type matches + if ( tok.type == type && + ( tok.subtype & subtype ) == subtype ) { + memcpy( token, &tok, sizeof( token_t ) ); + return 1; + } //end if + //token is not available + script->script_p = script->lastscript_p; + return 0; +} //end of the function PS_CheckTokenType +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int PS_SkipUntilString( script_t *script, char *string ) { + token_t token; + + while ( PS_ReadToken( script, &token ) ) + { + if ( !strcmp( token.string, string ) ) { + return 1; + } + } //end while + return 0; +} //end of the function PS_SkipUntilString +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PS_UnreadLastToken( script_t *script ) { + script->tokenavailable = 1; +} //end of the function UnreadLastToken +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void PS_UnreadToken( script_t *script, token_t *token ) { + memcpy( &script->token, token, sizeof( token_t ) ); + script->tokenavailable = 1; +} //end of the function UnreadToken +//============================================================================ +// returns the next character of the read white space, returns NULL if none +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +char PS_NextWhiteSpaceChar( script_t *script ) { + if ( script->whitespace_p != script->endwhitespace_p ) { + return *script->whitespace_p++; + } //end if + else + { + return 0; + } //end else +} //end of the function PS_NextWhiteSpaceChar +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void StripDoubleQuotes( char *string ) { + if ( *string == '\"' ) { + strcpy( string, string + 1 ); + } //end if + if ( string[strlen( string ) - 1] == '\"' ) { + string[strlen( string ) - 1] = '\0'; + } //end if +} //end of the function StripDoubleQuotes +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void StripSingleQuotes( char *string ) { + if ( *string == '\'' ) { + strcpy( string, string + 1 ); + } //end if + if ( string[strlen( string ) - 1] == '\'' ) { + string[strlen( string ) - 1] = '\0'; + } //end if +} //end of the function StripSingleQuotes +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +long double ReadSignedFloat( script_t *script ) { + token_t token; + long double sign = 1; + + PS_ExpectAnyToken( script, &token ); + if ( !strcmp( token.string, "-" ) ) { + sign = -1; + PS_ExpectTokenType( script, TT_NUMBER, 0, &token ); + } //end if + else if ( token.type != TT_NUMBER ) { + ScriptError( script, "expected float value, found %s\n", token.string ); + } //end else if + return sign * token.floatvalue; +} //end of the function ReadSignedFloat +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +signed long int ReadSignedInt( script_t *script ) { + token_t token; + signed long int sign = 1; + + PS_ExpectAnyToken( script, &token ); + if ( !strcmp( token.string, "-" ) ) { + sign = -1; + PS_ExpectTokenType( script, TT_NUMBER, TT_INTEGER, &token ); + } //end if + else if ( token.type != TT_NUMBER || token.subtype == TT_FLOAT ) { + ScriptError( script, "expected integer value, found %s\n", token.string ); + } //end else if + return sign * token.intvalue; +} //end of the function ReadSignedInt +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void SetScriptFlags( script_t *script, int flags ) { + script->flags = flags; +} //end of the function SetScriptFlags +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int GetScriptFlags( script_t *script ) { + return script->flags; +} //end of the function GetScriptFlags +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void ResetScript( script_t *script ) { + //pointer in script buffer + script->script_p = script->buffer; + //pointer in script buffer before reading token + script->lastscript_p = script->buffer; + //begin of white space + script->whitespace_p = NULL; + //end of white space + script->endwhitespace_p = NULL; + //set if there's a token available in script->token + script->tokenavailable = 0; + // + script->line = 1; + script->lastline = 1; + //clear the saved token + memset( &script->token, 0, sizeof( token_t ) ); +} //end of the function ResetScript +//============================================================================ +// returns true if at the end of the script +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int EndOfScript( script_t *script ) { + return script->script_p >= script->end_p; +} //end of the function EndOfScript +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int NumLinesCrossed( script_t *script ) { + return script->line - script->lastline; +} //end of the function NumLinesCrossed +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int ScriptSkipTo( script_t *script, char *value ) { + int len; + char firstchar; + + firstchar = *value; + len = strlen( value ); + do + { + if ( !PS_ReadWhiteSpace( script ) ) { + return 0; + } + if ( *script->script_p == firstchar ) { + if ( !strncmp( script->script_p, value, len ) ) { + return 1; + } //end if + } //end if + script->script_p++; + } while ( 1 ); +} //end of the function ScriptSkipTo +#ifndef BOTLIB +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +int FileLength( FILE *fp ) { + int pos; + int end; + + pos = ftell( fp ); + fseek( fp, 0, SEEK_END ); + end = ftell( fp ); + fseek( fp, pos, SEEK_SET ); + + return end; +} //end of the function FileLength +#endif +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +script_t *LoadScriptFile( char *filename ) { +#ifdef BOTLIB + fileHandle_t fp; + char pathname[MAX_QPATH]; +#else + FILE *fp; +#endif + int length; + void *buffer; + script_t *script; + +#ifdef BOTLIB + Com_sprintf( pathname, MAX_QPATH, "botfiles/%s", filename ); + length = botimport.FS_FOpenFile( pathname, &fp, FS_READ ); + if ( !fp ) { + return NULL; + } +#else + fp = fopen( filename, "rb" ); + if ( !fp ) { + return NULL; + } + + length = FileLength( fp ); +#endif + + buffer = GetClearedMemory( sizeof( script_t ) + length + 1 ); + script = (script_t *) buffer; + memset( script, 0, sizeof( script_t ) ); + strcpy( script->filename, filename ); + script->buffer = (char *) buffer + sizeof( script_t ); + script->buffer[length] = 0; + script->length = length; + //pointer in script buffer + script->script_p = script->buffer; + //pointer in script buffer before reading token + script->lastscript_p = script->buffer; + //pointer to end of script buffer + script->end_p = &script->buffer[length]; + //set if there's a token available in script->token + script->tokenavailable = 0; + // + script->line = 1; + script->lastline = 1; + // + SetScriptPunctuations( script, NULL ); + // +#ifdef BOTLIB + botimport.FS_Read( script->buffer, length, fp ); + botimport.FS_FCloseFile( fp ); +#else + if ( fread( script->buffer, length, 1, fp ) != 1 ) { + FreeMemory( buffer ); + script = NULL; + } //end if + fclose( fp ); +#endif + // + return script; +} //end of the function LoadScriptFile +//============================================================================ +//load a script from the given memory with the given length +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +script_t *LoadScriptMemory( char *ptr, int length, char *name ) { + void *buffer; + script_t *script; + + buffer = GetClearedMemory( sizeof( script_t ) + length + 1 ); + script = (script_t *) buffer; + memset( script, 0, sizeof( script_t ) ); + strcpy( script->filename, name ); + script->buffer = (char *) buffer + sizeof( script_t ); + script->buffer[length] = 0; + script->length = length; + //pointer in script buffer + script->script_p = script->buffer; + //pointer in script buffer before reading token + script->lastscript_p = script->buffer; + //pointer to end of script buffer + script->end_p = &script->buffer[length]; + //set if there's a token available in script->token + script->tokenavailable = 0; + // + script->line = 1; + script->lastline = 1; + // + SetScriptPunctuations( script, NULL ); + // + memcpy( script->buffer, ptr, length ); + // + return script; +} //end of the function LoadScriptMemory +//============================================================================ +// +// Parameter: - +// Returns: - +// Changes Globals: - +//============================================================================ +void FreeScript( script_t *script ) { +#ifdef PUNCTABLE + if ( script->punctuationtable ) { + FreeMemory( script->punctuationtable ); + } +#endif //PUNCTABLE + FreeMemory( script ); +} //end of the function FreeScript diff --git a/l_script.h b/l_script.h new file mode 100644 index 0000000..d9136a8 --- /dev/null +++ b/l_script.h @@ -0,0 +1,270 @@ +/* +=========================================================================== + +Return to Castle Wolfenstein single player GPL Source Code +Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. + +This file is part of the Return to Castle Wolfenstein single player GPL Source Code (“RTCW SP Source Code”). + +RTCW SP 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 3 of the License, or +(at your option) any later version. + +RTCW SP 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 RTCW SP Source Code. If not, see . + +In addition, the RTCW SP Source Code is also subject to certain additional terms. +You should have received a copy of these additional terms immediately following the +terms and conditions of the GNU General Public License which accompanied the RTCW SP +Source Code. If not, please request a copy in writing from id Software at the address below. + +If you have questions concerning this license or the applicable additional terms, +you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. + +=========================================================================== +*/ + + +/***************************************************************************** + * name: l_script.h + * + * desc: lexicographical parser + * + * + *****************************************************************************/ + +// Ridah, can't get it to compile without this +#ifndef QDECL + +// for windows fastcall option +#define QDECL +//======================= WIN32 DEFINES ================================= +#ifdef WIN32 +#undef QDECL +#define QDECL __cdecl +#endif +#endif +// done. + +//undef if binary numbers of the form 0b... or 0B... are not allowed +#define BINARYNUMBERS +//undef if not using the token.intvalue and token.floatvalue +#define NUMBERVALUE +//use dollar sign also as punctuation +#define DOLLAR + +//maximum token length +#define MAX_TOKEN 1024 +//maximum path length +#ifndef MAX_QPATH + #define MAX_QPATH 64 +#endif +#ifndef _MAX_PATH + #define _MAX_PATH MAX_QPATH +#endif + +//script flags +#define SCFL_NOERRORS 0x0001 +#define SCFL_NOWARNINGS 0x0002 +#define SCFL_NOSTRINGWHITESPACES 0x0004 +#define SCFL_NOSTRINGESCAPECHARS 0x0008 +#define SCFL_PRIMITIVE 0x0010 +#define SCFL_NOBINARYNUMBERS 0x0020 +#define SCFL_NONUMBERVALUES 0x0040 + +//token types +#define TT_STRING 1 // string +#define TT_LITERAL 2 // literal +#define TT_NUMBER 3 // number +#define TT_NAME 4 // name +#define TT_PUNCTUATION 5 // punctuation + +//string sub type +//--------------- +// the length of the string +//literal sub type +//---------------- +// the ASCII code of the literal +//number sub type +//--------------- +#define TT_DECIMAL 0x0008 // decimal number +#define TT_HEX 0x0100 // hexadecimal number +#define TT_OCTAL 0x0200 // octal number +#ifdef BINARYNUMBERS +#define TT_BINARY 0x0400 // binary number +#endif //BINARYNUMBERS +#define TT_FLOAT 0x0800 // floating point number +#define TT_INTEGER 0x1000 // integer number +#define TT_LONG 0x2000 // long number +#define TT_UNSIGNED 0x4000 // unsigned number +//punctuation sub type +//-------------------- +#define P_RSHIFT_ASSIGN 1 +#define P_LSHIFT_ASSIGN 2 +#define P_PARMS 3 +#define P_PRECOMPMERGE 4 + +#define P_LOGIC_AND 5 +#define P_LOGIC_OR 6 +#define P_LOGIC_GEQ 7 +#define P_LOGIC_LEQ 8 +#define P_LOGIC_EQ 9 +#define P_LOGIC_UNEQ 10 + +#define P_MUL_ASSIGN 11 +#define P_DIV_ASSIGN 12 +#define P_MOD_ASSIGN 13 +#define P_ADD_ASSIGN 14 +#define P_SUB_ASSIGN 15 +#define P_INC 16 +#define P_DEC 17 + +#define P_BIN_AND_ASSIGN 18 +#define P_BIN_OR_ASSIGN 19 +#define P_BIN_XOR_ASSIGN 20 +#define P_RSHIFT 21 +#define P_LSHIFT 22 + +#define P_POINTERREF 23 +#define P_CPP1 24 +#define P_CPP2 25 +#define P_MUL 26 +#define P_DIV 27 +#define P_MOD 28 +#define P_ADD 29 +#define P_SUB 30 +#define P_ASSIGN 31 + +#define P_BIN_AND 32 +#define P_BIN_OR 33 +#define P_BIN_XOR 34 +#define P_BIN_NOT 35 + +#define P_LOGIC_NOT 36 +#define P_LOGIC_GREATER 37 +#define P_LOGIC_LESS 38 + +#define P_REF 39 +#define P_COMMA 40 +#define P_SEMICOLON 41 +#define P_COLON 42 +#define P_QUESTIONMARK 43 + +#define P_PARENTHESESOPEN 44 +#define P_PARENTHESESCLOSE 45 +#define P_BRACEOPEN 46 +#define P_BRACECLOSE 47 +#define P_SQBRACKETOPEN 48 +#define P_SQBRACKETCLOSE 49 +#define P_BACKSLASH 50 + +#define P_PRECOMP 51 +#define P_DOLLAR 52 +//name sub type +//------------- +// the length of the name + +//punctuation +typedef struct punctuation_s +{ + char *p; //punctuation character(s) + int n; //punctuation indication + struct punctuation_s *next; //next punctuation +} punctuation_t; + +//token +typedef struct token_s +{ + char string[MAX_TOKEN]; //available token + int type; //last read token type + int subtype; //last read token sub type +#ifdef NUMBERVALUE + unsigned long int intvalue; //integer value + long double floatvalue; //floating point value +#endif //NUMBERVALUE + char *whitespace_p; //start of white space before token + char *endwhitespace_p; //start of white space before token + int line; //line the token was on + int linescrossed; //lines crossed in white space + struct token_s *next; //next token in chain +} token_t; + +//script file +typedef struct script_s +{ + char filename[_MAX_PATH]; //file name of the script + char *buffer; //buffer containing the script + char *script_p; //current pointer in the script + char *end_p; //pointer to the end of the script + char *lastscript_p; //script pointer before reading token + char *whitespace_p; //begin of the white space + char *endwhitespace_p; //end of the white space + int length; //length of the script in bytes + int line; //current line in script + int lastline; //line before reading token + int tokenavailable; //set by UnreadLastToken + int flags; //several script flags + punctuation_t *punctuations; //the punctuations used in the script + punctuation_t **punctuationtable; + token_t token; //available token + struct script_s *next; //next script in a chain +} script_t; + +//read a token from the script +int PS_ReadToken( script_t *script, token_t *token ); +//expect a certain token +int PS_ExpectTokenString( script_t *script, char *string ); +//expect a certain token type +int PS_ExpectTokenType( script_t *script, int type, int subtype, token_t *token ); +//expect a token +int PS_ExpectAnyToken( script_t *script, token_t *token ); +//returns true when the token is available +int PS_CheckTokenString( script_t *script, char *string ); +//returns true an reads the token when a token with the given type is available +int PS_CheckTokenType( script_t *script, int type, int subtype, token_t *token ); +//skip tokens until the given token string is read +int PS_SkipUntilString( script_t *script, char *string ); +//unread the last token read from the script +void PS_UnreadLastToken( script_t *script ); +//unread the given token +void PS_UnreadToken( script_t *script, token_t *token ); +//returns the next character of the read white space, returns NULL if none +char PS_NextWhiteSpaceChar( script_t *script ); +//remove any leading and trailing double quotes from the token +void StripDoubleQuotes( char *string ); +//remove any leading and trailing single quotes from the token +void StripSingleQuotes( char *string ); +//read a possible signed integer +signed long int ReadSignedInt( script_t *script ); +//read a possible signed floating point number +long double ReadSignedFloat( script_t *script ); +//set an array with punctuations, NULL restores default C/C++ set +void SetScriptPunctuations( script_t *script, punctuation_t *p ); +//set script flags +void SetScriptFlags( script_t *script, int flags ); +//get script flags +int GetScriptFlags( script_t *script ); +//reset a script +void ResetScript( script_t *script ); +//returns true if at the end of the script +int EndOfScript( script_t *script ); +//returns a pointer to the punctuation with the given number +char *PunctuationFromNum( script_t *script, int num ); +//load a script from the given file at the given offset with the given length +script_t *LoadScriptFile( char *filename ); +//load a script from the given memory with the given length +script_t *LoadScriptMemory( char *ptr, int length, char *name ); +//free a script +void FreeScript( script_t *script ); +//print a script error with filename and line number +void QDECL ScriptError( script_t *script, char *str, ... ); +//print a script warning with filename and line number +void QDECL ScriptWarning( script_t *script, char *str, ... ); + +