- merged 3dfloors2 branch into trunk.
SVN r3124 (trunk)
This commit is contained in:
parent
a2acc382df
commit
fae8ed444c
41 changed files with 12358 additions and 445 deletions
746
src/fragglescript/t_parse.cpp
Normal file
746
src/fragglescript/t_parse.cpp
Normal file
|
|
@ -0,0 +1,746 @@
|
|||
// Emacs style mode select -*- C++ -*-
|
||||
//----------------------------------------------------------------------------
|
||||
//
|
||||
// Copyright(C) 2000 Simon Howard
|
||||
// Copyright(C) 2002-2008 Christoph Oelckers
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Parsing.
|
||||
//
|
||||
// Takes lines of code, or groups of lines and runs them.
|
||||
// The main core of FraggleScript
|
||||
//
|
||||
// By Simon Howard
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// FraggleScript is from SMMU which is under the GPL. Technically,
|
||||
// therefore, combining the FraggleScript code with the non-free
|
||||
// ZDoom code is a violation of the GPL.
|
||||
//
|
||||
// As this may be a problem for you, I hereby grant an exception to my
|
||||
// copyright on the SMMU source (including FraggleScript). You may use
|
||||
// any code from SMMU in (G)ZDoom, provided that:
|
||||
//
|
||||
// * For any binary release of the port, the source code is also made
|
||||
// available.
|
||||
// * The copyright notice is kept on any file containing my code.
|
||||
//
|
||||
//
|
||||
|
||||
/* includes ************************/
|
||||
#include <stdarg.h>
|
||||
#include "t_script.h"
|
||||
#include "s_sound.h"
|
||||
#include "v_text.h"
|
||||
#include "c_cvars.h"
|
||||
#include "i_system.h"
|
||||
|
||||
|
||||
CVAR(Bool, script_debug, false, 0)
|
||||
|
||||
/************ Divide into tokens **************/
|
||||
#define isnum(c) ( ((c)>='0' && (c)<='9') || (c)=='.')
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// NextToken: end this token, go onto the next
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::NextToken()
|
||||
{
|
||||
if(Tokens[NumTokens-1][0] || TokenType[NumTokens-1]==string_)
|
||||
{
|
||||
NumTokens++;
|
||||
Tokens[NumTokens-1] = Tokens[NumTokens-2] + strlen(Tokens[NumTokens-2]) + 1;
|
||||
Tokens[NumTokens-1][0] = 0;
|
||||
}
|
||||
|
||||
// get to the next token, ignoring spaces, newlines,
|
||||
// useless chars, comments etc
|
||||
|
||||
while(1)
|
||||
{
|
||||
// empty whitespace
|
||||
if(*Rover && (*Rover==' ' || *Rover<32))
|
||||
{
|
||||
while((*Rover==' ' || *Rover<32) && *Rover) Rover++;
|
||||
}
|
||||
// end-of-script?
|
||||
if(!*Rover)
|
||||
{
|
||||
if(Tokens[0][0])
|
||||
{
|
||||
// line contains text, but no semicolon: an error
|
||||
script_error("missing ';'\n");
|
||||
}
|
||||
// empty line, end of command-list
|
||||
return;
|
||||
}
|
||||
break; // otherwise
|
||||
}
|
||||
|
||||
if(NumTokens>1 && *Rover == '(' && TokenType[NumTokens-2] == name_)
|
||||
TokenType[NumTokens-2] = function;
|
||||
|
||||
if(*Rover == '{' || *Rover == '}')
|
||||
{
|
||||
if(*Rover == '{')
|
||||
{
|
||||
BraceType = bracket_open;
|
||||
Section = Script->FindSectionStart(Rover);
|
||||
}
|
||||
else // closing brace
|
||||
{
|
||||
BraceType = bracket_close;
|
||||
Section = Script->FindSectionEnd(Rover);
|
||||
}
|
||||
if(!Section)
|
||||
{
|
||||
I_Error("section not found!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(*Rover == ':') // label
|
||||
{
|
||||
// ignore the label : reset
|
||||
NumTokens = 1;
|
||||
Tokens[0][0] = 0; TokenType[NumTokens-1] = name_;
|
||||
Rover++; // ignore
|
||||
}
|
||||
else if(*Rover == '\"')
|
||||
{
|
||||
TokenType[NumTokens-1] = string_;
|
||||
if(TokenType[NumTokens-2] == string_) NumTokens--; // join strings
|
||||
Rover++;
|
||||
}
|
||||
else
|
||||
{
|
||||
TokenType[NumTokens-1] = isop(*Rover) ? operator_ : isnum(*Rover) ? number : name_;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// return an escape sequence (prefixed by a '\')
|
||||
// do not use all C escape sequences
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static char escape_sequence(char c)
|
||||
{
|
||||
if(c == 'n') return '\n';
|
||||
if(c == '\\') return '\\';
|
||||
if(c == '"') return '"';
|
||||
if(c == '?') return '?';
|
||||
if(c == 'a') return '\a'; // alert beep
|
||||
if(c == 't') return '\t'; //tab
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// add_char: add one character to the current token
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void add_char(char *tokn, char c)
|
||||
{
|
||||
char *out = tokn + strlen(tokn);
|
||||
|
||||
out[0] = c;
|
||||
out[1] = 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// get_tokens.
|
||||
// Take a string, break it into tokens.
|
||||
//
|
||||
// individual tokens are stored inside the tokens[] array
|
||||
// tokentype is also used to hold the type for each token:
|
||||
//
|
||||
// name: a piece of text which starts with an alphabet letter.
|
||||
// probably a variable name. Some are converted into
|
||||
// function types later on in find_brackets
|
||||
// number: a number. like '12' or '1337'
|
||||
// operator: an operator such as '&&' or '+'. All FraggleScript
|
||||
// operators are either one character, or two character
|
||||
// (if 2 character, 2 of the same char or ending in '=')
|
||||
// string: a text string that was enclosed in quote "" marks in
|
||||
// the original text
|
||||
// unset: shouldn't ever end up being set really.
|
||||
// function: a function name (found in second stage parsing)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
char *FParser::GetTokens(char *s)
|
||||
{
|
||||
char *tokn = NULL;
|
||||
|
||||
Rover = s;
|
||||
NumTokens = 1;
|
||||
Tokens[0][0] = 0; TokenType[NumTokens-1] = name_;
|
||||
|
||||
Section = NULL; // default to no section found
|
||||
|
||||
NextToken();
|
||||
LineStart = Rover; // save the start
|
||||
|
||||
if(*Rover)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
tokn = Tokens[NumTokens-1];
|
||||
if(Section)
|
||||
{
|
||||
// a { or } section brace has been found
|
||||
break; // stop parsing now
|
||||
}
|
||||
else if(TokenType[NumTokens-1] != string_)
|
||||
{
|
||||
if(*Rover == ';') break; // check for end of command ';'
|
||||
}
|
||||
|
||||
switch(TokenType[NumTokens-1])
|
||||
{
|
||||
case unset:
|
||||
case string_:
|
||||
while(*Rover != '\"') // dedicated loop for speed
|
||||
{
|
||||
if(*Rover == '\\') // escape sequences
|
||||
{
|
||||
Rover++;
|
||||
if (*Rover>='0' && *Rover<='9')
|
||||
{
|
||||
add_char(tokn, TEXTCOLOR_ESCAPE);
|
||||
add_char(tokn, *Rover+'A'-'0');
|
||||
}
|
||||
else add_char(tokn, escape_sequence(*Rover));
|
||||
}
|
||||
else
|
||||
add_char(tokn, *Rover);
|
||||
Rover++;
|
||||
}
|
||||
Rover++;
|
||||
NextToken(); // end of this token
|
||||
continue;
|
||||
|
||||
case operator_:
|
||||
// all 2-character operators either end in '=' or
|
||||
// are 2 of the same character
|
||||
// do not allow 2-characters for brackets '(' ')'
|
||||
// which are still being considered as operators
|
||||
|
||||
// operators are only 2-char max, do not need
|
||||
// a seperate loop
|
||||
|
||||
if((*tokn && *Rover != '=' && *Rover!=*tokn) ||
|
||||
*tokn == '(' || *tokn == ')')
|
||||
{
|
||||
// end of operator
|
||||
NextToken();
|
||||
continue;
|
||||
}
|
||||
add_char(tokn, *Rover);
|
||||
break;
|
||||
|
||||
case number:
|
||||
|
||||
// haleyjd: 8-17
|
||||
// add while number chars are read
|
||||
|
||||
while(isnum(*Rover)) // dedicated loop
|
||||
add_char(tokn, *Rover++);
|
||||
NextToken();
|
||||
continue;
|
||||
|
||||
case name_:
|
||||
|
||||
// add the chars
|
||||
|
||||
while(!isop(*Rover)) // dedicated loop
|
||||
add_char(tokn, *Rover++);
|
||||
NextToken();
|
||||
continue;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Rover++;
|
||||
}
|
||||
}
|
||||
|
||||
// check for empty last token
|
||||
|
||||
if(!tokn || !tokn[0])
|
||||
{
|
||||
NumTokens = NumTokens - 1;
|
||||
}
|
||||
|
||||
Rover++;
|
||||
return Rover;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// PrintTokens: add one character to the current token
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::PrintTokens() // DEBUG
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < NumTokens; i++)
|
||||
{
|
||||
Printf("\n'%s' \t\t --", Tokens[i]);
|
||||
switch (TokenType[i])
|
||||
{
|
||||
case string_:
|
||||
Printf("string");
|
||||
break;
|
||||
case operator_:
|
||||
Printf("operator");
|
||||
break;
|
||||
case name_:
|
||||
Printf("name");
|
||||
break;
|
||||
case number:
|
||||
Printf("number");
|
||||
break;
|
||||
case unset:
|
||||
Printf("duh");
|
||||
break;
|
||||
case function:
|
||||
Printf("function name");
|
||||
break;
|
||||
}
|
||||
}
|
||||
Printf("\n");
|
||||
if (Section)
|
||||
Printf("current section: offset %i\n", Section->start_index);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Parses a block of script code
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::Run(char *rover, char *data, char *end)
|
||||
{
|
||||
Rover = rover;
|
||||
try
|
||||
{
|
||||
PrevSection = NULL; // clear it
|
||||
|
||||
while(*Rover) // go through the script executing each statement
|
||||
{
|
||||
// past end of script?
|
||||
if(Rover > end)
|
||||
break;
|
||||
|
||||
PrevSection = Section; // store from prev. statement
|
||||
|
||||
// get the line and tokens
|
||||
GetTokens(Rover);
|
||||
|
||||
if(!NumTokens)
|
||||
{
|
||||
if(Section) // no tokens but a brace
|
||||
{
|
||||
// possible } at end of loop:
|
||||
// refer to spec.c
|
||||
spec_brace();
|
||||
}
|
||||
|
||||
continue; // continue to next statement
|
||||
}
|
||||
|
||||
if(script_debug) PrintTokens(); // debug
|
||||
RunStatement(); // run the statement
|
||||
}
|
||||
}
|
||||
catch (const CFsError &err)
|
||||
{
|
||||
ErrorMessage(err.msg);
|
||||
}
|
||||
catch (const CFsTerminator &)
|
||||
{
|
||||
// The script has signalled that it wants to be terminated in an orderly fashion.
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// decide what to do with it
|
||||
//
|
||||
// NB this stuff is a bit hardcoded:
|
||||
// it could be nicer really but i'm
|
||||
// aiming for speed
|
||||
//
|
||||
// if() and while() will be mistaken for functions
|
||||
// during token processing
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::RunStatement()
|
||||
{
|
||||
if(TokenType[0] == function)
|
||||
{
|
||||
if(!strcmp(Tokens[0], "if"))
|
||||
{
|
||||
Script->lastiftrue = spec_if();
|
||||
return;
|
||||
}
|
||||
else if(!strcmp(Tokens[0], "elseif"))
|
||||
{
|
||||
if(!PrevSection ||
|
||||
(PrevSection->type != st_if &&
|
||||
PrevSection->type != st_elseif))
|
||||
{
|
||||
script_error("elseif statement without if\n");
|
||||
return;
|
||||
}
|
||||
Script->lastiftrue = spec_elseif(Script->lastiftrue);
|
||||
return;
|
||||
}
|
||||
else if(!strcmp(Tokens[0], "else"))
|
||||
{
|
||||
if(!PrevSection ||
|
||||
(PrevSection->type != st_if &&
|
||||
PrevSection->type != st_elseif))
|
||||
{
|
||||
script_error("else statement without if\n");
|
||||
return;
|
||||
}
|
||||
spec_else(Script->lastiftrue);
|
||||
Script->lastiftrue = true;
|
||||
return;
|
||||
}
|
||||
else if(!strcmp(Tokens[0], "while"))
|
||||
{
|
||||
spec_while();
|
||||
return;
|
||||
}
|
||||
else if(!strcmp(Tokens[0], "for"))
|
||||
{
|
||||
spec_for();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if(TokenType[0] == name_)
|
||||
{
|
||||
// NB: goto is a function so is not here
|
||||
|
||||
// Allow else without '()'
|
||||
if (!strcmp(Tokens[0], "else"))
|
||||
{
|
||||
if(!PrevSection ||
|
||||
(PrevSection->type != st_if &&
|
||||
PrevSection->type != st_elseif))
|
||||
{
|
||||
script_error("else statement without if\n");
|
||||
return;
|
||||
}
|
||||
spec_else(Script->lastiftrue);
|
||||
Script->lastiftrue = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// if a variable declaration, return now
|
||||
if(spec_variable()) return;
|
||||
}
|
||||
|
||||
// just a plain expression
|
||||
svalue_t scratch;
|
||||
EvaluateExpression(scratch,0, NumTokens-1);
|
||||
}
|
||||
|
||||
/***************** Evaluating Expressions ************************/
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// find a token, ignoring things in brackets
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FParser::FindOperator(int start, int stop, const char *value)
|
||||
{
|
||||
int i;
|
||||
int bracketlevel = 0;
|
||||
|
||||
for(i=start; i<=stop; i++)
|
||||
{
|
||||
// only interested in operators
|
||||
if(TokenType[i] != operator_) continue;
|
||||
|
||||
// use bracketlevel to check the number of brackets
|
||||
// which we are inside
|
||||
bracketlevel += Tokens[i][0]=='(' ? 1 :
|
||||
Tokens[i][0]==')' ? -1 : 0;
|
||||
|
||||
// only check when we are not in brackets
|
||||
if(!bracketlevel && !strcmp(value, Tokens[i]))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// go through tokens the same as find_operator, but backwards
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int FParser::FindOperatorBackwards(int start, int stop, const char *value)
|
||||
{
|
||||
int i;
|
||||
int bracketlevel = 0;
|
||||
|
||||
for(i=stop; i>=start; i--) // check backwards
|
||||
{
|
||||
// operators only
|
||||
|
||||
if(TokenType[i] != operator_) continue;
|
||||
|
||||
// use bracketlevel to check the number of brackets
|
||||
// which we are inside
|
||||
|
||||
bracketlevel += Tokens[i][0]=='(' ? -1 :
|
||||
Tokens[i][0]==')' ? 1 : 0;
|
||||
|
||||
// only check when we are not in brackets
|
||||
// if we find what we want, return it
|
||||
|
||||
if(!bracketlevel && !strcmp(value, Tokens[i]))
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// simple_evaluate is used once evalute_expression gets to the level
|
||||
// where it is evaluating just one token
|
||||
//
|
||||
// converts number tokens into svalue_ts and returns
|
||||
// the same with string tokens
|
||||
// name tokens are considered to be variables and
|
||||
// attempts are made to find the value of that variable
|
||||
// command tokens are executed (does not return a svalue_t)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::SimpleEvaluate(svalue_t &returnvar, int n)
|
||||
{
|
||||
DFsVariable *var;
|
||||
|
||||
switch(TokenType[n])
|
||||
{
|
||||
case string_:
|
||||
returnvar.type = svt_string;
|
||||
returnvar.string = Tokens[n];
|
||||
break;
|
||||
|
||||
case number:
|
||||
if(strchr(Tokens[n], '.'))
|
||||
{
|
||||
returnvar.type = svt_fixed;
|
||||
returnvar.value.f = (fixed_t)(atof(Tokens[n]) * FRACUNIT);
|
||||
}
|
||||
else
|
||||
{
|
||||
returnvar.type = svt_int;
|
||||
returnvar.value.i = atoi(Tokens[n]);
|
||||
}
|
||||
break;
|
||||
|
||||
case name_:
|
||||
var = Script->FindVariable(Tokens[n]);
|
||||
if(!var)
|
||||
{
|
||||
script_error("unknown variable '%s'\n", Tokens[n]);
|
||||
}
|
||||
else var->GetValue(returnvar);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// pointless_brackets checks to see if there are brackets surrounding
|
||||
// an expression. eg. "(2+4)" is the same as just "2+4"
|
||||
//
|
||||
// because of the recursive nature of evaluate_expression, this function is
|
||||
// neccesary as evaluating expressions such as "2*(2+4)" will inevitably
|
||||
// lead to evaluating "(2+4)"
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::PointlessBrackets(int *start, int *stop)
|
||||
{
|
||||
int bracket_level, i;
|
||||
|
||||
// check that the start and end are brackets
|
||||
|
||||
while(Tokens[*start][0] == '(' && Tokens[*stop][0] == ')')
|
||||
{
|
||||
|
||||
bracket_level = 0;
|
||||
|
||||
// confirm there are pointless brackets..
|
||||
// if they are, bracket_level will only get to 0
|
||||
// at the last token
|
||||
// check up to <*stop rather than <=*stop to ignore
|
||||
// the last token
|
||||
|
||||
for(i = *start; i<*stop; i++)
|
||||
{
|
||||
if(TokenType[i] != operator_) continue; // ops only
|
||||
bracket_level += (Tokens[i][0] == '(');
|
||||
bracket_level -= (Tokens[i][0] == ')');
|
||||
if(bracket_level == 0) return; // stop if braces stop before end
|
||||
}
|
||||
|
||||
// move both brackets in
|
||||
|
||||
*start = *start + 1;
|
||||
*stop = *stop - 1;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// evaluate_expresion is the basic function used to evaluate
|
||||
// a FraggleScript expression.
|
||||
// start and stop denote the tokens which are to be evaluated.
|
||||
//
|
||||
// works by recursion: it finds operators in the expression
|
||||
// (checking for each in turn), then splits the expression into
|
||||
// 2 parts, left and right of the operator found.
|
||||
// The handler function for that particular operator is then
|
||||
// called, which in turn calls evaluate_expression again to
|
||||
// evaluate each side. When it reaches the level of being asked
|
||||
// to evaluate just 1 token, it calls simple_evaluate
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::EvaluateExpression(svalue_t &result, int start, int stop)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
// possible pointless brackets
|
||||
if(TokenType[start] == operator_ && TokenType[stop] == operator_)
|
||||
PointlessBrackets(&start, &stop);
|
||||
|
||||
if(start == stop) // only 1 thing to evaluate
|
||||
{
|
||||
SimpleEvaluate(result, start);
|
||||
return;
|
||||
}
|
||||
|
||||
// go through each operator in order of precedence
|
||||
for(i=0; i<num_operators; i++)
|
||||
{
|
||||
// check backwards for the token. it has to be
|
||||
// done backwards for left-to-right reading: eg so
|
||||
// 5-3-2 is (5-3)-2 not 5-(3-2)
|
||||
|
||||
if (operators[i].direction==forward)
|
||||
{
|
||||
n = FindOperatorBackwards(start, stop, operators[i].string);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = FindOperator(start, stop, operators[i].string);
|
||||
}
|
||||
|
||||
if( n != -1)
|
||||
{
|
||||
// call the operator function and evaluate this chunk of tokens
|
||||
(this->*operators[i].handler)(result, start, n, stop);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(TokenType[start] == function)
|
||||
{
|
||||
EvaluateFunction(result, start, stop);
|
||||
return;
|
||||
}
|
||||
|
||||
// error ?
|
||||
{
|
||||
FString tempstr;
|
||||
|
||||
for(i=start; i<=stop; i++) tempstr << Tokens[i] << ' ';
|
||||
script_error("couldnt evaluate expression: %s\n",tempstr.GetChars());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// intercepts an error message and inserts script/line information
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FParser::ErrorMessage(FString msg)
|
||||
{
|
||||
int linenum = 0;
|
||||
|
||||
// find the line number
|
||||
if(Rover >= Script->data && Rover <= Script->data+Script->len)
|
||||
{
|
||||
char *temp;
|
||||
for(temp = Script->data; temp<LineStart; temp++)
|
||||
if(*temp == '\n') linenum++; // count EOLs
|
||||
}
|
||||
|
||||
//lineinfo.Format("Script %d, line %d: ", Script->scriptnum, linenum);
|
||||
I_Error("Script %d, line %d: %s", Script->scriptnum, linenum, msg.GetChars());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// throws an error message
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void script_error(const char *s, ...)
|
||||
{
|
||||
FString composed;
|
||||
va_list args;
|
||||
va_start(args, s);
|
||||
composed.VFormat(s, args);
|
||||
throw CFsError(composed);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// EOF
|
||||
Loading…
Add table
Add a link
Reference in a new issue