2007-01-15 12:09:25 -08:00
|
|
|
/*
|
|
|
|
This file is part of Warzone 2100.
|
|
|
|
Copyright (C) 1999-2004 Eidos Interactive
|
2010-07-26 16:36:29 -07:00
|
|
|
Copyright (C) 2005-2010 Warzone 2100 Project
|
2007-01-15 12:09:25 -08:00
|
|
|
|
|
|
|
Warzone 2100 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.
|
|
|
|
|
|
|
|
Warzone 2100 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 Warzone 2100; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
2007-06-28 10:47:08 -07:00
|
|
|
/*
|
|
|
|
* Script.c
|
|
|
|
*
|
|
|
|
* A few general functions for the script library
|
|
|
|
*/
|
|
|
|
|
2006-06-02 12:34:58 -07:00
|
|
|
#include "lib/framework/frame.h"
|
2007-06-28 10:47:08 -07:00
|
|
|
#include "script.h"
|
|
|
|
#include <assert.h>
|
2006-06-02 12:34:58 -07:00
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
|
|
|
|
// Flags for storing function indexes
|
2006-02-28 22:12:28 -08:00
|
|
|
#define FUNC_SETBIT 0x80000000 // set for a set function, clear otherwise
|
2007-06-28 10:47:08 -07:00
|
|
|
#define FUNC_LISTMASK 0x70000000
|
|
|
|
#define FUNC_INDEXMASK 0x0fffffff
|
|
|
|
#define FUNC_INSTINCT 0x00000000 // instinct function
|
|
|
|
#define FUNC_CALLBACK 0x40000000 // callback function
|
2006-02-28 22:12:28 -08:00
|
|
|
#define FUNC_OBJVAR 0x10000000 // object variable
|
2007-06-28 10:47:08 -07:00
|
|
|
#define FUNC_EXTERNAL 0x20000000 // external variable
|
|
|
|
|
|
|
|
|
|
|
|
// Initialise the script library
|
2007-12-10 15:15:46 -08:00
|
|
|
BOOL scriptInitialise()
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
|
|
|
if (!stackInitialise())
|
|
|
|
{
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
if (!interpInitialise())
|
|
|
|
{
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2007-12-10 15:15:46 -08:00
|
|
|
if (!eventInitialise())
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Shutdown the script library
|
|
|
|
void scriptShutDown(void)
|
|
|
|
{
|
|
|
|
eventShutDown();
|
|
|
|
stackShutDown();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Free a SCRIPT_CODE structure */
|
|
|
|
void scriptFreeCode(SCRIPT_CODE *psCode)
|
|
|
|
{
|
2008-07-06 07:02:46 -07:00
|
|
|
unsigned int i, j;
|
2007-06-28 10:47:08 -07:00
|
|
|
|
2006-09-19 09:07:06 -07:00
|
|
|
debug(LOG_WZ, "Unloading script data");
|
|
|
|
|
|
|
|
/* Free local vars */
|
2008-07-06 07:02:46 -07:00
|
|
|
for (i = 0; i < psCode->numEvents; i++)
|
2006-09-19 09:07:06 -07:00
|
|
|
{
|
2008-07-06 07:02:46 -07:00
|
|
|
if (psCode->numLocalVars[i] > 0) // only free if any defined
|
2006-09-19 09:07:06 -07:00
|
|
|
{
|
|
|
|
//free strings for event i
|
2008-07-06 07:02:46 -07:00
|
|
|
for (j = 0; j < psCode->numLocalVars[i]; j++)
|
2006-09-19 09:07:06 -07:00
|
|
|
{
|
2009-02-27 18:17:20 -08:00
|
|
|
// When a script fails, it don't allocate storage.
|
|
|
|
if (psCode->ppsLocalVarVal)
|
2008-03-27 11:38:25 -07:00
|
|
|
{
|
2009-02-27 18:17:20 -08:00
|
|
|
if (psCode->ppsLocalVarVal[i][j].type == VAL_STRING)
|
|
|
|
{
|
|
|
|
free(psCode->ppsLocalVarVal[i][j].v.sval);
|
|
|
|
}
|
2008-03-27 11:38:25 -07:00
|
|
|
}
|
2006-09-19 09:07:06 -07:00
|
|
|
}
|
|
|
|
|
2008-03-27 11:38:25 -07:00
|
|
|
free(psCode->ppsLocalVars[i]);
|
2009-02-27 18:17:20 -08:00
|
|
|
if (psCode->ppsLocalVarVal)
|
|
|
|
{
|
|
|
|
free(psCode->ppsLocalVarVal[i]); // free pointer to event i local vars
|
|
|
|
}
|
2006-09-19 09:07:06 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-06 07:02:46 -07:00
|
|
|
free(psCode->numLocalVars);
|
|
|
|
free(psCode->ppsLocalVars);
|
|
|
|
free(psCode->ppsLocalVarVal);
|
|
|
|
|
2007-04-15 03:43:05 -07:00
|
|
|
free(psCode->pCode);
|
2008-07-06 07:02:46 -07:00
|
|
|
|
|
|
|
free(psCode->pTriggerTab);
|
|
|
|
free(psCode->psTriggerData);
|
|
|
|
|
2007-04-15 03:43:05 -07:00
|
|
|
free(psCode->pEventTab);
|
|
|
|
free(psCode->pEventLinks);
|
2008-07-06 07:02:46 -07:00
|
|
|
|
|
|
|
free(psCode->pGlobals);
|
|
|
|
|
|
|
|
free(psCode->psArrayInfo);
|
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
if (psCode->psDebug)
|
|
|
|
{
|
2008-07-06 07:02:46 -07:00
|
|
|
for (i = 0; i < psCode->debugEntries; i++)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2008-07-06 07:02:46 -07:00
|
|
|
free(psCode->psDebug[i].pLabel);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2007-04-15 03:43:05 -07:00
|
|
|
free(psCode->psDebug);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2008-07-06 07:02:46 -07:00
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
if (psCode->psVarDebug)
|
|
|
|
{
|
2008-07-06 07:02:46 -07:00
|
|
|
for (i = 0; i < psCode->numGlobals; i++)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2008-07-06 07:02:46 -07:00
|
|
|
free(psCode->psVarDebug[i].pIdent);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2007-04-15 03:43:05 -07:00
|
|
|
free(psCode->psVarDebug);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2008-07-06 07:02:46 -07:00
|
|
|
|
2007-06-28 10:47:08 -07:00
|
|
|
if (psCode->psArrayDebug)
|
|
|
|
{
|
2008-07-06 07:02:46 -07:00
|
|
|
for (i = 0; i < psCode->numArrays; i++)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
2008-07-06 07:02:46 -07:00
|
|
|
free(psCode->psArrayDebug[i].pIdent);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2007-04-15 03:43:05 -07:00
|
|
|
free(psCode->psArrayDebug);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2006-08-15 11:38:51 -07:00
|
|
|
|
2008-07-06 07:02:46 -07:00
|
|
|
free(psCode->numParams);
|
2006-08-20 07:48:14 -07:00
|
|
|
|
2007-04-15 03:43:05 -07:00
|
|
|
free(psCode);
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Lookup a script variable */
|
2006-11-03 13:35:50 -08:00
|
|
|
BOOL scriptGetVarIndex(SCRIPT_CODE *psCode, char *pID, UDWORD *pIndex)
|
2007-06-28 10:47:08 -07:00
|
|
|
{
|
|
|
|
UDWORD index;
|
|
|
|
|
|
|
|
if (!psCode->psVarDebug)
|
|
|
|
{
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for(index=0; index<psCode->numGlobals; index++)
|
|
|
|
{
|
|
|
|
if (strcmp(psCode->psVarDebug[index].pIdent, pID)==0)
|
|
|
|
{
|
|
|
|
*pIndex = index;
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2007-06-28 10:47:08 -07:00
|
|
|
}
|
2006-11-24 17:26:05 -08:00
|
|
|
|
|
|
|
/* returns true if passed INTERP_TYPE is used as a pointer in INTERP_VAL, false otherwise.
|
|
|
|
all types are listed explicitly, with asserts/warnings for invalid/unrecognised types, as
|
|
|
|
getting this wrong will cause segfaults if sizeof(void*) != sizeof(SDWORD) (eg. amd64). a lot of
|
|
|
|
these aren't currently checked for, but it's a lot clearer what's going on if they're all here */
|
|
|
|
BOOL scriptTypeIsPointer(INTERP_TYPE type)
|
|
|
|
{
|
2010-12-20 09:16:45 -08:00
|
|
|
ASSERT((_scr_user_types)type < ST_MAXTYPE || type >= VAL_REF, "Invalid type: %d", type);
|
2006-11-24 17:26:05 -08:00
|
|
|
// any value or'ed with VAL_REF is a pointer
|
2008-03-24 09:51:17 -07:00
|
|
|
if (type >= VAL_REF) return true;
|
2010-12-21 15:58:59 -08:00
|
|
|
switch ((unsigned)type) // Unsigned cast to suppress compiler warnings due to enum abuse.
|
|
|
|
{
|
2006-11-24 17:26:05 -08:00
|
|
|
case VAL_STRING:
|
|
|
|
case VAL_OBJ_GETSET:
|
|
|
|
case VAL_FUNC_EXTERN:
|
|
|
|
case ST_INTMESSAGE:
|
|
|
|
case ST_BASEOBJECT:
|
|
|
|
case ST_DROID:
|
|
|
|
case ST_STRUCTURE:
|
|
|
|
case ST_FEATURE:
|
|
|
|
case ST_TEMPLATE:
|
|
|
|
case ST_TEXTSTRING:
|
|
|
|
case ST_LEVEL:
|
|
|
|
case ST_RESEARCH:
|
|
|
|
case ST_GROUP:
|
|
|
|
case ST_POINTER_O:
|
|
|
|
case ST_POINTER_T:
|
|
|
|
case ST_POINTER_S:
|
|
|
|
case ST_POINTER_STRUCTSTAT:
|
2008-03-24 09:51:17 -07:00
|
|
|
return true;
|
2006-11-24 17:26:05 -08:00
|
|
|
case VAL_BOOL:
|
|
|
|
case VAL_INT:
|
|
|
|
case VAL_FLOAT:
|
|
|
|
case VAL_TRIGGER:
|
|
|
|
case VAL_EVENT:
|
|
|
|
case VAL_VOID:
|
|
|
|
case VAL_OPCODE:
|
|
|
|
case VAL_PKOPCODE:
|
|
|
|
case ST_BASESTATS:
|
|
|
|
case ST_COMPONENT:
|
|
|
|
case ST_BODY:
|
|
|
|
case ST_PROPULSION:
|
|
|
|
case ST_ECM:
|
|
|
|
case ST_SENSOR:
|
|
|
|
case ST_CONSTRUCT:
|
|
|
|
case ST_WEAPON:
|
|
|
|
case ST_REPAIR:
|
|
|
|
case ST_BRAIN:
|
|
|
|
case ST_STRUCTUREID:
|
|
|
|
case ST_STRUCTURESTAT:
|
|
|
|
case ST_FEATURESTAT:
|
|
|
|
case ST_DROIDID:
|
|
|
|
case ST_SOUND:
|
2008-03-24 09:51:17 -07:00
|
|
|
return false;
|
2006-11-24 17:26:05 -08:00
|
|
|
default:
|
2008-03-24 09:51:17 -07:00
|
|
|
ASSERT(false, "scriptTypeIsPointer: unhandled type: %d", type );
|
|
|
|
return false;
|
2006-11-24 17:26:05 -08:00
|
|
|
}
|
|
|
|
}
|
2008-03-26 07:57:35 -07:00
|
|
|
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
INTERP_TYPE type;
|
|
|
|
const char *name;
|
|
|
|
} typeToStringMap[] = {
|
|
|
|
// Basic types
|
|
|
|
{ VAL_BOOL, "bool" },
|
|
|
|
{ VAL_INT, "int" },
|
|
|
|
{ VAL_FLOAT, "float" },
|
|
|
|
{ VAL_STRING, "string" },
|
|
|
|
|
|
|
|
// events and triggers
|
|
|
|
{ VAL_TRIGGER, "trigger" },
|
|
|
|
{ VAL_EVENT, "event" },
|
|
|
|
|
|
|
|
{ VAL_VOID, "void" },
|
|
|
|
|
|
|
|
{ VAL_OPCODE, "opcode" },
|
|
|
|
{ VAL_PKOPCODE, "pkopcode" },
|
|
|
|
|
|
|
|
{ VAL_OBJ_GETSET, "objgs" },
|
|
|
|
{ VAL_FUNC_EXTERN, "func" },
|
|
|
|
|
|
|
|
{ VAL_USERTYPESTART, "usertype" },
|
|
|
|
{ VAL_REF, "ref" },
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const char *scriptTypeToString(INTERP_TYPE type)
|
|
|
|
{
|
|
|
|
int i; // Loop goes down -> signed
|
|
|
|
|
|
|
|
// Look whether it is a defaul type:
|
|
|
|
for (i = ARRAY_SIZE(typeToStringMap)-1;
|
|
|
|
i >= 0 && type <= typeToStringMap[i].type;
|
|
|
|
i--)
|
|
|
|
{
|
|
|
|
if (type >= typeToStringMap[i].type)
|
|
|
|
return typeToStringMap[i].name;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look whether it is a user type:
|
|
|
|
if (asScrTypeTab)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
for(i = 0; asScrTypeTab[i].typeID != 0; i++)
|
|
|
|
{
|
|
|
|
if (asScrTypeTab[i].typeID == type)
|
|
|
|
{
|
|
|
|
return asScrTypeTab[i].pIdent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
OPCODE opcode;
|
|
|
|
const char *name;
|
|
|
|
} opcodeToStringMap[] = {
|
|
|
|
{ OP_PUSH, "push" },
|
|
|
|
{ OP_PUSHREF, "push(ref)" },
|
|
|
|
{ OP_POP, "pop" },
|
|
|
|
|
|
|
|
{ OP_PUSHGLOBAL, "push(global)" },
|
|
|
|
{ OP_POPGLOBAL, "pop(global)" },
|
|
|
|
|
|
|
|
{ OP_PUSHARRAYGLOBAL, "push(global[])" },
|
|
|
|
{ OP_POPARRAYGLOBAL, "push(global[])" },
|
|
|
|
|
|
|
|
{ OP_CALL, "call" },
|
|
|
|
{ OP_VARCALL, "vcall" },
|
|
|
|
|
|
|
|
{ OP_JUMP, "jump" },
|
|
|
|
{ OP_JUMPTRUE, "jump(true)" },
|
|
|
|
{ OP_JUMPFALSE, "jump(false)" },
|
|
|
|
|
|
|
|
{ OP_BINARYOP, "binary" },
|
|
|
|
{ OP_UNARYOP, "unary" },
|
|
|
|
|
|
|
|
{ OP_EXIT, "exit" },
|
|
|
|
{ OP_PAUSE, "pause" },
|
|
|
|
|
|
|
|
// The following operations are secondary data to OP_BINARYOP and OP_UNARYOP
|
|
|
|
|
|
|
|
// Maths operators
|
|
|
|
{ OP_ADD, "+" },
|
|
|
|
{ OP_SUB, "-" },
|
|
|
|
{ OP_MUL, "*" },
|
|
|
|
{ OP_DIV, "/" },
|
|
|
|
{ OP_NEG, "(-)" },
|
|
|
|
{ OP_INC, "--" },
|
|
|
|
{ OP_DEC, "++" },
|
|
|
|
|
|
|
|
// Boolean operators
|
|
|
|
{ OP_AND, "&&" },
|
|
|
|
{ OP_OR, "||" },
|
|
|
|
{ OP_NOT, "!" },
|
|
|
|
|
|
|
|
//String concatenation
|
|
|
|
{ OP_CONC, "&" },
|
|
|
|
|
|
|
|
// Comparison operators
|
|
|
|
{ OP_EQUAL, "=" },
|
|
|
|
{ OP_NOTEQUAL, "!=" },
|
|
|
|
{ OP_GREATEREQUAL, ">=" },
|
|
|
|
{ OP_LESSEQUAL, "<=" },
|
|
|
|
{ OP_GREATER, ">" },
|
|
|
|
{ OP_LESS, "<" },
|
|
|
|
|
|
|
|
{ OP_FUNC, "func" },
|
|
|
|
{ OP_POPLOCAL, "pop(local)" },
|
|
|
|
{ OP_PUSHLOCAL, "push(local)" },
|
|
|
|
|
|
|
|
{ OP_PUSHLOCALREF, "push(localref)" },
|
|
|
|
{ OP_TO_FLOAT, "(float)" },
|
|
|
|
{ OP_TO_INT, "(int)" },
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const char *scriptOpcodeToString(OPCODE opcode)
|
|
|
|
{
|
|
|
|
int i; // Loop goes down -> signed
|
|
|
|
|
|
|
|
// Look whether it is a defaul type:
|
|
|
|
for (i = ARRAY_SIZE(opcodeToStringMap)-1;
|
|
|
|
i >= 0 && opcode <= opcodeToStringMap[i].opcode;
|
|
|
|
i--)
|
|
|
|
{
|
|
|
|
if (opcode >= opcodeToStringMap[i].opcode)
|
|
|
|
return opcodeToStringMap[i].name;
|
|
|
|
}
|
|
|
|
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char *scriptFunctionToString(SCRIPT_FUNC function)
|
|
|
|
{
|
|
|
|
// Search the instinct functions
|
|
|
|
if (asScrInstinctTab)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
for(i = 0; asScrInstinctTab[i].pFunc != NULL; i++)
|
|
|
|
{
|
|
|
|
if (asScrInstinctTab[i].pFunc == function)
|
|
|
|
{
|
|
|
|
return asScrInstinctTab[i].pIdent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search the callback functions
|
|
|
|
if (asScrCallbackTab)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
for(i = 0; asScrCallbackTab[i].type != 0; i++)
|
|
|
|
{
|
|
|
|
if (asScrCallbackTab[i].pFunc == function)
|
|
|
|
{
|
|
|
|
return asScrCallbackTab[i].pIdent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
|