mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
1246 lines
29 KiB
C++
1246 lines
29 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2024 the OpenMoHAA team
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
OpenMoHAA 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 OpenMoHAA source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// gamescript.cpp : Subclass of script that preprocesses labels
|
|
|
|
#include "glb_local.h"
|
|
#include "gamescript.h"
|
|
#include "scriptcompiler.h"
|
|
#include "scriptmaster.h"
|
|
#include "scriptthread.h"
|
|
#include "scriptclass.h"
|
|
#include "scriptexception.h"
|
|
#include "level.h"
|
|
|
|
static unsigned char *current_progBuffer = NULL;
|
|
|
|
str& AbstractScript::Filename(void)
|
|
{
|
|
return Director.GetString(m_Filename);
|
|
}
|
|
|
|
const_str AbstractScript::ConstFilename(void)
|
|
{
|
|
return m_Filename;
|
|
}
|
|
|
|
bool AbstractScript::GetSourceAt(size_t sourcePos, str *sourceLine, int& column, int& line)
|
|
{
|
|
size_t posLine;
|
|
size_t i;
|
|
size_t start;
|
|
char *p;
|
|
char old_token;
|
|
sourceinfo_t *minCachedInfo = NULL;
|
|
|
|
if (!m_SourceBuffer || sourcePos >= m_SourceLength) {
|
|
return false;
|
|
}
|
|
|
|
line = 1;
|
|
column = 0;
|
|
posLine = 0;
|
|
start = 0;
|
|
|
|
p = m_SourceBuffer;
|
|
|
|
if (sourcePos > 128) {
|
|
// Start caching above certain values
|
|
|
|
for (i = 0; i < ARRAY_LEN(cachedInfo); i++) {
|
|
sourceinfo_t *info = &cachedInfo[i];
|
|
|
|
if (info->line && sourcePos > info->sourcePos
|
|
&& (!minCachedInfo || info->sourcePos > minCachedInfo->sourcePos)) {
|
|
minCachedInfo = info;
|
|
}
|
|
}
|
|
|
|
if (minCachedInfo) {
|
|
start = minCachedInfo->sourcePos;
|
|
line = minCachedInfo->line;
|
|
column = minCachedInfo->column;
|
|
posLine = minCachedInfo->startLinePos;
|
|
}
|
|
}
|
|
|
|
for (i = start; i < sourcePos; i++) {
|
|
p = m_SourceBuffer + i;
|
|
column++;
|
|
|
|
if (*p == '\n') {
|
|
line++;
|
|
column = 0;
|
|
if (i + 1 != sourcePos) {
|
|
posLine = i + 1;
|
|
}
|
|
} else if (*p == '\0') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (*p != '\0' && (*p != '\r' && *p != '\n')) {
|
|
p++;
|
|
}
|
|
|
|
old_token = *p;
|
|
*p = '\0';
|
|
|
|
if (sourceLine) {
|
|
*sourceLine = (m_SourceBuffer + posLine);
|
|
}
|
|
|
|
*p = old_token;
|
|
|
|
cachedInfo[cachedInfoIndex].sourcePos = sourcePos;
|
|
cachedInfo[cachedInfoIndex].line = line;
|
|
cachedInfo[cachedInfoIndex].column = column;
|
|
cachedInfo[cachedInfoIndex].startLinePos = posLine;
|
|
cachedInfoIndex = (cachedInfoIndex + 1) % ARRAY_LEN(cachedInfo);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool AbstractScript::GetSourceAt(const unsigned char *sourcePos, str *sourceLine, int& column, int& line)
|
|
{
|
|
const sourceinfo_t *codePos = m_ProgToSource->findKeyValue(sourcePos);
|
|
|
|
if (!codePos) {
|
|
return false;
|
|
}
|
|
|
|
return GetSourceAt(codePos->sourcePos, sourceLine, column, line);
|
|
}
|
|
|
|
void AbstractScript::PrintSourcePos(sourceinfo_t *sourcePos, bool dev)
|
|
{
|
|
int line;
|
|
int column;
|
|
str sourceLine;
|
|
|
|
if (GetSourceAt(sourcePos->sourcePos, &sourceLine, column, line)) {
|
|
PrintSourcePos(sourceLine, column, line, dev);
|
|
} else {
|
|
gi.DPrintf(
|
|
"file '%s', source pos %d line %d column %d:\n",
|
|
Filename().c_str(),
|
|
sourcePos->sourcePos,
|
|
sourcePos->line,
|
|
sourcePos->column
|
|
);
|
|
}
|
|
}
|
|
|
|
void AbstractScript::PrintSourcePos(size_t sourcePos, bool dev)
|
|
{
|
|
int line;
|
|
int column;
|
|
str sourceLine;
|
|
|
|
if (GetSourceAt(sourcePos, &sourceLine, column, line)) {
|
|
PrintSourcePos(sourceLine, column, line, dev);
|
|
} else {
|
|
gi.DPrintf("file '%s', source pos %d:\n", Filename().c_str(), sourcePos);
|
|
}
|
|
}
|
|
|
|
void AbstractScript::PrintSourcePos(unsigned char *m_pCodePos, bool dev)
|
|
{
|
|
if (!m_ProgToSource) {
|
|
return;
|
|
}
|
|
|
|
sourceinfo_t *codePos = m_ProgToSource->findKeyValue(m_pCodePos);
|
|
|
|
if (!codePos) {
|
|
return;
|
|
}
|
|
|
|
PrintSourcePos(codePos, dev);
|
|
}
|
|
|
|
void AbstractScript::PrintSourcePos(str sourceLine, int column, int line, bool dev)
|
|
{
|
|
int i;
|
|
str markerLine = "";
|
|
|
|
for (i = 0; i < column; i++) {
|
|
markerLine.append(sourceLine[i]);
|
|
}
|
|
|
|
markerLine.append("^");
|
|
|
|
gi.DPrintf("(%s, %d):\n%s\n%s\n", Filename().c_str(), line, sourceLine.c_str(), markerLine.c_str());
|
|
}
|
|
|
|
AbstractScript::AbstractScript()
|
|
{
|
|
m_ProgToSource = NULL;
|
|
m_SourceBuffer = NULL;
|
|
m_SourceLength = 0;
|
|
cachedInfoIndex = 0;
|
|
}
|
|
|
|
StateScript::StateScript()
|
|
{
|
|
m_Parent = NULL;
|
|
}
|
|
|
|
template<>
|
|
void con_set<const_str, script_label_t>::Entry::Archive(Archiver& arc)
|
|
{
|
|
unsigned int offset;
|
|
|
|
Director.ArchiveString(arc, key);
|
|
|
|
if (arc.Saving()) {
|
|
offset = value.codepos - current_progBuffer;
|
|
arc.ArchiveUnsigned(&offset);
|
|
} else {
|
|
arc.ArchiveUnsigned(&offset);
|
|
value.codepos = current_progBuffer + offset;
|
|
value.key = key;
|
|
}
|
|
|
|
arc.ArchiveBool(&value.isprivate);
|
|
}
|
|
|
|
void StateScript::Archive(Archiver& arc)
|
|
{
|
|
label_list.Archive(arc);
|
|
}
|
|
|
|
bool StateScript::AddLabel(const_str label, unsigned char *pos, bool private_section)
|
|
{
|
|
if (label_list.findKeyValue(label)) {
|
|
return false;
|
|
}
|
|
|
|
script_label_t& s = label_list.addKeyValue(label);
|
|
|
|
s.codepos = pos;
|
|
s.key = label;
|
|
s.isprivate = private_section;
|
|
|
|
if (!label_list.findKeyValue(STRING_NULL)) {
|
|
label_list.addKeyValue(STRING_NULL) = s;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool StateScript::AddLabel(str label, unsigned char *pos, bool private_section)
|
|
{
|
|
return AddLabel(Director.AddString(label), pos, private_section);
|
|
}
|
|
|
|
unsigned char *StateScript::FindLabel(const_str label)
|
|
{
|
|
script_label_t *s;
|
|
ScriptClass *scriptClass;
|
|
GameScript *script;
|
|
|
|
s = label_list.findKeyValue(label);
|
|
|
|
if (s) {
|
|
// check if the label is a private function
|
|
if (s->isprivate) {
|
|
scriptClass = Director.CurrentScriptClass();
|
|
|
|
if (scriptClass) {
|
|
script = scriptClass->GetScript();
|
|
|
|
// now check if the label's statescript matches this statescript
|
|
if ((&script->m_State) != this) {
|
|
ScriptError("Cannot call a private function.");
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return s->codepos;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
unsigned char *StateScript::FindLabel(str label)
|
|
{
|
|
return FindLabel(Director.AddString(label));
|
|
}
|
|
|
|
const_str StateScript::NearestLabel(unsigned char *pos)
|
|
{
|
|
unsigned int offset = pos - m_Parent->m_ProgBuffer;
|
|
unsigned int bestOfs = 0;
|
|
const_str label = STRING_NULL;
|
|
con_set_enum<const_str, script_label_t> en = label_list;
|
|
con_set_enum<const_str, script_label_t>::Entry *entry;
|
|
|
|
for (entry = en.NextElement(); entry; entry = en.NextElement()) {
|
|
const script_label_t& l = entry->value;
|
|
|
|
if ((l.codepos - m_Parent->m_ProgBuffer) >= bestOfs) {
|
|
bestOfs = l.codepos - m_Parent->m_ProgBuffer;
|
|
|
|
if (bestOfs > offset) {
|
|
break;
|
|
}
|
|
|
|
label = l.key;
|
|
}
|
|
}
|
|
|
|
return label;
|
|
}
|
|
|
|
GameScript::GameScript()
|
|
{
|
|
m_Filename = STRING_NULL;
|
|
successCompile = false;
|
|
|
|
m_ProgBuffer = NULL;
|
|
m_ProgLength = 0;
|
|
m_bPrecompiled = false;
|
|
|
|
requiredStackSize = 0;
|
|
|
|
m_State.m_Parent = this;
|
|
}
|
|
|
|
GameScript::GameScript(const char *filename)
|
|
{
|
|
m_Filename = Director.AddString(filename);
|
|
successCompile = false;
|
|
|
|
m_ProgBuffer = NULL;
|
|
m_ProgLength = 0;
|
|
m_ProgToSource = NULL;
|
|
m_bPrecompiled = false;
|
|
|
|
m_SourceBuffer = NULL;
|
|
m_SourceLength = 0;
|
|
|
|
requiredStackSize = 0;
|
|
|
|
m_State.m_Parent = this;
|
|
}
|
|
|
|
GameScript::~GameScript()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
struct pfixup_t {
|
|
bool isString;
|
|
unsigned int *ptr;
|
|
};
|
|
|
|
static Container<const_str> archivedEvents;
|
|
static Container<const_str> archivedStrings;
|
|
static Container<pfixup_t *> archivedPointerFixup;
|
|
|
|
void ArchiveOpcode(Archiver& arc, unsigned char *code)
|
|
{
|
|
unsigned int index;
|
|
|
|
arc.ArchiveByte(code);
|
|
|
|
switch (*code) {
|
|
case OP_STORE_NIL:
|
|
case OP_STORE_NULL:
|
|
case OP_DONE:
|
|
break;
|
|
|
|
case OP_EXEC_CMD_COUNT1:
|
|
case OP_EXEC_CMD_METHOD_COUNT1:
|
|
case OP_EXEC_METHOD_COUNT1:
|
|
arc.ArchiveByte(code + 1);
|
|
goto __exec;
|
|
|
|
case OP_EXEC_CMD0:
|
|
case OP_EXEC_CMD1:
|
|
case OP_EXEC_CMD2:
|
|
case OP_EXEC_CMD3:
|
|
case OP_EXEC_CMD4:
|
|
case OP_EXEC_CMD5:
|
|
case OP_EXEC_CMD_METHOD0:
|
|
case OP_EXEC_CMD_METHOD1:
|
|
case OP_EXEC_CMD_METHOD2:
|
|
case OP_EXEC_CMD_METHOD3:
|
|
case OP_EXEC_CMD_METHOD4:
|
|
case OP_EXEC_CMD_METHOD5:
|
|
case OP_EXEC_METHOD0:
|
|
case OP_EXEC_METHOD1:
|
|
case OP_EXEC_METHOD2:
|
|
case OP_EXEC_METHOD3:
|
|
case OP_EXEC_METHOD4:
|
|
case OP_EXEC_METHOD5:
|
|
code--;
|
|
__exec:
|
|
if (!arc.Loading()) {
|
|
index = archivedEvents.AddUniqueObject(*reinterpret_cast<const_str *>(code + 2));
|
|
}
|
|
|
|
arc.ArchiveUnsigned(&index);
|
|
|
|
if (arc.Loading()) {
|
|
pfixup_t *p = new pfixup_t;
|
|
|
|
p->isString = false;
|
|
p->ptr = reinterpret_cast<unsigned int *>(code + 2);
|
|
|
|
*reinterpret_cast<unsigned int *>(code + 2) = index;
|
|
archivedPointerFixup.AddObject(p);
|
|
}
|
|
break;
|
|
|
|
case OP_LOAD_FIELD_VAR:
|
|
case OP_LOAD_GAME_VAR:
|
|
case OP_LOAD_GROUP_VAR:
|
|
case OP_LOAD_LEVEL_VAR:
|
|
case OP_LOAD_LOCAL_VAR:
|
|
case OP_LOAD_OWNER_VAR:
|
|
case OP_LOAD_PARM_VAR:
|
|
case OP_LOAD_SELF_VAR:
|
|
case OP_LOAD_STORE_GAME_VAR:
|
|
case OP_LOAD_STORE_GROUP_VAR:
|
|
case OP_LOAD_STORE_LEVEL_VAR:
|
|
case OP_LOAD_STORE_LOCAL_VAR:
|
|
case OP_LOAD_STORE_OWNER_VAR:
|
|
case OP_LOAD_STORE_PARM_VAR:
|
|
case OP_LOAD_STORE_SELF_VAR:
|
|
case OP_STORE_FIELD:
|
|
case OP_STORE_FIELD_REF:
|
|
case OP_STORE_GAME_VAR:
|
|
case OP_STORE_GROUP_VAR:
|
|
case OP_STORE_LEVEL_VAR:
|
|
case OP_STORE_LOCAL_VAR:
|
|
case OP_STORE_OWNER_VAR:
|
|
case OP_STORE_PARM_VAR:
|
|
case OP_STORE_SELF_VAR:
|
|
case OP_STORE_STRING:
|
|
if (!arc.Loading()) {
|
|
index = archivedStrings.AddUniqueObject(*reinterpret_cast<const_str *>(code + 1));
|
|
}
|
|
|
|
arc.ArchiveUnsigned(&index);
|
|
|
|
if (arc.Loading()) {
|
|
pfixup_t *p = new pfixup_t;
|
|
|
|
p->isString = true;
|
|
p->ptr = reinterpret_cast<unsigned int *>(code + 1);
|
|
|
|
*reinterpret_cast<unsigned int *>(code + 1) = index;
|
|
archivedPointerFixup.AddObject(p);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (OpcodeLength(*code) > 1) {
|
|
arc.ArchiveRaw(code + 1, OpcodeLength(*code) - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
template<>
|
|
void con_set<unsigned char *, sourceinfo_t>::Entry::Archive(Archiver& arc)
|
|
{
|
|
unsigned int offset;
|
|
|
|
if (arc.Loading()) {
|
|
arc.ArchiveUnsigned(&offset);
|
|
key = current_progBuffer + offset;
|
|
} else {
|
|
offset = key - current_progBuffer;
|
|
arc.ArchiveUnsigned(&offset);
|
|
}
|
|
|
|
arc.ArchiveUnsigned(&value.sourcePos);
|
|
arc.ArchiveInteger(&value.column);
|
|
arc.ArchiveInteger(&value.line);
|
|
}
|
|
|
|
void GameScript::Archive(Archiver& arc)
|
|
{
|
|
/*
|
|
int count = 0, i;
|
|
unsigned char *p, *code_pos, *code_end;
|
|
const_str s;
|
|
command_t *c, cmd;
|
|
|
|
arc.ArchiveSize( ( long * )&m_ProgLength );
|
|
|
|
if( arc.Saving() )
|
|
{
|
|
p = m_ProgBuffer;
|
|
current_progBuffer = m_ProgBuffer;
|
|
|
|
// archive opcodes
|
|
while( *p != OP_DONE )
|
|
{
|
|
ArchiveOpcode( arc, p );
|
|
|
|
p += OpcodeLength( *p );
|
|
}
|
|
|
|
ArchiveOpcode( arc, p );
|
|
|
|
// archive string dictionary list
|
|
i = archivedStrings.NumObjects();
|
|
arc.ArchiveInteger( &i );
|
|
|
|
for( ; i > 0; i-- )
|
|
{
|
|
Director.ArchiveString( arc, archivedStrings.ObjectAt( i ) );
|
|
}
|
|
|
|
// archive event list
|
|
i = archivedEvents.NumObjects();
|
|
arc.ArchiveInteger( &i );
|
|
|
|
for( ; i > 0; i-- )
|
|
{
|
|
c = Event::GetEventInfo( archivedEvents.ObjectAt( i ) );
|
|
|
|
arc.ArchiveString( &c->command );
|
|
arc.ArchiveInteger( &c->flags );
|
|
arc.ArchiveByte( &c->type );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_ProgBuffer = ( unsigned char * )gi.Malloc( m_ProgLength );
|
|
code_pos = m_ProgBuffer;
|
|
code_end = m_ProgBuffer + m_ProgLength;
|
|
|
|
current_progBuffer = m_ProgBuffer;
|
|
|
|
do
|
|
{
|
|
ArchiveOpcode( arc, code_pos );
|
|
|
|
code_pos += OpcodeLength( *code_pos );
|
|
} while( *code_pos != OP_DONE && arc.NoErrors() );
|
|
|
|
if( !arc.NoErrors() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// retrieve the string dictionary list
|
|
arc.ArchiveInteger( &i );
|
|
archivedStrings.Resize( i + 1 );
|
|
|
|
for( ; i > 0; i-- )
|
|
{
|
|
Director.ArchiveString( arc, s );
|
|
archivedStrings.AddObjectAt( i, s );
|
|
}
|
|
|
|
// retrieve the event list
|
|
arc.ArchiveInteger( &i );
|
|
archivedEvents.Resize( i + 1 );
|
|
|
|
for( ; i > 0; i-- )
|
|
{
|
|
arc.ArchiveString( &cmd.command );
|
|
arc.ArchiveInteger( &cmd.flags );
|
|
arc.ArchiveByte( &cmd.type );
|
|
|
|
archivedEvents.AddObjectAt( i, Event::GetEventWithFlags( cmd.command, cmd.flags, cmd.type ) );
|
|
}
|
|
|
|
// fix program string/event pointers
|
|
for( i = archivedPointerFixup.NumObjects(); i > 0; i-- )
|
|
{
|
|
pfixup_t *fixup = archivedPointerFixup.ObjectAt( i );
|
|
|
|
if( fixup->isString )
|
|
{
|
|
*fixup->ptr = archivedStrings.ObjectAt( *fixup->ptr );
|
|
}
|
|
else
|
|
{
|
|
*fixup->ptr = archivedEvents.ObjectAt( *fixup->ptr );
|
|
}
|
|
|
|
delete fixup;
|
|
}
|
|
|
|
successCompile = true;
|
|
}
|
|
|
|
// cleanup
|
|
archivedStrings.FreeObjectList();
|
|
archivedEvents.FreeObjectList();
|
|
archivedPointerFixup.FreeObjectList();
|
|
|
|
if( !arc.Loading() )
|
|
{
|
|
if( m_ProgToSource )
|
|
{
|
|
count = m_ProgToSource->size();
|
|
arc.ArchiveInteger( &count );
|
|
|
|
m_ProgToSource->Archive( arc );
|
|
}
|
|
else
|
|
{
|
|
arc.ArchiveInteger( &count );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
arc.ArchiveInteger( &count );
|
|
|
|
if( count )
|
|
{
|
|
m_ProgToSource = new con_set < unsigned char *, sourceinfo_t >;
|
|
m_ProgToSource->Archive( arc );
|
|
}
|
|
}
|
|
|
|
arc.ArchiveUnsigned( &requiredStackSize );
|
|
arc.ArchiveBool( &m_bPrecompiled );
|
|
|
|
if( !m_bPrecompiled && arc.Loading() )
|
|
{
|
|
fileHandle_t filehandle = NULL;
|
|
|
|
m_SourceLength = gi.FS_ReadFile( Filename().c_str(), ( void ** )&m_SourceBuffer, true );
|
|
|
|
if( m_SourceLength > 0 )
|
|
{
|
|
m_SourceBuffer = ( char * )gi.Malloc( m_SourceLength );
|
|
|
|
gi.FS_Read( m_SourceBuffer, m_SourceLength, filehandle );
|
|
gi.FS_FCloseFile( filehandle );
|
|
}
|
|
}
|
|
|
|
m_State.Archive( arc );
|
|
|
|
current_progBuffer = NULL;*/
|
|
}
|
|
|
|
void GameScript::Archive(Archiver& arc, GameScript *& scr)
|
|
{
|
|
str filename;
|
|
|
|
if (arc.Saving()) {
|
|
if (scr) {
|
|
filename = scr->Filename();
|
|
} else {
|
|
filename = "";
|
|
}
|
|
|
|
arc.ArchiveString(&filename);
|
|
} else {
|
|
arc.ArchiveString(&filename);
|
|
|
|
if (filename != "") {
|
|
scr = Director.GetScript(filename);
|
|
} else {
|
|
scr = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameScript::ArchiveCodePos(Archiver& arc, unsigned char **codePos)
|
|
{
|
|
int pos = 0;
|
|
str filename;
|
|
|
|
if (arc.Saving()) {
|
|
GetCodePos(*codePos, filename, pos);
|
|
}
|
|
|
|
arc.ArchiveInteger(&pos);
|
|
arc.ArchiveString(&filename);
|
|
|
|
if (arc.Loading()) {
|
|
SetCodePos(*codePos, filename, pos);
|
|
}
|
|
}
|
|
|
|
void GameScript::Close(void)
|
|
{
|
|
for (int i = m_CatchBlocks.NumObjects(); i > 0; i--) {
|
|
delete m_CatchBlocks.ObjectAt(i);
|
|
}
|
|
|
|
m_CatchBlocks.FreeObjectList();
|
|
|
|
if (m_ProgToSource) {
|
|
delete m_ProgToSource;
|
|
m_ProgToSource = NULL;
|
|
}
|
|
|
|
if (m_ProgBuffer) {
|
|
gi.Free(m_ProgBuffer);
|
|
m_ProgBuffer = NULL;
|
|
}
|
|
|
|
if (m_SourceBuffer) {
|
|
gi.Free(m_SourceBuffer);
|
|
m_SourceBuffer = NULL;
|
|
}
|
|
|
|
m_ProgLength = 0;
|
|
m_SourceLength = 0;
|
|
m_bPrecompiled = false;
|
|
}
|
|
|
|
void GameScript::Load(const void *sourceBuffer, size_t sourceLength)
|
|
{
|
|
size_t nodeLength;
|
|
char *m_PreprocessedBuffer;
|
|
|
|
m_SourceBuffer = (char *)gi.Malloc(sourceLength + 2);
|
|
m_SourceLength = sourceLength;
|
|
|
|
// Original mohaa doesn't reallocate the input string to append a newline
|
|
// This is a temporary workaround to handle the absolute disaster of newlines
|
|
// Both the lexer and grammar are extremely abhorrent at handling newlines followed by an EOF
|
|
m_SourceBuffer[sourceLength] = '\n';
|
|
m_SourceBuffer[sourceLength + 1] = 0;
|
|
|
|
memcpy(m_SourceBuffer, sourceBuffer, sourceLength);
|
|
|
|
Compiler.Reset();
|
|
|
|
m_PreprocessedBuffer = Compiler.Preprocess(m_SourceBuffer);
|
|
if (!Compiler.Parse(this, m_PreprocessedBuffer, "script", nodeLength)) {
|
|
gi.DPrintf2("^~^~^ Script file compile error: Couldn't parse '%s'\n", Filename().c_str());
|
|
return Close();
|
|
}
|
|
|
|
if (nodeLength == 0) {
|
|
// No code, assume success
|
|
requiredStackSize = 0;
|
|
successCompile = true;
|
|
return;
|
|
}
|
|
|
|
Compiler.Preclean(m_PreprocessedBuffer);
|
|
|
|
m_ProgBuffer = (unsigned char *)gi.Malloc(nodeLength);
|
|
|
|
if (!Compiler.Compile(this, m_ProgBuffer, m_ProgLength)) {
|
|
gi.DPrintf2("^~^~^ Script file compile error: Couldn't compile '%s'\n", Filename().c_str());
|
|
return Close();
|
|
}
|
|
|
|
requiredStackSize = Compiler.m_iInternalMaxVarStackOffset + 9 * Compiler.m_iMaxExternalVarStackOffset + 1;
|
|
|
|
successCompile = true;
|
|
}
|
|
|
|
bool GameScript::GetCodePos(unsigned char *codePos, str& filename, int& pos)
|
|
{
|
|
pos = codePos - m_ProgBuffer;
|
|
|
|
if (pos >= 0 && pos < m_ProgLength) {
|
|
filename = Filename();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool GameScript::SetCodePos(unsigned char *& codePos, str& filename, int pos)
|
|
{
|
|
if (Filename() == filename) {
|
|
codePos = m_ProgBuffer + pos;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
unsigned int GameScript::GetRequiredStackSize(void)
|
|
{
|
|
return requiredStackSize;
|
|
}
|
|
|
|
qboolean GameScript::labelExists(const char *name)
|
|
{
|
|
str labelname;
|
|
|
|
// if we got passed a NULL than that means just run the script so of course it exists
|
|
if (!name) {
|
|
return true;
|
|
}
|
|
|
|
if (m_State.FindLabel(name)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
StateScript *GameScript::CreateCatchStateScript(unsigned char *try_begin_code_pos, unsigned char *try_end_code_pos)
|
|
{
|
|
CatchBlock *catchBlock = new CatchBlock;
|
|
|
|
catchBlock->m_TryStartCodePos = try_begin_code_pos;
|
|
catchBlock->m_TryEndCodePos = try_end_code_pos;
|
|
|
|
m_CatchBlocks.AddObject(catchBlock);
|
|
|
|
return &catchBlock->m_StateScript;
|
|
}
|
|
|
|
StateScript *GameScript::CreateSwitchStateScript(void)
|
|
{
|
|
return new StateScript;
|
|
}
|
|
|
|
StateScript *GameScript::GetCatchStateScript(unsigned char *in, unsigned char *& out)
|
|
{
|
|
CatchBlock *catchBlock;
|
|
CatchBlock *bestCatchBlock = NULL;
|
|
|
|
for (int i = m_CatchBlocks.NumObjects(); i > 0; i--) {
|
|
catchBlock = m_CatchBlocks.ObjectAt(i);
|
|
|
|
if (in >= catchBlock->m_TryStartCodePos && in < catchBlock->m_TryEndCodePos) {
|
|
if (!bestCatchBlock || catchBlock->m_TryEndCodePos < bestCatchBlock->m_TryEndCodePos) {
|
|
bestCatchBlock = catchBlock;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestCatchBlock) {
|
|
out = bestCatchBlock->m_TryEndCodePos;
|
|
|
|
return &bestCatchBlock->m_StateScript;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
bool GameScript::ScriptCheck(void)
|
|
{
|
|
if (g_scriptcheck->integer == 1) {
|
|
return true;
|
|
}
|
|
|
|
if (g_scriptcheck->integer == 2 || g_scriptcheck->integer == 3) {
|
|
if (strstr(Filename().c_str(), "anim/") != Filename().c_str()) {
|
|
return true;
|
|
}
|
|
}
|
|
if (g_scriptcheck->integer == 3) {
|
|
if (strstr(Filename().c_str(), "global/") != Filename().c_str()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ScriptThreadLabel::ScriptThreadLabel()
|
|
{
|
|
m_Script = NULL;
|
|
m_Label = STRING_EMPTY;
|
|
}
|
|
|
|
ScriptThread *ScriptThreadLabel::Create(Listener *listener)
|
|
{
|
|
ScriptClass *scriptClass;
|
|
ScriptThread *thread;
|
|
|
|
if (!m_Script) {
|
|
return NULL;
|
|
}
|
|
|
|
scriptClass = NULL;
|
|
thread = NULL;
|
|
|
|
try {
|
|
scriptClass = new ScriptClass(m_Script, listener);
|
|
thread = new ScriptThread(scriptClass, m_Script->m_State.FindLabel(m_Label));
|
|
} catch (...) {
|
|
if (scriptClass) {
|
|
delete scriptClass;
|
|
}
|
|
|
|
if (thread) {
|
|
delete thread;
|
|
}
|
|
|
|
throw;
|
|
}
|
|
|
|
return thread;
|
|
}
|
|
|
|
void ScriptThreadLabel::Execute(Listener *listener)
|
|
{
|
|
if (!m_Script) {
|
|
return;
|
|
}
|
|
|
|
ScriptThread *thread = Create(listener);
|
|
|
|
if (thread) {
|
|
thread->Execute();
|
|
}
|
|
}
|
|
|
|
void ScriptThreadLabel::Execute(Listener *listener, Event& ev)
|
|
{
|
|
if (!m_Script) {
|
|
return;
|
|
}
|
|
|
|
ScriptThread *thread = Create(listener);
|
|
|
|
if (thread) {
|
|
thread->Execute(ev);
|
|
}
|
|
}
|
|
|
|
void ScriptThreadLabel::Execute(Listener *listener, Event *ev)
|
|
{
|
|
Execute(listener, *ev);
|
|
}
|
|
|
|
void ScriptThreadLabel::Execute(Listener *pSelf, const SafePtr<Listener>& listener, const SafePtr<Listener>& param)
|
|
{
|
|
if (!m_Script) {
|
|
return;
|
|
}
|
|
|
|
ScriptVariable params[2];
|
|
|
|
params[0].setListenerValue(listener);
|
|
params[1].setListenerValue(param);
|
|
|
|
ScriptClass *scriptClass = new ScriptClass(m_Script, pSelf);
|
|
ScriptThread *thread = Director.CreateScriptThread(scriptClass, m_Label);
|
|
|
|
if (thread) {
|
|
thread->Execute(params, 2);
|
|
}
|
|
}
|
|
|
|
void ScriptThreadLabel::Clear()
|
|
{
|
|
m_Script = NULL;
|
|
m_Label = STRING_EMPTY;
|
|
}
|
|
|
|
void ScriptThreadLabel::Set(const char *label)
|
|
{
|
|
str script;
|
|
char buffer[1023];
|
|
char *p = buffer;
|
|
int i = 0;
|
|
bool foundLabel = false;
|
|
|
|
if (!label || !*label) {
|
|
m_Script = NULL;
|
|
m_Label = STRING_EMPTY;
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz(buffer, label, sizeof(buffer));
|
|
|
|
while (true) {
|
|
if (p[0] == ':' && p[1] == ':') {
|
|
*p = '\0';
|
|
|
|
script = buffer;
|
|
m_Label = Director.AddString(&p[2]);
|
|
foundLabel = true;
|
|
|
|
break;
|
|
}
|
|
|
|
p++;
|
|
i++;
|
|
|
|
if (*p == '\0') {
|
|
break;
|
|
}
|
|
|
|
if (i >= 1023) {
|
|
//we didn't find label but this is how it works.
|
|
//the whole string is the label
|
|
m_Label = Director.AddString(buffer);
|
|
script = buffer;
|
|
foundLabel = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundLabel) {
|
|
script = level.m_mapscript;
|
|
m_Label = Director.AddString(buffer);
|
|
}
|
|
|
|
m_Script = Director.GetGameScript(script);
|
|
|
|
if (!m_Script->m_State.FindLabel(m_Label)) {
|
|
str l = Director.GetString(m_Label);
|
|
|
|
m_Script = NULL;
|
|
m_Label = STRING_EMPTY;
|
|
|
|
ScriptError("^~^~^ Could not find label '%s' in '%s'", l.c_str(), script.c_str());
|
|
}
|
|
}
|
|
|
|
void ScriptThreadLabel::Set(const_str label)
|
|
{
|
|
return Set(Director.GetString(label));
|
|
}
|
|
|
|
void ScriptThreadLabel::SetScript(const ScriptVariable& label)
|
|
{
|
|
switch (label.GetType()) {
|
|
case VARIABLE_STRING:
|
|
m_Script = Director.GetGameScript(label.stringValue());
|
|
m_Label = STRING_EMPTY;
|
|
break;
|
|
case VARIABLE_CONSTSTRING:
|
|
m_Script = Director.GetGameScript(label.constStringValue());
|
|
m_Label = STRING_EMPTY;
|
|
break;
|
|
case VARIABLE_NONE:
|
|
m_Script = NULL;
|
|
m_Label = STRING_EMPTY;
|
|
break;
|
|
case VARIABLE_CONSTARRAY:
|
|
if (label.arraysize() > 1) {
|
|
ScriptVariable *script = label[1];
|
|
ScriptVariable *labelname = label[2];
|
|
|
|
if (script->GetType() == VARIABLE_CONSTSTRING) {
|
|
m_Script = Director.GetGameScript(script->constStringValue());
|
|
} else {
|
|
m_Script = Director.GetGameScript(script->stringValue());
|
|
}
|
|
|
|
m_Label = labelname->constStringValue();
|
|
break;
|
|
}
|
|
default:
|
|
ScriptError("ScriptThreadLabel::SetScript: bad label type '%s'", label.GetTypeName());
|
|
break;
|
|
}
|
|
|
|
if (m_Script && !m_Script->m_State.FindLabel(m_Label)) {
|
|
const str& scriptName = m_Script->Filename();
|
|
const str& labelName = Director.GetString(m_Label);
|
|
|
|
m_Script = NULL;
|
|
m_Label = STRING_EMPTY;
|
|
|
|
ScriptError("^~^~^ Could not find label '%s' in '%s'", scriptName.c_str(), labelName.c_str());
|
|
}
|
|
}
|
|
|
|
void ScriptThreadLabel::SetScript(const char *label)
|
|
{
|
|
str script;
|
|
char buffer[1023];
|
|
char *p = buffer;
|
|
int i = 0;
|
|
bool foundLabel = false;
|
|
|
|
if (!label || !*label) {
|
|
m_Script = NULL;
|
|
m_Label = STRING_EMPTY;
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz(buffer, label, sizeof(buffer));
|
|
|
|
script = buffer;
|
|
while (true) {
|
|
if (p[0] == ':' && p[1] == ':') {
|
|
*p = '\0';
|
|
|
|
m_Label = Director.AddString(&p[2]);
|
|
foundLabel = true;
|
|
|
|
break;
|
|
}
|
|
|
|
p++;
|
|
i++;
|
|
|
|
if (*p == '\0') {
|
|
m_Label = STRING_EMPTY;
|
|
foundLabel = true;
|
|
break;
|
|
}
|
|
|
|
if (i >= 1023) {
|
|
//we didn't find label so empty label
|
|
m_Label = STRING_EMPTY;
|
|
foundLabel = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_Script = Director.GetGameScript(script);
|
|
|
|
if (!m_Script->m_State.FindLabel(m_Label)) {
|
|
str l = Director.GetString(m_Label);
|
|
|
|
m_Script = NULL;
|
|
m_Label = STRING_EMPTY;
|
|
|
|
ScriptError("^~^~^ Could not find label '%s' in '%s'", l.c_str(), script.c_str());
|
|
}
|
|
}
|
|
|
|
void ScriptThreadLabel::SetScript(const_str label)
|
|
{
|
|
SetScript(Director.GetString(label));
|
|
}
|
|
|
|
void ScriptThreadLabel::SetThread(const ScriptVariable& label)
|
|
{
|
|
switch (label.GetType()) {
|
|
case VARIABLE_STRING:
|
|
case VARIABLE_CONSTSTRING:
|
|
m_Script = Director.CurrentScriptClass()->GetScript();
|
|
m_Label = label.constStringValue();
|
|
break;
|
|
case VARIABLE_NONE:
|
|
m_Script = NULL;
|
|
m_Label = STRING_EMPTY;
|
|
break;
|
|
case VARIABLE_CONSTARRAY:
|
|
if (label.arraysize() > 1) {
|
|
ScriptVariable *script = label[1];
|
|
ScriptVariable *labelname = label[2];
|
|
|
|
if (script->GetType() == VARIABLE_CONSTSTRING) {
|
|
m_Script = Director.GetGameScript(script->constStringValue());
|
|
} else {
|
|
m_Script = Director.GetGameScript(script->stringValue());
|
|
}
|
|
|
|
m_Label = labelname->constStringValue();
|
|
break;
|
|
}
|
|
default:
|
|
ScriptError("ScriptThreadLabel::SetThread: bad label type '%s'", label.GetTypeName());
|
|
break;
|
|
}
|
|
|
|
if (m_Script && !m_Script->m_State.FindLabel(m_Label)) {
|
|
const str& scriptName = m_Script->Filename();
|
|
const str& labelName = Director.GetString(m_Label);
|
|
|
|
m_Script = NULL;
|
|
m_Label = STRING_EMPTY;
|
|
|
|
ScriptError("^~^~^ Could not find label '%s' in '%s'", scriptName.c_str(), labelName.c_str());
|
|
}
|
|
}
|
|
|
|
bool ScriptThreadLabel::TrySet(const char *label)
|
|
{
|
|
try {
|
|
Set(label);
|
|
} catch (const ScriptException& exc) {
|
|
Com_Printf("%s\n", exc.string.c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ScriptThreadLabel::TrySet(const_str label)
|
|
{
|
|
try {
|
|
Set(label);
|
|
} catch (const ScriptException& exc) {
|
|
Com_Printf("%s\n", exc.string.c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ScriptThreadLabel::TrySetScript(const char *label)
|
|
{
|
|
try {
|
|
SetScript(label);
|
|
} catch (const ScriptException& exc) {
|
|
Com_Printf("%s\n", exc.string.c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ScriptThreadLabel::TrySetScript(const_str label)
|
|
{
|
|
try {
|
|
SetScript(label);
|
|
} catch (const ScriptException& exc) {
|
|
Com_Printf("%s\n", exc.string.c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ScriptThreadLabel::GetScriptValue(ScriptVariable *var)
|
|
{
|
|
if (!m_Script) {
|
|
var->Clear();
|
|
return;
|
|
}
|
|
|
|
ScriptVariable var_array[2];
|
|
|
|
var_array[0].setConstStringValue(m_Script->ConstFilename());
|
|
var_array[1].setConstStringValue(m_Label);
|
|
var->setConstArrayValue(var_array, 2);
|
|
}
|
|
|
|
bool ScriptThreadLabel::IsSet(void)
|
|
{
|
|
return m_Script != NULL;
|
|
}
|
|
|
|
bool ScriptThreadLabel::IsFile(const_str filename)
|
|
{
|
|
return m_Script && m_Script->ConstFilename() == filename && m_Label == STRING_EMPTY;
|
|
}
|
|
|
|
void ScriptThreadLabel::Archive(Archiver& arc)
|
|
{
|
|
GameScript::Archive(arc, m_Script);
|
|
|
|
Director.ArchiveString(arc, m_Label);
|
|
}
|