aboutsummaryrefslogtreecommitdiff
path: root/engines/wintermute/base/scriptables
diff options
context:
space:
mode:
Diffstat (limited to 'engines/wintermute/base/scriptables')
-rw-r--r--engines/wintermute/base/scriptables/SXArray.cpp238
-rw-r--r--engines/wintermute/base/scriptables/SXArray.h54
-rw-r--r--engines/wintermute/base/scriptables/SXDate.cpp297
-rw-r--r--engines/wintermute/base/scriptables/SXDate.h55
-rw-r--r--engines/wintermute/base/scriptables/SXFile.cpp779
-rw-r--r--engines/wintermute/base/scriptables/SXFile.h66
-rw-r--r--engines/wintermute/base/scriptables/SXMath.cpp295
-rw-r--r--engines/wintermute/base/scriptables/SXMath.h53
-rw-r--r--engines/wintermute/base/scriptables/SXMemBuffer.cpp508
-rw-r--r--engines/wintermute/base/scriptables/SXMemBuffer.h59
-rw-r--r--engines/wintermute/base/scriptables/SXString.cpp404
-rw-r--r--engines/wintermute/base/scriptables/SXString.h58
-rw-r--r--engines/wintermute/base/scriptables/ScEngine.cpp712
-rw-r--r--engines/wintermute/base/scriptables/ScEngine.h147
-rw-r--r--engines/wintermute/base/scriptables/ScScript.cpp1461
-rw-r--r--engines/wintermute/base/scriptables/ScScript.h183
-rw-r--r--engines/wintermute/base/scriptables/ScStack.cpp226
-rw-r--r--engines/wintermute/base/scriptables/ScStack.h66
-rw-r--r--engines/wintermute/base/scriptables/ScValue.cpp1054
-rw-r--r--engines/wintermute/base/scriptables/ScValue.h141
-rw-r--r--engines/wintermute/base/scriptables/SxObject.cpp67
-rw-r--r--engines/wintermute/base/scriptables/SxObject.h47
22 files changed, 6970 insertions, 0 deletions
diff --git a/engines/wintermute/base/scriptables/SXArray.cpp b/engines/wintermute/base/scriptables/SXArray.cpp
new file mode 100644
index 0000000000..425118a3e7
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXArray.cpp
@@ -0,0 +1,238 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/base/scriptables/ScValue.h"
+#include "engines/wintermute/base/scriptables/ScStack.h"
+#include "engines/wintermute/system/SysInstance.h"
+#include "engines/wintermute/base/scriptables/SXArray.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXArray, false)
+
+CBScriptable *makeSXArray(CBGame *inGame, CScStack *stack) {
+ return new CSXArray(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+CSXArray::CSXArray(CBGame *inGame, CScStack *stack): CBScriptable(inGame) {
+ _length = 0;
+ _values = new CScValue(_gameRef);
+
+ int numParams = stack->pop()->getInt(0);
+
+ if (numParams == 1) _length = stack->pop()->getInt(0);
+ else if (numParams > 1) {
+ _length = numParams;
+ char paramName[20];
+ for (int i = 0; i < numParams; i++) {
+ sprintf(paramName, "%d", i);
+ _values->setProp(paramName, stack->pop());
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+CSXArray::CSXArray(CBGame *inGame): CBScriptable(inGame) {
+ _length = 0;
+ _values = new CScValue(_gameRef);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXArray::~CSXArray() {
+ delete _values;
+ _values = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *CSXArray::scToString() {
+ static char dummy[32768]; // TODO: Get rid of static.
+ strcpy(dummy, "");
+ char propName[20];
+ for (int i = 0; i < _length; i++) {
+ sprintf(propName, "%d", i);
+ CScValue *val = _values->getProp(propName);
+ if (val) {
+ if (strlen(dummy) + strlen(val->getString()) < 32768) {
+ strcat(dummy, val->getString());
+ }
+ }
+
+ if (i < _length - 1 && strlen(dummy) + 1 < 32768) strcat(dummy, ",");
+ }
+ return dummy;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXArray::scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // Push
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Push") == 0) {
+ int numParams = stack->pop()->getInt(0);
+ char paramName[20];
+
+ for (int i = 0; i < numParams; i++) {
+ _length++;
+ sprintf(paramName, "%d", _length - 1);
+ _values->setProp(paramName, stack->pop(), true);
+ }
+ stack->pushInt(_length);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Pop
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Pop") == 0) {
+
+ stack->correctParams(0);
+
+ if (_length > 0) {
+ char paramName[20];
+ sprintf(paramName, "%d", _length - 1);
+ stack->push(_values->getProp(paramName));
+ _values->deleteProp(paramName);
+ _length--;
+ } else stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ else return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CSXArray::scGetProperty(const char *name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Type") == 0) {
+ _scValue->setString("array");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Length
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Length") == 0) {
+ _scValue->setInt(_length);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // [number]
+ //////////////////////////////////////////////////////////////////////////
+ else {
+ char ParamName[20];
+ if (validNumber(name, ParamName)) {
+ return _values->getProp(ParamName);
+ } else return _scValue;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXArray::scSetProperty(const char *name, CScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Length
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Length") == 0) {
+ int OrigLength = _length;
+ _length = MAX(value->getInt(0), 0);
+
+ char PropName[20];
+ if (_length < OrigLength) {
+ for (int i = _length; i < OrigLength; i++) {
+ sprintf(PropName, "%d", i);
+ _values->deleteProp(PropName);
+ }
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // [number]
+ //////////////////////////////////////////////////////////////////////////
+ else {
+ char paramName[20];
+ if (validNumber(name, paramName)) {
+ int Index = atoi(paramName);
+ if (Index >= _length) _length = Index + 1;
+ return _values->setProp(paramName, value);
+ } else return STATUS_FAILED;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXArray::persist(CBPersistMgr *persistMgr) {
+ CBScriptable::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_length));
+ persistMgr->transfer(TMEMBER(_values));
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXArray::validNumber(const char *origStr, char *outStr) {
+ bool isNumber = true;
+ for (uint32 i = 0; i < strlen(origStr); i++) {
+ if (!(origStr[i] >= '0' && origStr[i] <= '9')) {
+ isNumber = false;
+ break;
+ }
+ }
+
+ if (isNumber) {
+ int index = atoi(origStr);
+ sprintf(outStr, "%d", index);
+ return true;
+ } else return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXArray::push(CScValue *val) {
+ char paramName[20];
+ _length++;
+ sprintf(paramName, "%d", _length - 1);
+ _values->setProp(paramName, val, true);
+ return STATUS_OK;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/base/scriptables/SXArray.h b/engines/wintermute/base/scriptables/SXArray.h
new file mode 100644
index 0000000000..0f46bd546e
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXArray.h
@@ -0,0 +1,54 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXARRAY_H
+#define WINTERMUTE_SXARRAY_H
+
+#include "engines/wintermute/base/BScriptable.h"
+
+namespace WinterMute {
+
+class CSXArray : public CBScriptable {
+public:
+ bool push(CScValue *Val);
+ bool validNumber(const char *origStr, char *outStr);
+ DECLARE_PERSISTENT(CSXArray, CBScriptable)
+ CSXArray(CBGame *inGame, CScStack *stack);
+ CSXArray(CBGame *inGame);
+ virtual ~CSXArray();
+ CScValue *scGetProperty(const char *name);
+ bool scSetProperty(const char *name, CScValue *value);
+ bool scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name);
+ const char *scToString();
+ int _length;
+ CScValue *_values;
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/SXDate.cpp b/engines/wintermute/base/scriptables/SXDate.cpp
new file mode 100644
index 0000000000..cd705cc9d4
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXDate.cpp
@@ -0,0 +1,297 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/scriptables/ScStack.h"
+#include "engines/wintermute/base/scriptables/ScValue.h"
+#include "engines/wintermute/base/scriptables/SXDate.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXDate, false)
+
+CBScriptable *makeSXDate(CBGame *inGame, CScStack *stack) {
+ return new CSXDate(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+CSXDate::CSXDate(CBGame *inGame, CScStack *stack): CBScriptable(inGame) {
+ stack->correctParams(6);
+
+ memset(&_tm, 0, sizeof(_tm));
+
+ CScValue *valYear = stack->pop();
+ _tm.tm_year = valYear->getInt() - 1900;
+ _tm.tm_mon = stack->pop()->getInt() - 1;
+ _tm.tm_mday = stack->pop()->getInt();
+ _tm.tm_hour = stack->pop()->getInt();
+ _tm.tm_min = stack->pop()->getInt();
+ _tm.tm_sec = stack->pop()->getInt();
+
+ if (valYear->isNULL()) {
+ g_system->getTimeAndDate(_tm);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXDate::~CSXDate() {
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *CSXDate::scToString() {
+ // TODO: Make this more stringy, and less ISO 8601-like
+ _strRep.format("%04d-%02d-%02d - %02d:%02d:%02d", _tm.tm_year, _tm.tm_mon, _tm.tm_mday, _tm.tm_hour, _tm.tm_min, _tm.tm_sec);
+ return _strRep.c_str();
+#if 0
+ return asctime(&_tm);
+#endif
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXDate::scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // GetYear
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "GetYear") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_year + 1900);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetMonth
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetMonth") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_mon + 1);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetDate
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetDate") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_mday);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetHours
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetHours") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_hour);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetMinutes
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetMinutes") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_min);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetSeconds
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetSeconds") == 0) {
+ stack->correctParams(0);
+ stack->pushInt(_tm.tm_sec);
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetWeekday
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetWeekday") == 0) {
+ stack->correctParams(0);
+ warning("GetWeekday returns a wrong value on purpose");
+ stack->pushInt(_tm.tm_mday % 7);
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetYear
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetYear") == 0) {
+ stack->correctParams(1);
+ _tm.tm_year = stack->pop()->getInt() - 1900;
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetMonth
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetMonth") == 0) {
+ stack->correctParams(1);
+ _tm.tm_mon = stack->pop()->getInt() - 1;
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetDate
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetDate") == 0) {
+ stack->correctParams(1);
+ _tm.tm_mday = stack->pop()->getInt();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetHours
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetHours") == 0) {
+ stack->correctParams(1);
+ _tm.tm_hour = stack->pop()->getInt();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetMinutes
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetMinutes") == 0) {
+ stack->correctParams(1);
+ _tm.tm_min = stack->pop()->getInt();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetSeconds
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetSeconds") == 0) {
+ stack->correctParams(1);
+ _tm.tm_sec = stack->pop()->getInt();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetCurrentTime
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetCurrentTime") == 0) {
+ stack->correctParams(0);
+ g_system->getTimeAndDate(_tm);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ else
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CSXDate::scGetProperty(const char *name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Type") == 0) {
+ _scValue->setString("date");
+ return _scValue;
+ }
+
+ else return _scValue;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXDate::scSetProperty(const char *name, CScValue *value) {
+ /*
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if(strcmp(name, "Name")==0){
+ setName(value->getString());
+ return STATUS_OK;
+ }
+
+ else*/ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXDate::persist(CBPersistMgr *persistMgr) {
+
+ CBScriptable::persist(persistMgr);
+ persistMgr->transfer(TMEMBER(_tm.tm_year));
+ persistMgr->transfer(TMEMBER(_tm.tm_mon));
+ persistMgr->transfer(TMEMBER(_tm.tm_mday));
+ persistMgr->transfer(TMEMBER(_tm.tm_hour));
+ persistMgr->transfer(TMEMBER(_tm.tm_min));
+ persistMgr->transfer(TMEMBER(_tm.tm_sec));
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int CSXDate::scCompare(CBScriptable *Value) {
+ TimeDate time1 = _tm;
+ TimeDate time2 = ((CSXDate *)Value)->_tm;
+
+ if (time1.tm_year < time2.tm_year) {
+ return -1;
+ } else if (time1.tm_year == time2.tm_year) {
+ if (time1.tm_mon < time2.tm_mon) {
+ return -1;
+ } else if (time1.tm_mon == time2.tm_mon) {
+ if (time1.tm_mday < time2.tm_mday) {
+ return -1;
+ } else if (time1.tm_mday == time2.tm_mday) {
+ if (time1.tm_hour < time2.tm_hour) {
+ return -1;
+ } else if (time1.tm_hour == time2.tm_hour) {
+ if (time1.tm_min < time2.tm_min) {
+ return -1;
+ } else if (time1.tm_min == time2.tm_min) {
+ if (time1.tm_sec < time2.tm_sec) {
+ return -1;
+ } else if (time1.tm_sec == time2.tm_sec) {
+ return 0; // Equal
+ } else {
+ return 1; // Sec
+ }
+ } else {
+ return 1; // Minute
+ }
+ } else {
+ return 1; // Hour
+ }
+ } else {
+ return 1; // Day
+ }
+ } else {
+ return 1; // Month
+ }
+ } else {
+ return 1; // Year
+ }
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/base/scriptables/SXDate.h b/engines/wintermute/base/scriptables/SXDate.h
new file mode 100644
index 0000000000..df0641983f
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXDate.h
@@ -0,0 +1,55 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXDATE_H
+#define WINTERMUTE_SXDATE_H
+
+#include "common/system.h"
+#include "engines/wintermute/base/BScriptable.h"
+
+namespace WinterMute {
+
+class CSXDate : public CBScriptable {
+public:
+ int scCompare(CBScriptable *Value);
+ DECLARE_PERSISTENT(CSXDate, CBScriptable)
+ CSXDate(CBGame *inGame, CScStack *Stack);
+ virtual ~CSXDate();
+ CScValue *scGetProperty(const char *name);
+ bool scSetProperty(const char *name, CScValue *value);
+ bool scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name);
+ const char *scToString();
+ char *_string;
+ TimeDate _tm;
+private:
+ Common::String _strRep;
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/SXFile.cpp b/engines/wintermute/base/scriptables/SXFile.cpp
new file mode 100644
index 0000000000..b2a6d24677
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXFile.cpp
@@ -0,0 +1,779 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/system/SysClassRegistry.h"
+#include "engines/wintermute/system/SysClass.h"
+#include "engines/wintermute/base/scriptables/ScStack.h"
+#include "engines/wintermute/base/scriptables/ScValue.h"
+#include "engines/wintermute/base/scriptables/ScScript.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/base/BGame.h"
+#include "engines/wintermute/base/file/BFile.h"
+#include "engines/wintermute/base/BFileManager.h"
+#include "engines/wintermute/PlatformSDL.h"
+#include "engines/wintermute/base/scriptables/SXFile.h"
+
+// Note: This code is completely untested, as I have yet to find a game that uses SXFile.
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXFile, false)
+
+CBScriptable *makeSXFile(CBGame *inGame, CScStack *stack) {
+ return new CSXFile(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+CSXFile::CSXFile(CBGame *inGame, CScStack *stack): CBScriptable(inGame) {
+ stack->correctParams(1);
+ CScValue *Val = stack->pop();
+
+ _filename = NULL;
+ if (!Val->isNULL()) CBUtils::setString(&_filename, Val->getString());
+
+ _readFile = NULL;
+ _writeFile = NULL;
+
+ _mode = 0;
+ _textMode = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXFile::~CSXFile() {
+ cleanup();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXFile::cleanup() {
+ delete[] _filename;
+ _filename = NULL;
+ close();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CSXFile::close() {
+ if (_readFile) {
+ _gameRef->_fileManager->closeFile(_readFile);
+ _readFile = NULL;
+ }
+ if (_writeFile) {
+ _writeFile->finalize();
+ delete _writeFile;
+ _writeFile = NULL;
+ }
+ _mode = 0;
+ _textMode = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *CSXFile::scToString() {
+ if (_filename) return _filename;
+ else return "[file object]";
+}
+
+#define FILE_BUFFER_SIZE 32768
+//////////////////////////////////////////////////////////////////////////
+bool CSXFile::scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SetFilename
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetFilename") == 0) {
+ stack->correctParams(1);
+ const char *filename = stack->pop()->getString();
+ cleanup();
+ CBUtils::setString(&_filename, filename);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // OpenAsText / OpenAsBinary
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "OpenAsText") == 0 || strcmp(name, "OpenAsBinary") == 0) {
+ stack->correctParams(1);
+ close();
+ _mode = stack->pop()->getInt(1);
+ if (_mode < 1 || _mode > 3) {
+ script->runtimeError("File.%s: invalid access mode. Setting read mode.", name);
+ _mode = 1;
+ }
+ if (_mode == 1) {
+ _readFile = _gameRef->_fileManager->openFile(_filename);
+ if (!_readFile) {
+ //script->runtimeError("File.%s: Error opening file '%s' for reading.", Name, _filename);
+ close();
+ } else _textMode = strcmp(name, "OpenAsText") == 0;
+ } else {
+ if (strcmp(name, "OpenAsText") == 0) {
+ if (_mode == 2) _writeFile = openForWrite(_filename, false);
+ else _writeFile = openForAppend(_filename, false);
+ } else {
+ if (_mode == 2) _writeFile = openForWrite(_filename, true);
+ else _writeFile = openForAppend(_filename, true);
+ }
+
+ if (!_writeFile) {
+ //script->runtimeError("File.%s: Error opening file '%s' for writing.", Name, _filename);
+ close();
+ } else _textMode = strcmp(name, "OpenAsText") == 0;
+ }
+
+ if (_readFile || _writeFile) stack->pushBool(true);
+ else stack->pushBool(false);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Close
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Close") == 0) {
+ stack->correctParams(0);
+ close();
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetPosition
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetPosition") == 0) {
+ stack->correctParams(1);
+ if (_mode == 0) {
+ script->runtimeError("File.%s: File is not open", name);
+ stack->pushBool(false);
+ } else {
+ int Pos = stack->pop()->getInt();
+ stack->pushBool(setPos(Pos));
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Delete
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Delete") == 0) {
+ stack->correctParams(0);
+ close();
+ stack->pushBool(CBPlatform::deleteFile(_filename) != false);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Copy
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Copy") == 0) {
+ stack->correctParams(2);
+ const char *Dest = stack->pop()->getString();
+ bool Overwrite = stack->pop()->getBool(true);
+
+ close();
+ stack->pushBool(CBPlatform::copyFile(_filename, Dest, !Overwrite) != false);
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadLine
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadLine") == 0) {
+ stack->correctParams(0);
+ if (!_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open in text mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ uint32 bufSize = FILE_BUFFER_SIZE;
+ byte *buf = (byte *)malloc(bufSize);
+ uint32 counter = 0;
+ byte b;
+ bool foundNewLine = false;
+ bool ret = STATUS_FAILED;
+ do {
+ ret = _readFile->read(&b, 1);
+ if (ret != 1) break;
+
+ if (counter > bufSize) {
+ buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE);
+ bufSize += FILE_BUFFER_SIZE;
+ }
+ if (b == '\n') {
+ buf[counter] = '\0';
+ foundNewLine = true;
+ break;
+ } else if (b == 0x0D) continue;
+ else {
+ buf[counter] = b;
+ counter++;
+ }
+ } while (DID_SUCCEED(ret));
+
+ if (counter > bufSize) {
+ buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE);
+ bufSize += FILE_BUFFER_SIZE;
+ }
+ buf[counter] = '\0';
+
+ if (!foundNewLine && counter == 0) stack->pushNULL();
+ else stack->pushString((char *)buf);
+
+ free(buf);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadText
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadText") == 0) {
+ stack->correctParams(1);
+ int textLen = stack->pop()->getInt();
+
+ if (!_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open in text mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ uint32 bufSize = FILE_BUFFER_SIZE;
+ byte *buf = (byte *)malloc(bufSize);
+ uint32 counter = 0;
+ byte b;
+
+ bool ret = STATUS_FAILED;
+ while (counter < (uint32)textLen) {
+ ret = _readFile->read(&b, 1);
+ if (ret != 1) break;
+
+ if (counter > bufSize) {
+ buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE);
+ bufSize += FILE_BUFFER_SIZE;
+ }
+ if (b == 0x0D) continue;
+ else {
+ buf[counter] = b;
+ counter++;
+ }
+ }
+
+ if (counter > bufSize) {
+ buf = (byte *)realloc(buf, bufSize + FILE_BUFFER_SIZE);
+ bufSize += FILE_BUFFER_SIZE;
+ }
+ buf[counter] = '\0';
+
+ if (textLen > 0 && counter == 0) stack->pushNULL();
+ else stack->pushString((char *)buf);
+
+ free(buf);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteLine / WriteText
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteLine") == 0 || strcmp(name, "WriteText") == 0) {
+ stack->correctParams(1);
+ const char *line = stack->pop()->getString();
+ if (!_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in text mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ Common::String writeLine;
+ if (strcmp(name, "WriteLine") == 0) {
+ writeLine = Common::String::format("%s\n", line);
+ } else {
+ writeLine = Common::String::format("%s", line);
+ }
+ _writeFile->writeString(writeLine);
+ _writeFile->writeByte(0);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////
+ // ReadBool
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadBool") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ bool val;
+ if (_readFile->read(&val, sizeof(bool)) == sizeof(bool)) stack->pushBool(val);
+ else stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadByte
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadByte") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ byte val = _readFile->readByte();
+ if (!_readFile->err()) {
+ stack->pushInt(val);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadShort
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadShort") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ int16 val = _readFile->readSint16LE();
+ if (!_readFile->err()) {
+ stack->pushInt(65536 + val);
+ } else {
+ stack->pushNULL();
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadInt / ReadLong
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadInt") == 0 || strcmp(name, "ReadLong") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ int32 val = _readFile->readSint32LE();
+ if (!_readFile->err()) {
+ stack->pushInt(val);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadFloat
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadFloat") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ float val;
+ (*(uint32*)&val) = _readFile->readUint32LE();
+ if (!_readFile->err()) {
+ stack->pushFloat(val);
+ } else {
+ stack->pushNULL();
+ }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadDouble
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadDouble") == 0) { // TODO: Solve reading a 8 byte double.
+ error("SXFile::ReadDouble - Not endian safe yet");
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ double val;
+ if (_readFile->read(&val, sizeof(double)) == sizeof(double)) stack->pushFloat(val);
+ else stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ReadString") == 0) {
+ stack->correctParams(0);
+ if (_textMode || !_readFile) {
+ script->runtimeError("File.%s: File must be open for reading in binary mode.", name);
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+ uint32 size = _readFile->readUint32LE();
+ if (!_readFile->err()) {
+ byte *str = new byte[size + 1];
+ if (str) {
+ if (_readFile->read(str, size) == size) {
+ str[size] = '\0';
+ stack->pushString((char *)str);
+ }
+ delete [] str;
+ } else stack->pushNULL();
+ } else stack->pushNULL();
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteBool
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteBool") == 0) {
+ stack->correctParams(1);
+ bool val = stack->pop()->getBool();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ _writeFile->writeByte(val);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteByte
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteByte") == 0) {
+ stack->correctParams(1);
+ byte val = stack->pop()->getInt();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ _writeFile->writeByte(val);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteShort
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteShort") == 0) {
+ stack->correctParams(1);
+ int16 val = stack->pop()->getInt();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ _writeFile->writeSint16LE(val);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteInt / WriteLong
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteInt") == 0 || strcmp(name, "WriteLong") == 0) {
+ stack->correctParams(1);
+ int32 val = stack->pop()->getInt();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ _writeFile->writeSint32LE(val);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteFloat
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteFloat") == 0) {
+ stack->correctParams(1);
+ float val = stack->pop()->getFloat();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ uint32 *ptr = (uint32*)&val;
+ _writeFile->writeUint32LE(*ptr);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteDouble
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteDouble") == 0) {
+ error("SXFile::WriteDouble - Not endian safe yet");
+ stack->correctParams(1);
+ double val = stack->pop()->getFloat();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+ //fwrite(&val, sizeof(val), 1, (FILE *)_writeFile);
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "WriteString") == 0) {
+ stack->correctParams(1);
+ const char *val = stack->pop()->getString();
+
+ if (_textMode || !_writeFile) {
+ script->runtimeError("File.%s: File must be open for writing in binary mode.", name);
+ stack->pushBool(false);
+ return STATUS_OK;
+ }
+
+ uint32 size = strlen(val);
+ _writeFile->writeUint32LE(size);
+ _writeFile->writeString(val);
+
+ stack->pushBool(true);
+
+ return STATUS_OK;
+ }
+
+
+ else return CBScriptable::scCallMethod(script, stack, thisStack, name);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CSXFile::scGetProperty(const char *name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Type") == 0) {
+ _scValue->setString("file");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Filename (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Filename") == 0) {
+ _scValue->setString(_filename);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Position (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Position") == 0) {
+ _scValue->setInt(getPos());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Length (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Length") == 0) {
+ _scValue->setInt(getLength());
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // TextMode (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "TextMode") == 0) {
+ _scValue->setBool(_textMode);
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // AccessMode (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "AccessMode") == 0) {
+ _scValue->setInt(_mode);
+ return _scValue;
+ }
+
+ else return CBScriptable::scGetProperty(name);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXFile::scSetProperty(const char *name, CScValue *value) {
+ /*
+ //////////////////////////////////////////////////////////////////////////
+ // Length
+ //////////////////////////////////////////////////////////////////////////
+ if(strcmp(name, "Length")==0){
+ int OrigLength = _length;
+ _length = max(value->getInt(0), 0);
+
+ char PropName[20];
+ if(_length < OrigLength){
+ for(int i=_length; i<OrigLength; i++){
+ sprintf(PropName, "%d", i);
+ _values->DeleteProp(PropName);
+ }
+ }
+ return STATUS_OK;
+ }
+ else*/ return CBScriptable::scSetProperty(name, value);
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CSXFile::getPos() {
+ if (_mode == 1 && _readFile)
+ return _readFile->pos();
+ else if ((_mode == 2 || _mode == 3) && _writeFile) {
+ error("SXFile - getPos for WriteFile not supported");
+ return 0;
+// return ftell((FILE *)_writeFile);
+ } else {
+ return 0;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXFile::setPos(uint32 pos, int whence) {
+ if (_mode == 1 && _readFile)
+ return _readFile->seek(pos, whence);
+ else if ((_mode == 2 || _mode == 3) && _writeFile) {
+ error("CSXFile - seeking in WriteFile not supported");
+ return false;
+// return fseek((FILE *)_writeFile, pos, (int)origin) == 0;
+ }
+ else return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CSXFile::getLength() {
+ if (_mode == 1 && _readFile)
+ return _readFile->size();
+ else if ((_mode == 2 || _mode == 3) && _writeFile) {
+ error("CSXFile - reading length for WriteFile not supported");
+ return 0;
+/*
+ uint32 currentPos = ftell((FILE *)_writeFile);
+ fseek((FILE *)_writeFile, 0, SEEK_END);
+ int ret = ftell((FILE *)_writeFile);
+ fseek((FILE *)_writeFile, CurrentPos, SEEK_SET);
+ return Ret;*/
+ } else return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXFile::persist(CBPersistMgr *persistMgr) {
+
+ CBScriptable::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_filename));
+ persistMgr->transfer(TMEMBER(_mode));
+ persistMgr->transfer(TMEMBER(_textMode));
+
+ uint32 pos = 0;
+ if (persistMgr->_saving) {
+ pos = getPos();
+ persistMgr->transfer(TMEMBER(pos));
+ } else {
+ persistMgr->transfer(TMEMBER(pos));
+
+ // try to re-open file if needed
+ _writeFile = NULL;
+ _readFile = NULL;
+
+ if (_mode != 0) {
+ // open for reading
+ if (_mode == 1) {
+ _readFile = _gameRef->_fileManager->openFile(_filename);
+ if (!_readFile)
+ close();
+ }
+ // open for writing / appending
+ else {
+ if (_textMode) {
+ if (_mode == 2)
+ _writeFile = openForWrite(_filename, false);
+ else
+ _writeFile = openForAppend(_filename, false);
+ } else {
+ if (_mode == 2)
+ _writeFile = openForWrite(_filename, true);
+ else
+ _writeFile = openForAppend(_filename, true);
+ }
+ if (_writeFile)
+ close();
+ }
+ setPos(pos);
+ }
+ }
+
+ return STATUS_OK;
+}
+
+// Should replace fopen(..., "wb+") and fopen(..., "w+")
+Common::WriteStream *CSXFile::openForWrite(const Common::String &filename, bool binary) {
+ error("SXFile::openForWrite - WriteFiles not supported");
+}
+
+// Should replace fopen(..., "ab+") and fopen(..., "a+")
+Common::WriteStream *CSXFile::openForAppend(const Common::String &filename, bool binary) {
+ error("SXFile::openForAppend - WriteFiles not supported");
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/base/scriptables/SXFile.h b/engines/wintermute/base/scriptables/SXFile.h
new file mode 100644
index 0000000000..709d1f4378
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXFile.h
@@ -0,0 +1,66 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTES_SXFILE_H
+#define WINTERMUTES_SXFILE_H
+
+
+#include "engines/wintermute/base/BScriptable.h"
+#include "common/stream.h"
+
+namespace WinterMute {
+
+class CBFile;
+
+class CSXFile : public CBScriptable {
+public:
+ DECLARE_PERSISTENT(CSXFile, CBScriptable)
+ CScValue *scGetProperty(const char *name);
+ bool scSetProperty(const char *name, CScValue *value);
+ bool scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name);
+ const char *scToString();
+ CSXFile(CBGame *inGame, CScStack *Stack);
+ virtual ~CSXFile();
+private:
+ Common::SeekableReadStream *_readFile;
+ Common::WriteStream *_writeFile;
+ int _mode; // 0..none, 1..read, 2..write, 3..append
+ bool _textMode;
+ void close();
+ void cleanup();
+ uint32 getPos();
+ uint32 getLength();
+ bool setPos(uint32 Pos, int whence = SEEK_SET);
+ char *_filename;
+ Common::WriteStream *openForWrite(const Common::String &filename, bool binary);
+ Common::WriteStream *openForAppend(const Common::String &filename, bool binary);
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/SXMath.cpp b/engines/wintermute/base/scriptables/SXMath.cpp
new file mode 100644
index 0000000000..fb2838ee94
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXMath.cpp
@@ -0,0 +1,295 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/scriptables/SXMath.h"
+#include "engines/wintermute/base/scriptables/ScStack.h"
+#include "engines/wintermute/base/scriptables/ScValue.h"
+#include "engines/wintermute/persistent.h"
+#include "common/math.h"
+#include <cmath>
+
+namespace WinterMute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+
+IMPLEMENT_PERSISTENT(CSXMath, true)
+
+CBScriptable *makeSXMath(CBGame *inGame) {
+ return new CSXMath(inGame);
+}
+
+//////////////////////////////////////////////////////////////////////////
+CSXMath::CSXMath(CBGame *inGame): CBScriptable(inGame) {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXMath::~CSXMath() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXMath::scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // Abs
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Abs") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(fabs(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Acos
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Acos") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(acos(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Asin
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Asin") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(asin(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Atan
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Atan") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(atan(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Atan2
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Atan2") == 0) {
+ stack->correctParams(2);
+ double y = stack->pop()->getFloat();
+ double x = stack->pop()->getFloat();
+ stack->pushFloat(atan2(y, x));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Ceil
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Ceil") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(ceil(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Cos
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Cos") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(cos(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Cosh
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Cosh") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(cosh(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Exp
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Exp") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(exp(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Floor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Floor") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(floor(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Log
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Log") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(log(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Log10
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Log10") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(log10(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Pow
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Pow") == 0) {
+ stack->correctParams(2);
+ double x = stack->pop()->getFloat();
+ double y = stack->pop()->getFloat();
+
+ stack->pushFloat(pow(x, y));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sin
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Sin") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(sin(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sinh
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Sinh") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(sinh(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Tan
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Tan") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(tan(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Tanh
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Tanh") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(tanh(degreeToRadian(stack->pop()->getFloat())));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sqrt
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Sqrt") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(sqrt(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DegToRad
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DegToRad") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(degreeToRadian(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RadToDeg
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RadToDeg") == 0) {
+ stack->correctParams(1);
+ stack->pushFloat(radianToDegree(stack->pop()->getFloat()));
+ return STATUS_OK;
+ }
+
+ else return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CSXMath::scGetProperty(const char *name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Type") == 0) {
+ _scValue->setString("math");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PI
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "PI") == 0) {
+ _scValue->setFloat(M_PI);
+ return _scValue;
+ }
+
+ else return _scValue;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double CSXMath::degreeToRadian(double value) {
+ return value * (M_PI / 180.0f);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double CSXMath::radianToDegree(double value) {
+ return value * (180.0f / M_PI);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXMath::persist(CBPersistMgr *persistMgr) {
+
+ CBScriptable::persist(persistMgr);
+ return STATUS_OK;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/base/scriptables/SXMath.h b/engines/wintermute/base/scriptables/SXMath.h
new file mode 100644
index 0000000000..4389de611f
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXMath.h
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXMATH_H
+#define WINTERMUTE_SXMATH_H
+
+
+#include "engines/wintermute/base/BScriptable.h"
+
+namespace WinterMute {
+
+class CSXMath : public CBScriptable {
+public:
+ DECLARE_PERSISTENT(CSXMath, CBScriptable)
+ CSXMath(CBGame *inGame);
+ virtual ~CSXMath();
+ virtual CScValue *scGetProperty(const char *name);
+ virtual bool scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name);
+
+private:
+ double degreeToRadian(double value);
+ double radianToDegree(double value);
+
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/SXMemBuffer.cpp b/engines/wintermute/base/scriptables/SXMemBuffer.cpp
new file mode 100644
index 0000000000..9ac98ab11d
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXMemBuffer.cpp
@@ -0,0 +1,508 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/BScriptable.h"
+#include "engines/wintermute/base/scriptables/ScStack.h"
+#include "engines/wintermute/base/scriptables/ScScript.h"
+#include "engines/wintermute/base/scriptables/ScValue.h"
+#include "engines/wintermute/base/scriptables/SXMemBuffer.h"
+#include "common/file.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXMemBuffer, false)
+
+CBScriptable *makeSXMemBuffer(CBGame *inGame, CScStack *stack) {
+ return new CSXMemBuffer(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+CSXMemBuffer::CSXMemBuffer(CBGame *inGame, CScStack *stack): CBScriptable(inGame) {
+ stack->correctParams(1);
+ _buffer = NULL;
+ _size = 0;
+
+ int NewSize = stack->pop()->getInt();
+ resize(MAX(0, NewSize));
+}
+
+//////////////////////////////////////////////////////////////////////////
+CSXMemBuffer::CSXMemBuffer(CBGame *inGame, void *Buffer): CBScriptable(inGame) {
+ _size = 0;
+ _buffer = Buffer;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXMemBuffer::~CSXMemBuffer() {
+ cleanup();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void *CSXMemBuffer::scToMemBuffer() {
+ return _buffer;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXMemBuffer::cleanup() {
+ if (_size) free(_buffer);
+ _buffer = NULL;
+ _size = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXMemBuffer::resize(int newSize) {
+ int oldSize = _size;
+
+ if (_size == 0) {
+ _buffer = malloc(newSize);
+ if (_buffer) _size = newSize;
+ } else {
+ void *newBuf = realloc(_buffer, newSize);
+ if (!newBuf) {
+ if (newSize == 0) {
+ _buffer = newBuf;
+ _size = newSize;
+ } else return STATUS_FAILED;
+ } else {
+ _buffer = newBuf;
+ _size = newSize;
+ }
+ }
+
+ if (_buffer && _size > oldSize) {
+ memset((byte *)_buffer + oldSize, 0, _size - oldSize);
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXMemBuffer::checkBounds(CScScript *script, int start, int length) {
+ if (_buffer == NULL) {
+ script->runtimeError("Cannot use Set/Get methods on an uninitialized memory buffer");
+ return false;
+ }
+ if (_size == 0)
+ return true;
+
+ if (start < 0 || length == 0 || start + length > _size) {
+ script->runtimeError("Set/Get method call is out of bounds");
+ return false;
+ } else
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *CSXMemBuffer::scToString() {
+ return "[membuffer object]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXMemBuffer::scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SetSize
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "SetSize") == 0) {
+ stack->correctParams(1);
+ int newSize = stack->pop()->getInt();
+ newSize = MAX(0, newSize);
+ if (DID_SUCCEED(resize(newSize)))
+ stack->pushBool(true);
+ else
+ stack->pushBool(false);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetBool
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetBool") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(bool)))
+ stack->pushNULL();
+ else
+ stack->pushBool(*(bool *)((byte *)_buffer + start));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetByte
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetByte") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(byte)))
+ stack->pushNULL();
+ else
+ stack->pushInt(*(byte *)((byte *)_buffer + start));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetShort
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetShort") == 0) {
+ stack->correctParams(1);
+ int Start = stack->pop()->getInt();
+ if (!checkBounds(script, Start, sizeof(short)))
+ stack->pushNULL();
+ else
+ stack->pushInt(65536 + * (short *)((byte *)_buffer + Start));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetInt / GetLong
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetInt") == 0 || strcmp(name, "GetLong") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(int)))
+ stack->pushNULL();
+ else
+ stack->pushInt(*(int *)((byte *)_buffer + start));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetFloat
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetFloat") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(float)))
+ stack->pushNULL();
+ else
+ stack->pushFloat(*(float *)((byte *)_buffer + start));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetDouble
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetDouble") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(double)))
+ stack->pushNULL();
+ else
+ stack->pushFloat(*(double *)((byte *)_buffer + start));
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetString") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ int length = stack->pop()->getInt();
+
+ // find end of string
+ if (length == 0 && start >= 0 && start < _size) {
+ for (int i = start; i < _size; i++) {
+ if (((char *)_buffer)[i] == '\0') {
+ length = i - start;
+ break;
+ }
+ }
+ }
+
+ if (!checkBounds(script, start, length))
+ stack->pushNULL();
+ else {
+ char *str = new char[length + 1];
+ strncpy(str, (const char *)_buffer + start, length);
+ str[length] = '\0';
+ stack->pushString(str);
+ delete [] str;
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // GetPointer
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetPointer") == 0) {
+ stack->correctParams(1);
+ int start = stack->pop()->getInt();
+ if (!checkBounds(script, start, sizeof(void *)))
+ stack->pushNULL();
+ else {
+ void *pointer = *(void **)((byte *)_buffer + start);
+ CSXMemBuffer *buf = new CSXMemBuffer(_gameRef, pointer);
+ stack->pushNative(buf, false);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetBool
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetBool") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ bool val = stack->pop()->getBool();
+
+ if (!checkBounds(script, start, sizeof(bool)))
+ stack->pushBool(false);
+ else {
+ *(bool *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetByte
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetByte") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ byte val = (byte)stack->pop()->getInt();
+
+ if (!checkBounds(script, start, sizeof(byte)))
+ stack->pushBool(false);
+ else {
+ *(byte *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetShort
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetShort") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ short val = (short)stack->pop()->getInt();
+
+ if (!checkBounds(script, start, sizeof(short)))
+ stack->pushBool(false);
+ else {
+ *(short *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetInt / SetLong
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetInt") == 0 || strcmp(name, "SetLong") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ int val = stack->pop()->getInt();
+
+ if (!checkBounds(script, start, sizeof(int)))
+ stack->pushBool(false);
+ else {
+ *(int *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetFloat
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetFloat") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ float val = (float)stack->pop()->getFloat();
+
+ if (!checkBounds(script, start, sizeof(float)))
+ stack->pushBool(false);
+ else {
+ *(float *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetDouble
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetDouble") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ double val = stack->pop()->getFloat();
+
+ if (!checkBounds(script, start, sizeof(double)))
+ stack->pushBool(false);
+ else {
+ *(double *)((byte *)_buffer + start) = val;
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetString") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ const char *val = stack->pop()->getString();
+
+ if (!checkBounds(script, start, strlen(val) + 1))
+ stack->pushBool(false);
+ else {
+ memcpy((byte *)_buffer + start, val, strlen(val) + 1);
+ stack->pushBool(true);
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetPointer
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "SetPointer") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ /* CScValue *Val = */ stack->pop();
+
+ if (!checkBounds(script, start, sizeof(void *)))
+ stack->pushBool(false);
+ else {
+ /*
+ int Pointer = (int)Val->getMemBuffer();
+ memcpy((byte *)_buffer+Start, &Pointer, sizeof(void*));
+ stack->pushBool(true);
+ */
+ // TODO fix
+ stack->pushBool(false);
+
+ }
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DEBUG_Dump
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DEBUG_Dump") == 0) {
+ stack->correctParams(0);
+ if (_buffer && _size) {
+ warning("SXMemBuffer::ScCallMethod - DEBUG_Dump");
+ Common::DumpFile f;
+ f.open("buffer.bin");
+ f.write(_buffer, _size);
+ f.close();
+ }
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+ else return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CSXMemBuffer::scGetProperty(const char *name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Type") == 0) {
+ _scValue->setString("membuffer");
+ return _scValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Size (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Size") == 0) {
+ _scValue->setInt(_size);
+ return _scValue;
+ }
+
+ else return CBScriptable::scGetProperty(name);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXMemBuffer::scSetProperty(const char *name, CScValue *value) {
+ /*
+ //////////////////////////////////////////////////////////////////////////
+ // Length
+ //////////////////////////////////////////////////////////////////////////
+ if(strcmp(name, "Length")==0){
+ int OrigLength = _length;
+ _length = max(value->getInt(0), 0);
+
+ char PropName[20];
+ if(_length < OrigLength){
+ for(int i=_length; i<OrigLength; i++){
+ sprintf(PropName, "%d", i);
+ _values->DeleteProp(PropName);
+ }
+ }
+ return STATUS_OK;
+ }
+ else*/ return CBScriptable::scSetProperty(name, value);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXMemBuffer::persist(CBPersistMgr *persistMgr) {
+
+ CBScriptable::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_size));
+
+ if (persistMgr->_saving) {
+ if (_size > 0) persistMgr->putBytes((byte *)_buffer, _size);
+ } else {
+ if (_size > 0) {
+ _buffer = malloc(_size);
+ persistMgr->getBytes((byte *)_buffer, _size);
+ } else _buffer = NULL;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CSXMemBuffer::scCompare(CBScriptable *val) {
+ if (_buffer == val->scToMemBuffer()) return 0;
+ else return 1;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/base/scriptables/SXMemBuffer.h b/engines/wintermute/base/scriptables/SXMemBuffer.h
new file mode 100644
index 0000000000..09831bf464
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXMemBuffer.h
@@ -0,0 +1,59 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXMEMBUFFER_H
+#define WINTERMUTE_SXMEMBUFFER_H
+
+
+#include "engines/wintermute/base/BScriptable.h"
+
+namespace WinterMute {
+
+class CSXMemBuffer : public CBScriptable {
+public:
+ virtual int scCompare(CBScriptable *Val);
+ DECLARE_PERSISTENT(CSXMemBuffer, CBScriptable)
+ CScValue *scGetProperty(const char *name);
+ bool scSetProperty(const char *name, CScValue *value);
+ bool scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name);
+ const char *scToString();
+ CSXMemBuffer(CBGame *inGame, CScStack *stack);
+ CSXMemBuffer(CBGame *inGame, void *buffer);
+ virtual ~CSXMemBuffer();
+ virtual void *scToMemBuffer();
+ int _size;
+private:
+ bool resize(int newSize);
+ void *_buffer;
+ void cleanup();
+ bool checkBounds(CScScript *script, int start, int length);
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/SXString.cpp b/engines/wintermute/base/scriptables/SXString.cpp
new file mode 100644
index 0000000000..ed3d243cb0
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXString.cpp
@@ -0,0 +1,404 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/BGame.h"
+#include "engines/wintermute/base/scriptables/ScStack.h"
+#include "engines/wintermute/base/scriptables/ScValue.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/base/scriptables/SXString.h"
+#include "engines/wintermute/base/scriptables/SXArray.h"
+#include "engines/wintermute/utils/StringUtil.h"
+#include "common/tokenizer.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXString, false)
+
+CBScriptable *makeSXString(CBGame *inGame, CScStack *stack) {
+ return new CSXString(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+CSXString::CSXString(CBGame *inGame, CScStack *stack): CBScriptable(inGame) {
+ _string = NULL;
+ _capacity = 0;
+
+ stack->correctParams(1);
+ CScValue *val = stack->pop();
+
+ if (val->isInt()) {
+ _capacity = MAX(0, val->getInt());
+ if (_capacity > 0) {
+ _string = new char[_capacity];
+ memset(_string, 0, _capacity);
+ }
+ } else {
+ setStringVal(val->getString());
+ }
+
+ if (_capacity == 0) setStringVal("");
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXString::~CSXString() {
+ if (_string) delete [] _string;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CSXString::setStringVal(const char *val) {
+ int len = strlen(val);
+ if (len >= _capacity) {
+ _capacity = len + 1;
+ delete[] _string;
+ _string = NULL;
+ _string = new char[_capacity];
+ memset(_string, 0, _capacity);
+ }
+ strcpy(_string, val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *CSXString::scToString() {
+ if (_string) return _string;
+ else return "[null string]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CSXString::scSetString(const char *val) {
+ setStringVal(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXString::scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // Substring
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Substring") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+ int end = stack->pop()->getInt();
+
+ if (end < start) CBUtils::swap(&start, &end);
+
+ //try {
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ str = StringUtil::utf8ToWide(_string);
+ else
+ str = StringUtil::ansiToWide(_string);
+
+ //WideString subStr = str.substr(start, end - start + 1);
+ WideString subStr(str.c_str() + start, end - start + 1);
+
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ stack->pushString(StringUtil::wideToUtf8(subStr).c_str());
+ else
+ stack->pushString(StringUtil::wideToAnsi(subStr).c_str());
+ // } catch (std::exception &) {
+ // stack->pushNULL();
+ // }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Substr
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Substr") == 0) {
+ stack->correctParams(2);
+ int start = stack->pop()->getInt();
+
+ CScValue *val = stack->pop();
+ int len = val->getInt();
+
+ if (!val->isNULL() && len <= 0) {
+ stack->pushString("");
+ return STATUS_OK;
+ }
+
+ if (val->isNULL()) len = strlen(_string) - start;
+
+// try {
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ str = StringUtil::utf8ToWide(_string);
+ else
+ str = StringUtil::ansiToWide(_string);
+
+// WideString subStr = str.substr(start, len);
+ WideString subStr(str.c_str() + start, len);
+
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ stack->pushString(StringUtil::wideToUtf8(subStr).c_str());
+ else
+ stack->pushString(StringUtil::wideToAnsi(subStr).c_str());
+// } catch (std::exception &) {
+// stack->pushNULL();
+// }
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ToUpperCase
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ToUpperCase") == 0) {
+ stack->correctParams(0);
+
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ str = StringUtil::utf8ToWide(_string);
+ else
+ str = StringUtil::ansiToWide(_string);
+
+ str.toUppercase();
+
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ stack->pushString(StringUtil::wideToUtf8(str).c_str());
+ else
+ stack->pushString(StringUtil::wideToAnsi(str).c_str());
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ToLowerCase
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ToLowerCase") == 0) {
+ stack->correctParams(0);
+
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ str = StringUtil::utf8ToWide(_string);
+ else
+ str = StringUtil::ansiToWide(_string);
+
+ str.toLowercase();
+
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ stack->pushString(StringUtil::wideToUtf8(str).c_str());
+ else
+ stack->pushString(StringUtil::wideToAnsi(str).c_str());
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IndexOf
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IndexOf") == 0) {
+ stack->correctParams(2);
+
+ const char *strToFind = stack->pop()->getString();
+ int index = stack->pop()->getInt();
+
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ str = StringUtil::utf8ToWide(_string);
+ else
+ str = StringUtil::ansiToWide(_string);
+
+ WideString toFind;
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ toFind = StringUtil::utf8ToWide(strToFind);
+ else
+ toFind = StringUtil::ansiToWide(strToFind);
+
+ int indexOf = StringUtil::indexOf(str, toFind, index);
+ stack->pushInt(indexOf);
+
+ return STATUS_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Split
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Split") == 0) {
+ stack->correctParams(1);
+ CScValue *val = stack->pop();
+ char separators[MAX_PATH_LENGTH] = ",";
+ if (!val->isNULL()) strcpy(separators, val->getString());
+
+ CSXArray *array = new CSXArray(_gameRef);
+ if (!array) {
+ stack->pushNULL();
+ return STATUS_OK;
+ }
+
+
+ WideString str;
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ str = StringUtil::utf8ToWide(_string);
+ else
+ str = StringUtil::ansiToWide(_string);
+
+ WideString delims;
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ delims = StringUtil::utf8ToWide(separators);
+ else
+ delims = StringUtil::ansiToWide(separators);
+
+ Common::Array<WideString> parts;
+
+
+
+ Common::StringTokenizer tokenizer(str, delims);
+ while (!tokenizer.empty()) {
+ Common::String str2 = tokenizer.nextToken();
+ parts.push_back(str2);
+ }
+ // TODO: Clean this up
+ /*do {
+ pos = StringUtil::IndexOf(Common::String(str.c_str() + start), delims, start);
+ //pos = str.find_first_of(delims, start);
+ if (pos == start) {
+ start = pos + 1;
+ } else if (pos == str.size()) {
+ parts.push_back(Common::String(str.c_str() + start));
+ break;
+ } else {
+ parts.push_back(Common::String(str.c_str() + start, pos - start));
+ start = pos + 1;
+ }
+ //start = str.find_first_not_of(delims, start);
+ start = StringUtil::LastIndexOf(Common::String(str.c_str() + start), delims, start) + 1;
+
+ } while (pos != str.size());*/
+
+ for (Common::Array<WideString>::iterator it = parts.begin(); it != parts.end(); ++it) {
+ WideString &part = (*it);
+
+ if (_gameRef->_textEncoding == TEXT_UTF8)
+ val = new CScValue(_gameRef, StringUtil::wideToUtf8(part).c_str());
+ else
+ val = new CScValue(_gameRef, StringUtil::wideToAnsi(part).c_str());
+
+ array->push(val);
+ delete val;
+ val = NULL;
+ }
+
+ stack->pushNative(array, false);
+ return STATUS_OK;
+ }
+
+ else return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CSXString::scGetProperty(const char *name) {
+ _scValue->setNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Type") == 0) {
+ _scValue->setString("string");
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Length (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Length") == 0) {
+ if (_gameRef->_textEncoding == TEXT_UTF8) {
+ WideString wstr = StringUtil::utf8ToWide(_string);
+ _scValue->setInt(wstr.size());
+ } else
+ _scValue->setInt(strlen(_string));
+
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Capacity
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Capacity") == 0) {
+ _scValue->setInt(_capacity);
+ return _scValue;
+ }
+
+ else return _scValue;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXString::scSetProperty(const char *name, CScValue *value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Capacity
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Capacity") == 0) {
+ int32 newCap = (uint32)value->getInt();
+ if (newCap < (int32)(strlen(_string) + 1)) _gameRef->LOG(0, "Warning: cannot lower string capacity");
+ else if (newCap != _capacity) {
+ char *newStr = new char[newCap];
+ if (newStr) {
+ memset(newStr, 0, newCap);
+ strcpy(newStr, _string);
+ delete[] _string;
+ _string = newStr;
+ _capacity = newCap;
+ }
+ }
+ return STATUS_OK;
+ }
+
+ else return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXString::persist(CBPersistMgr *persistMgr) {
+
+ CBScriptable::persist(persistMgr);
+
+ persistMgr->transfer(TMEMBER(_capacity));
+
+ if (persistMgr->_saving) {
+ if (_capacity > 0) persistMgr->putBytes((byte *)_string, _capacity);
+ } else {
+ if (_capacity > 0) {
+ _string = new char[_capacity];
+ persistMgr->getBytes((byte *)_string, _capacity);
+ } else _string = NULL;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CSXString::scCompare(CBScriptable *val) {
+ return strcmp(_string, ((CSXString *)val)->_string);
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/base/scriptables/SXString.h b/engines/wintermute/base/scriptables/SXString.h
new file mode 100644
index 0000000000..348595ad29
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SXString.h
@@ -0,0 +1,58 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXSTRING_H
+#define WINTERMUTE_SXSTRING_H
+
+
+#include "engines/wintermute/base/BScriptable.h"
+
+namespace WinterMute {
+
+class CSXString : public CBScriptable {
+public:
+ virtual int scCompare(CBScriptable *Val);
+ DECLARE_PERSISTENT(CSXString, CBScriptable)
+ CScValue *scGetProperty(const char *name);
+ bool scSetProperty(const char *name, CScValue *value);
+ bool scCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name);
+ void scSetString(const char *val);
+ const char *scToString();
+ void setStringVal(const char *val);
+
+ CSXString(CBGame *inGame, CScStack *Stack);
+ virtual ~CSXString();
+
+private:
+ char *_string;
+ int _capacity;
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/ScEngine.cpp b/engines/wintermute/base/scriptables/ScEngine.cpp
new file mode 100644
index 0000000000..db79a7d0e9
--- /dev/null
+++ b/engines/wintermute/base/scriptables/ScEngine.cpp
@@ -0,0 +1,712 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/dcgf.h"
+#include "engines/wintermute/base/scriptables/ScEngine.h"
+#include "engines/wintermute/utils/StringUtil.h"
+#include "engines/wintermute/base/scriptables/ScValue.h"
+#include "engines/wintermute/base/scriptables/ScScript.h"
+#include "engines/wintermute/base/scriptables/ScStack.h"
+#include "engines/wintermute/base/scriptables/SXMath.h"
+#include "engines/wintermute/base/BRegistry.h"
+#include "engines/wintermute/base/BGame.h"
+#include "engines/wintermute/base/BSound.h"
+#include "engines/wintermute/base/BFileManager.h"
+
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CScEngine, true)
+
+#define COMPILER_DLL "dcscomp.dll"
+//////////////////////////////////////////////////////////////////////////
+CScEngine::CScEngine(CBGame *inGame): CBBase(inGame) {
+ _gameRef->LOG(0, "Initializing scripting engine...");
+
+ if (_compilerAvailable) _gameRef->LOG(0, " Script compiler bound successfuly");
+ else _gameRef->LOG(0, " Script compiler is NOT available");
+
+ _globals = new CScValue(_gameRef);
+
+
+ // register 'Game' as global variable
+ if (!_globals->propExists("Game")) {
+ CScValue val(_gameRef);
+ val.setNative(_gameRef, true);
+ _globals->setProp("Game", &val);
+ }
+
+ // register 'Math' as global variable
+ if (!_globals->propExists("Math")) {
+ CScValue val(_gameRef);
+ val.setNative(_gameRef->_mathClass, true);
+ _globals->setProp("Math", &val);
+ }
+
+ // prepare script cache
+ for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) _cachedScripts[i] = NULL;
+
+ _currentScript = NULL;
+
+ _isProfiling = false;
+ _profilingStartTime = 0;
+
+ //EnableProfiling();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScEngine::~CScEngine() {
+ _gameRef->LOG(0, "Shutting down scripting engine");
+ saveBreakpoints();
+
+ disableProfiling();
+
+ cleanup();
+
+ for (int i = 0; i < _breakpoints.getSize(); i++) {
+ delete _breakpoints[i];
+ _breakpoints[i] = NULL;
+ }
+ _breakpoints.removeAll();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::cleanup() {
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ if (!_scripts[i]->_thread && _scripts[i]->_owner) _scripts[i]->_owner->removeScript(_scripts[i]);
+ delete _scripts[i];
+ _scripts.removeAt(i);
+ i--;
+ }
+
+ _scripts.removeAll();
+
+ delete _globals;
+ _globals = NULL;
+
+ emptyScriptCache();
+
+ _currentScript = NULL; // ref only
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+byte *CScEngine::loadFile(void *data, char *filename, uint32 *size) {
+ CBGame *gameRef = (CBGame *)data;
+ return gameRef->_fileManager->readWholeFile(filename, size);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::closeFile(void *data, byte *buffer) {
+ delete [] buffer;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::parseElement(void *data, int line, int type, void *elementData) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScScript *CScEngine::runScript(const char *filename, CBScriptHolder *owner) {
+ byte *compBuffer;
+ uint32 compSize;
+
+ // get script from cache
+ compBuffer = getCompiledScript(filename, &compSize);
+ if (!compBuffer) return NULL;
+
+ // add new script
+ CScScript *script = new CScScript(_gameRef, this);
+ bool ret = script->create(filename, compBuffer, compSize, owner);
+ if (DID_FAIL(ret)) {
+ _gameRef->LOG(ret, "Error running script '%s'...", filename);
+ delete script;
+ return NULL;
+ } else {
+ // publish the "self" pseudo-variable
+ CScValue val(_gameRef);
+ if (owner)val.setNative(owner, true);
+ else val.setNULL();
+
+ script->_globals->setProp("self", &val);
+ script->_globals->setProp("this", &val);
+
+ _scripts.add(script);
+ _gameRef->getDebugMgr()->onScriptInit(script);
+
+ return script;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+byte *CScEngine::getCompiledScript(const char *filename, uint32 *outSize, bool ignoreCache) {
+ // is script in cache?
+ if (!ignoreCache) {
+ for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) {
+ if (_cachedScripts[i] && scumm_stricmp(_cachedScripts[i]->_filename.c_str(), filename) == 0) {
+ _cachedScripts[i]->_timestamp = CBPlatform::getTime();
+ *outSize = _cachedScripts[i]->_size;
+ return _cachedScripts[i]->_buffer;
+ }
+ }
+ }
+
+ // nope, load it
+ byte *compBuffer;
+ uint32 compSize;
+
+ uint32 size;
+
+ byte *buffer = _gameRef->_fileManager->readWholeFile(filename, &size);
+ if (!buffer) {
+ _gameRef->LOG(0, "CScEngine::GetCompiledScript - error opening script '%s'", filename);
+ return NULL;
+ }
+
+ // needs to be compiled?
+ if (FROM_LE_32(*(uint32 *)buffer) == SCRIPT_MAGIC) {
+ compBuffer = buffer;
+ compSize = size;
+ } else {
+ if (!_compilerAvailable) {
+ _gameRef->LOG(0, "CScEngine::GetCompiledScript - script '%s' needs to be compiled but compiler is not available", filename);
+ delete [] buffer;
+ return NULL;
+ }
+ // This code will never be called, since _compilerAvailable is const false.
+ // It's only here in the event someone would want to reinclude the compiler.
+ error("Script needs compilation, ScummVM does not contain a WME compiler");
+ }
+
+ byte *ret = NULL;
+
+ // add script to cache
+ CScCachedScript *cachedScript = new CScCachedScript(filename, compBuffer, compSize);
+ if (cachedScript) {
+ int index = 0;
+ uint32 MinTime = CBPlatform::getTime();
+ for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) {
+ if (_cachedScripts[i] == NULL) {
+ index = i;
+ break;
+ } else if (_cachedScripts[i]->_timestamp <= MinTime) {
+ MinTime = _cachedScripts[i]->_timestamp;
+ index = i;
+ }
+ }
+
+ if (_cachedScripts[index] != NULL) delete _cachedScripts[index];
+ _cachedScripts[index] = cachedScript;
+
+ ret = cachedScript->_buffer;
+ *outSize = cachedScript->_size;
+ }
+
+
+ // cleanup
+ delete [] buffer;
+
+ return ret;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::tick() {
+ if (_scripts.getSize() == 0)
+ return STATUS_OK;
+
+
+ // resolve waiting scripts
+ for (int i = 0; i < _scripts.getSize(); i++) {
+
+ switch (_scripts[i]->_state) {
+ case SCRIPT_WAITING: {
+ /*
+ bool obj_found=false;
+ for(int j=0; j<_gameRef->_regObjects.getSize(); j++)
+ {
+ if(_gameRef->_regObjects[j] == _scripts[i]->_waitObject)
+ {
+ if(_gameRef->_regObjects[j]->IsReady()) _scripts[i]->Run();
+ obj_found = true;
+ break;
+ }
+ }
+ if(!obj_found) _scripts[i]->finish(); // _waitObject no longer exists
+ */
+ if (_gameRef->validObject(_scripts[i]->_waitObject)) {
+ if (_scripts[i]->_waitObject->isReady()) _scripts[i]->run();
+ } else _scripts[i]->finish();
+ break;
+ }
+
+ case SCRIPT_SLEEPING: {
+ if (_scripts[i]->_waitFrozen) {
+ if (_scripts[i]->_waitTime <= CBPlatform::getTime()) _scripts[i]->run();
+ } else {
+ if (_scripts[i]->_waitTime <= _gameRef->_timer) _scripts[i]->run();
+ }
+ break;
+ }
+
+ case SCRIPT_WAITING_SCRIPT: {
+ if (!isValidScript(_scripts[i]->_waitScript) || _scripts[i]->_waitScript->_state == SCRIPT_ERROR) {
+ // fake return value
+ _scripts[i]->_stack->pushNULL();
+ _scripts[i]->_waitScript = NULL;
+ _scripts[i]->run();
+ } else {
+ if (_scripts[i]->_waitScript->_state == SCRIPT_THREAD_FINISHED) {
+ // copy return value
+ _scripts[i]->_stack->push(_scripts[i]->_waitScript->_stack->pop());
+ _scripts[i]->run();
+ _scripts[i]->_waitScript->finish();
+ _scripts[i]->_waitScript = NULL;
+ }
+ }
+ break;
+ }
+ default:
+ //warning("CScEngine::Tick - Unhandled enum");
+ break;
+ } // switch
+ } // for each script
+
+
+ // execute scripts
+ for (int i = 0; i < _scripts.getSize(); i++) {
+
+ // skip paused scripts
+ if (_scripts[i]->_state == SCRIPT_PAUSED) continue;
+
+ // time sliced script
+ if (_scripts[i]->_timeSlice > 0) {
+ uint32 StartTime = CBPlatform::getTime();
+ while (_scripts[i]->_state == SCRIPT_RUNNING && CBPlatform::getTime() - StartTime < _scripts[i]->_timeSlice) {
+ _currentScript = _scripts[i];
+ _scripts[i]->executeInstruction();
+ }
+ if (_isProfiling && _scripts[i]->_filename) addScriptTime(_scripts[i]->_filename, CBPlatform::getTime() - StartTime);
+ }
+
+ // normal script
+ else {
+ uint32 startTime = 0;
+ bool isProfiling = _isProfiling;
+ if (isProfiling) startTime = CBPlatform::getTime();
+
+ while (_scripts[i]->_state == SCRIPT_RUNNING) {
+ _currentScript = _scripts[i];
+ _scripts[i]->executeInstruction();
+ }
+ if (isProfiling && _scripts[i]->_filename) addScriptTime(_scripts[i]->_filename, CBPlatform::getTime() - startTime);
+ }
+ _currentScript = NULL;
+ }
+
+ removeFinishedScripts();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::tickUnbreakable() {
+ // execute unbreakable scripts
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ if (!_scripts[i]->_unbreakable) continue;
+
+ while (_scripts[i]->_state == SCRIPT_RUNNING) {
+ _currentScript = _scripts[i];
+ _scripts[i]->executeInstruction();
+ }
+ _scripts[i]->finish();
+ _currentScript = NULL;
+ }
+ removeFinishedScripts();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::removeFinishedScripts() {
+ // remove finished scripts
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ if (_scripts[i]->_state == SCRIPT_FINISHED || _scripts[i]->_state == SCRIPT_ERROR) {
+ if (!_scripts[i]->_thread && _scripts[i]->_owner) _scripts[i]->_owner->removeScript(_scripts[i]);
+ _gameRef->getDebugMgr()->onScriptShutdown(_scripts[i]);
+ delete _scripts[i];
+ _scripts.removeAt(i);
+ i--;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CScEngine::getNumScripts(int *running, int *waiting, int *persistent) {
+ int numRunning = 0, numWaiting = 0, numPersistent = 0, numTotal = 0;
+
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ if (_scripts[i]->_state == SCRIPT_FINISHED) continue;
+ switch (_scripts[i]->_state) {
+ case SCRIPT_RUNNING:
+ case SCRIPT_SLEEPING:
+ case SCRIPT_PAUSED:
+ numRunning++;
+ break;
+ case SCRIPT_WAITING:
+ numWaiting++;
+ break;
+ case SCRIPT_PERSISTENT:
+ numPersistent++;
+ break;
+ default:
+ warning("CScEngine::GetNumScripts - unhandled enum");
+ break;
+ }
+ numTotal++;
+ }
+ if (running) *running = numRunning;
+ if (waiting) *waiting = numWaiting;
+ if (persistent) *persistent = numPersistent;
+
+ return numTotal;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::emptyScriptCache() {
+ for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) {
+ if (_cachedScripts[i]) {
+ delete _cachedScripts[i];
+ _cachedScripts[i] = NULL;
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::resetObject(CBObject *Object) {
+ // terminate all scripts waiting for this object
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ if (_scripts[i]->_state == SCRIPT_WAITING && _scripts[i]->_waitObject == Object) {
+ if (!_gameRef->_compatKillMethodThreads) resetScript(_scripts[i]);
+
+ bool IsThread = _scripts[i]->_methodThread || _scripts[i]->_thread;
+ _scripts[i]->finish(!IsThread); // 1.9b1 - top-level script kills its threads as well
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::resetScript(CScScript *script) {
+ // terminate all scripts waiting for this script
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ if (_scripts[i]->_state == SCRIPT_WAITING_SCRIPT && _scripts[i]->_waitScript == script) {
+ _scripts[i]->finish();
+ }
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::persist(CBPersistMgr *persistMgr) {
+ if (!persistMgr->_saving) cleanup();
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+ persistMgr->transfer(TMEMBER(_currentScript));
+ persistMgr->transfer(TMEMBER(_globals));
+ _scripts.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::editorCleanup() {
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ if (_scripts[i]->_owner == NULL && (_scripts[i]->_state == SCRIPT_FINISHED || _scripts[i]->_state == SCRIPT_ERROR)) {
+ delete _scripts[i];
+ _scripts.removeAt(i);
+ i--;
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::pauseAll() {
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ if (_scripts[i] != _currentScript) _scripts[i]->pause();
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::resumeAll() {
+ for (int i = 0; i < _scripts.getSize(); i++)
+ _scripts[i]->resume();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::isValidScript(CScScript *script) {
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ if (_scripts[i] == script) return true;
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::clearGlobals(bool includingNatives) {
+ _globals->CleanProps(includingNatives);
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::dbgSendScripts(IWmeDebugClient *client) {
+ // send global variables
+ _globals->dbgSendVariables(client, WME_DBGVAR_GLOBAL, NULL, 0);
+
+ // process normal scripts first
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ if (_scripts[i]->_thread || _scripts[i]->_methodThread) continue;
+ _scripts[i]->dbgSendScript(client);
+ }
+
+ // and threads later
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ if (_scripts[i]->_thread || _scripts[i]->_methodThread)
+ _scripts[i]->dbgSendScript(client);
+ }
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::addBreakpoint(const char *scriptFilename, int line) {
+ if (!_gameRef->getDebugMgr()->_enabled) return STATUS_OK;
+
+ CScBreakpoint *bp = NULL;
+ for (int i = 0; i < _breakpoints.getSize(); i++) {
+ if (scumm_stricmp(_breakpoints[i]->_filename.c_str(), scriptFilename) == 0) {
+ bp = _breakpoints[i];
+ break;
+ }
+ }
+ if (bp == NULL) {
+ bp = new CScBreakpoint(scriptFilename);
+ _breakpoints.add(bp);
+ }
+
+ for (int i = 0; i < bp->_lines.getSize(); i++) {
+ if (bp->_lines[i] == line) return STATUS_OK;
+ }
+ bp->_lines.add(line);
+
+ // refresh changes
+ refreshScriptBreakpoints();
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::removeBreakpoint(const char *scriptFilename, int line) {
+ if (!_gameRef->getDebugMgr()->_enabled) return STATUS_OK;
+
+ for (int i = 0; i < _breakpoints.getSize(); i++) {
+ if (scumm_stricmp(_breakpoints[i]->_filename.c_str(), scriptFilename) == 0) {
+ for (int j = 0; j < _breakpoints[i]->_lines.getSize(); j++) {
+ if (_breakpoints[i]->_lines[j] == line) {
+ _breakpoints[i]->_lines.removeAt(j);
+ if (_breakpoints[i]->_lines.getSize() == 0) {
+ delete _breakpoints[i];
+ _breakpoints.removeAt(i);
+ }
+ // refresh changes
+ refreshScriptBreakpoints();
+
+ return STATUS_OK;
+ }
+ }
+ break;
+ }
+ }
+ return STATUS_FAILED;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::refreshScriptBreakpoints() {
+ if (!_gameRef->getDebugMgr()->_enabled) return STATUS_OK;
+
+ for (int i = 0; i < _scripts.getSize(); i++) {
+ refreshScriptBreakpoints(_scripts[i]);
+ }
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::refreshScriptBreakpoints(CScScript *script) {
+ if (!_gameRef->getDebugMgr()->_enabled) return STATUS_OK;
+
+ if (!script || !script->_filename) return STATUS_FAILED;
+
+ for (int i = 0; i < _breakpoints.getSize(); i++) {
+ if (scumm_stricmp(_breakpoints[i]->_filename.c_str(), script->_filename) == 0) {
+ script->_breakpoints.copy(_breakpoints[i]->_lines);
+ return STATUS_OK;
+ }
+ }
+ if (script->_breakpoints.getSize() > 0) script->_breakpoints.removeAll();
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::saveBreakpoints() {
+ if (!_gameRef->getDebugMgr()->_enabled) return STATUS_OK;
+
+
+ char text[512];
+ char key[100];
+
+ int count = 0;
+ for (int i = 0; i < _breakpoints.getSize(); i++) {
+ for (int j = 0; j < _breakpoints[i]->_lines.getSize(); j++) {
+ count++;
+ sprintf(key, "Breakpoint%d", count);
+ sprintf(text, "%s:%d", _breakpoints[i]->_filename.c_str(), _breakpoints[i]->_lines[j]);
+
+ _gameRef->_registry->writeString("Debug", key, text);
+ }
+ }
+ _gameRef->_registry->writeInt("Debug", "NumBreakpoints", count);
+
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::loadBreakpoints() {
+ if (!_gameRef->getDebugMgr()->_enabled) return STATUS_OK;
+
+ char key[100];
+
+ int count = _gameRef->_registry->readInt("Debug", "NumBreakpoints", 0);
+ for (int i = 1; i <= count; i++) {
+ /* uint32 BufSize = 512; */
+ sprintf(key, "Breakpoint%d", i);
+ AnsiString breakpoint = _gameRef->_registry->readString("Debug", key, "");
+
+ char *path = CBUtils::strEntry(0, breakpoint.c_str(), ':');
+ char *line = CBUtils::strEntry(1, breakpoint.c_str(), ':');
+
+ if (path != NULL && line != NULL) addBreakpoint(path, atoi(line));
+ delete[] path;
+ delete[] line;
+ path = NULL;
+ line = NULL;
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::addScriptTime(const char *filename, uint32 time) {
+ if (!_isProfiling) return;
+
+ AnsiString fileName = filename;
+ fileName.toLowercase();
+ _scriptTimes[fileName] += time;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::enableProfiling() {
+ if (_isProfiling) return;
+
+ // destroy old data, if any
+ _scriptTimes.clear();
+
+ _profilingStartTime = CBPlatform::getTime();
+ _isProfiling = true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::disableProfiling() {
+ if (!_isProfiling) return;
+
+ dumpStats();
+ _isProfiling = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::dumpStats() {
+ error("DumpStats not ported to ScummVM yet");
+ /* uint32 totalTime = CBPlatform::getTime() - _profilingStartTime;
+
+ typedef std::vector <std::pair<uint32, std::string> > TimeVector;
+ TimeVector times;
+
+ ScriptTimes::iterator it;
+ for (it = _scriptTimes.begin(); it != _scriptTimes.end(); it++) {
+ times.push_back(std::pair<uint32, std::string> (it->_value, it->_key));
+ }
+ std::sort(times.begin(), times.end());
+
+
+ TimeVector::reverse_iterator tit;
+
+ _gameRef->LOG(0, "***** Script profiling information: *****");
+ _gameRef->LOG(0, " %-40s %fs", "Total execution time", (float)totalTime / 1000);
+
+ for (tit = times.rbegin(); tit != times.rend(); tit++) {
+ _gameRef->LOG(0, " %-40s %fs (%f%%)", tit->second.c_str(), (float)tit->first / 1000, (float)tit->first / (float)totalTime * 100);
+ }*/
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/base/scriptables/ScEngine.h b/engines/wintermute/base/scriptables/ScEngine.h
new file mode 100644
index 0000000000..df327d800c
--- /dev/null
+++ b/engines/wintermute/base/scriptables/ScEngine.h
@@ -0,0 +1,147 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SCENGINE_H
+#define WINTERMUTE_SCENGINE_H
+
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/coll_templ.h"
+#include "engines/wintermute/base/BBase.h"
+#include "engines/wintermute/wme_debugger.h"
+#include "engines/wintermute/utils/utils.h"
+#include "engines/wintermute/PlatformSDL.h"
+
+namespace WinterMute {
+
+#define MAX_CACHED_SCRIPTS 20
+class CScScript;
+class CScValue;
+class CBObject;
+class CBScriptHolder;
+class CScEngine : public CBBase {
+public:
+ class CScCachedScript {
+ public:
+ CScCachedScript(const char *filename, byte *buffer, uint32 size) {
+ _timestamp = CBPlatform::getTime();
+ _buffer = new byte[size];
+ if (_buffer) memcpy(_buffer, buffer, size);
+ _size = size;
+ _filename = filename;
+ };
+
+ ~CScCachedScript() {
+ if (_buffer) delete [] _buffer;
+ };
+
+ uint32 _timestamp;
+ byte *_buffer;
+ uint32 _size;
+ Common::String _filename;
+ };
+
+ class CScBreakpoint {
+ public:
+ CScBreakpoint(const char *filename) {
+ _filename = filename;
+ }
+
+ ~CScBreakpoint() {
+ _lines.removeAll();
+ }
+
+ Common::String _filename;
+ CBArray<int, int> _lines;
+ };
+
+
+
+
+public:
+ bool dbgSendScripts(IWmeDebugClient *client);
+
+ CBArray<CScBreakpoint *, CScBreakpoint *> _breakpoints;
+ bool addBreakpoint(const char *scriptFilename, int line);
+ bool removeBreakpoint(const char *scriptFilename, int line);
+ bool refreshScriptBreakpoints();
+ bool refreshScriptBreakpoints(CScScript *script);
+ bool saveBreakpoints();
+ bool loadBreakpoints();
+
+ bool clearGlobals(bool includingNatives = false);
+ bool tickUnbreakable();
+ bool removeFinishedScripts();
+ bool isValidScript(CScScript *script);
+
+ CScScript *_currentScript;
+ bool resumeAll();
+ bool pauseAll();
+ void editorCleanup();
+ bool resetObject(CBObject *Object);
+ bool resetScript(CScScript *script);
+ bool emptyScriptCache();
+ byte *getCompiledScript(const char *filename, uint32 *outSize, bool ignoreCache = false);
+ DECLARE_PERSISTENT(CScEngine, CBBase)
+ bool cleanup();
+ int getNumScripts(int *running = NULL, int *waiting = NULL, int *persistent = NULL);
+ bool tick();
+ CScValue *_globals;
+ CScScript *runScript(const char *filename, CBScriptHolder *owner = NULL);
+ static const bool _compilerAvailable = false;
+
+ CScEngine(CBGame *inGame);
+ virtual ~CScEngine();
+ static byte *loadFile(void *data, char *filename, uint32 *size);
+ static void closeFile(void *data, byte *buffer);
+ static void parseElement(void *data, int line, int type, void *elementData);
+
+ CBArray<CScScript *, CScScript *> _scripts;
+
+ void enableProfiling();
+ void disableProfiling();
+ bool getIsProfiling() {
+ return _isProfiling;
+ }
+
+ void addScriptTime(const char *filename, uint32 Time);
+ void dumpStats();
+
+private:
+
+ CScCachedScript *_cachedScripts[MAX_CACHED_SCRIPTS];
+ bool _isProfiling;
+ uint32 _profilingStartTime;
+
+ typedef Common::HashMap<Common::String, uint32> ScriptTimes;
+ ScriptTimes _scriptTimes;
+
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/ScScript.cpp b/engines/wintermute/base/scriptables/ScScript.cpp
new file mode 100644
index 0000000000..0b5b3c24bf
--- /dev/null
+++ b/engines/wintermute/base/scriptables/ScScript.cpp
@@ -0,0 +1,1461 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/dcgf.h"
+#include "engines/wintermute/base/scriptables/ScValue.h"
+#include "engines/wintermute/base/scriptables/ScScript.h"
+#include "engines/wintermute/base/BGame.h"
+#include "engines/wintermute/base/scriptables/ScEngine.h"
+#include "engines/wintermute/base/scriptables/ScStack.h"
+#include "common/memstream.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CScScript, false)
+
+//////////////////////////////////////////////////////////////////////////
+CScScript::CScScript(CBGame *inGame, CScEngine *Engine): CBBase(inGame) {
+ _buffer = NULL;
+ _bufferSize = _iP = 0;
+ _scriptStream = NULL;
+ _filename = NULL;
+ _currentLine = 0;
+
+ _symbols = NULL;
+ _numSymbols = 0;
+
+ _engine = Engine;
+
+ _globals = NULL;
+
+ _scopeStack = NULL;
+ _callStack = NULL;
+ _thisStack = NULL;
+ _stack = NULL;
+
+ _operand = NULL;
+ _reg1 = NULL;
+
+ _functions = NULL;
+ _numFunctions = 0;
+
+ _methods = NULL;
+ _numMethods = 0;
+
+ _events = NULL;
+ _numEvents = 0;
+
+ _externals = NULL;
+ _numExternals = 0;
+
+ _state = SCRIPT_FINISHED;
+ _origState = SCRIPT_FINISHED;
+
+ _waitObject = NULL;
+ _waitTime = 0;
+ _waitFrozen = false;
+ _waitScript = NULL;
+
+ _timeSlice = 0;
+
+ _thread = false;
+ _methodThread = false;
+ _threadEvent = NULL;
+
+ _freezable = true;
+ _owner = NULL;
+
+ _unbreakable = false;
+ _parentScript = NULL;
+
+ _tracingMode = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScScript::~CScScript() {
+ cleanup();
+}
+
+void CScScript::readHeader() {
+ uint32 oldPos = _scriptStream->pos();
+ _scriptStream->seek(0);
+ _header.magic = _scriptStream->readUint32LE();
+ _header.version = _scriptStream->readUint32LE();
+ _header.code_start = _scriptStream->readUint32LE();
+ _header.func_table = _scriptStream->readUint32LE();
+ _header.symbol_table = _scriptStream->readUint32LE();
+ _header.event_table = _scriptStream->readUint32LE();
+ _header.externals_table = _scriptStream->readUint32LE();
+ _header.method_table = _scriptStream->readUint32LE();
+ _scriptStream->seek(oldPos);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::initScript() {
+ if (!_scriptStream) {
+ _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize);
+ }
+ readHeader();
+
+ if (_header.magic != SCRIPT_MAGIC) {
+ _gameRef->LOG(0, "File '%s' is not a valid compiled script", _filename);
+ cleanup();
+ return STATUS_FAILED;
+ }
+
+ if (_header.version > SCRIPT_VERSION) {
+ _gameRef->LOG(0, "Script '%s' has a wrong version %d.%d (expected %d.%d)", _filename, _header.version / 256, _header.version % 256, SCRIPT_VERSION / 256, SCRIPT_VERSION % 256);
+ cleanup();
+ return STATUS_FAILED;
+ }
+
+ initTables();
+
+ // init stacks
+ _scopeStack = new CScStack(_gameRef);
+ _callStack = new CScStack(_gameRef);
+ _thisStack = new CScStack(_gameRef);
+ _stack = new CScStack(_gameRef);
+
+ _operand = new CScValue(_gameRef);
+ _reg1 = new CScValue(_gameRef);
+
+
+ // skip to the beginning
+ _iP = _header.code_start;
+ _scriptStream->seek(_iP);
+ _currentLine = 0;
+
+ // init breakpoints
+ _engine->refreshScriptBreakpoints(this);
+
+
+ // ready to rumble...
+ _state = SCRIPT_RUNNING;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::initTables() {
+ uint32 OrigIP = _iP;
+
+ readHeader();
+ // load symbol table
+ _iP = _header.symbol_table;
+
+ _numSymbols = getDWORD();
+ _symbols = new char*[_numSymbols];
+ for (uint32 i = 0; i < _numSymbols; i++) {
+ uint32 index = getDWORD();
+ _symbols[index] = getString();
+ }
+
+ // load functions table
+ _iP = _header.func_table;
+
+ _numFunctions = getDWORD();
+ _functions = new TFunctionPos[_numFunctions];
+ for (uint32 i = 0; i < _numFunctions; i++) {
+ _functions[i].pos = getDWORD();
+ _functions[i].name = getString();
+ }
+
+
+ // load events table
+ _iP = _header.event_table;
+
+ _numEvents = getDWORD();
+ _events = new TEventPos[_numEvents];
+ for (uint32 i = 0; i < _numEvents; i++) {
+ _events[i].pos = getDWORD();
+ _events[i].name = getString();
+ }
+
+
+ // load externals
+ if (_header.version >= 0x0101) {
+ _iP = _header.externals_table;
+
+ _numExternals = getDWORD();
+ _externals = new TExternalFunction[_numExternals];
+ for (uint32 i = 0; i < _numExternals; i++) {
+ _externals[i].dll_name = getString();
+ _externals[i].name = getString();
+ _externals[i].call_type = (TCallType)getDWORD();
+ _externals[i].returns = (TExternalType)getDWORD();
+ _externals[i].nu_params = getDWORD();
+ if (_externals[i].nu_params > 0) {
+ _externals[i].params = new TExternalType[_externals[i].nu_params];
+ for (int j = 0; j < _externals[i].nu_params; j++) {
+ _externals[i].params[j] = (TExternalType)getDWORD();
+ }
+ }
+ }
+ }
+
+ // load method table
+ _iP = _header.method_table;
+
+ _numMethods = getDWORD();
+ _methods = new TMethodPos[_numMethods];
+ for (uint32 i = 0; i < _numMethods; i++) {
+ _methods[i].pos = getDWORD();
+ _methods[i].name = getString();
+ }
+
+
+ _iP = OrigIP;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::create(const char *filename, byte *buffer, uint32 size, CBScriptHolder *owner) {
+ cleanup();
+
+ _thread = false;
+ _methodThread = false;
+
+ delete[] _threadEvent;
+ _threadEvent = NULL;
+
+ _filename = new char[strlen(filename) + 1];
+ if (_filename) strcpy(_filename, filename);
+
+ _buffer = new byte [size];
+ if (!_buffer) return STATUS_FAILED;
+
+ memcpy(_buffer, buffer, size);
+
+ _bufferSize = size;
+
+ bool res = initScript();
+ if (DID_FAIL(res)) return res;
+
+ // establish global variables table
+ _globals = new CScValue(_gameRef);
+
+ _owner = owner;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::createThread(CScScript *original, uint32 initIP, const char *eventName) {
+ cleanup();
+
+ _thread = true;
+ _methodThread = false;
+ _threadEvent = new char[strlen(eventName) + 1];
+ if (_threadEvent) strcpy(_threadEvent, eventName);
+
+ // copy filename
+ _filename = new char[strlen(original->_filename) + 1];
+ if (_filename) strcpy(_filename, original->_filename);
+
+ // copy buffer
+ _buffer = new byte [original->_bufferSize];
+ if (!_buffer) return STATUS_FAILED;
+
+ memcpy(_buffer, original->_buffer, original->_bufferSize);
+ _bufferSize = original->_bufferSize;
+
+ // initialize
+ bool res = initScript();
+ if (DID_FAIL(res)) return res;
+
+ // copy globals
+ _globals = original->_globals;
+
+ // skip to the beginning of the event
+ _iP = initIP;
+ _scriptStream->seek(_iP);
+
+ _timeSlice = original->_timeSlice;
+ _freezable = original->_freezable;
+ _owner = original->_owner;
+
+ _engine = original->_engine;
+ _parentScript = original;
+
+ return STATUS_OK;
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::createMethodThread(CScScript *original, const char *methodName) {
+ uint32 ip = original->getMethodPos(methodName);
+ if (ip == 0) return STATUS_FAILED;
+
+ cleanup();
+
+ _thread = true;
+ _methodThread = true;
+ _threadEvent = new char[strlen(methodName) + 1];
+ if (_threadEvent) strcpy(_threadEvent, methodName);
+
+ // copy filename
+ _filename = new char[strlen(original->_filename) + 1];
+ if (_filename) strcpy(_filename, original->_filename);
+
+ // copy buffer
+ _buffer = new byte [original->_bufferSize];
+ if (!_buffer) return STATUS_FAILED;
+
+ memcpy(_buffer, original->_buffer, original->_bufferSize);
+ _bufferSize = original->_bufferSize;
+
+ // initialize
+ bool res = initScript();
+ if (DID_FAIL(res)) return res;
+
+ // copy globals
+ _globals = original->_globals;
+
+ // skip to the beginning of the event
+ _iP = ip;
+
+ _timeSlice = original->_timeSlice;
+ _freezable = original->_freezable;
+ _owner = original->_owner;
+
+ _engine = original->_engine;
+ _parentScript = original;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScScript::cleanup() {
+ if (_buffer) delete [] _buffer;
+ _buffer = NULL;
+
+ if (_filename) delete [] _filename;
+ _filename = NULL;
+
+ if (_symbols) delete [] _symbols;
+ _symbols = NULL;
+ _numSymbols = 0;
+
+ if (_globals && !_thread) delete _globals;
+ _globals = NULL;
+
+ delete _scopeStack;
+ _scopeStack = NULL;
+
+ delete _callStack;
+ _callStack = NULL;
+
+ delete _thisStack;
+ _thisStack = NULL;
+
+ delete _stack;
+ _stack = NULL;
+
+ if (_functions) delete [] _functions;
+ _functions = NULL;
+ _numFunctions = 0;
+
+ if (_methods) delete [] _methods;
+ _methods = NULL;
+ _numMethods = 0;
+
+ if (_events) delete [] _events;
+ _events = NULL;
+ _numEvents = 0;
+
+
+ if (_externals) {
+ for (uint32 i = 0; i < _numExternals; i++) {
+ if (_externals[i].nu_params > 0) delete [] _externals[i].params;
+ }
+ delete [] _externals;
+ }
+ _externals = NULL;
+ _numExternals = 0;
+
+ delete _operand;
+ delete _reg1;
+ _operand = NULL;
+ _reg1 = NULL;
+
+ delete[] _threadEvent;
+ _threadEvent = NULL;
+
+ _state = SCRIPT_FINISHED;
+
+ _waitObject = NULL;
+ _waitTime = 0;
+ _waitFrozen = false;
+ _waitScript = NULL;
+
+ _parentScript = NULL; // ref only
+
+ delete _scriptStream;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CScScript::getDWORD() {
+ _scriptStream->seek((int32)_iP);
+ uint32 ret = _scriptStream->readUint32LE();
+ _iP += sizeof(uint32);
+// assert(oldRet == ret);
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+double CScScript::getFloat() {
+ _scriptStream->seek((int32)_iP);
+ byte buffer[8];
+ _scriptStream->read(buffer, 8);
+
+#ifdef SCUMM_BIG_ENDIAN
+ // TODO: For lack of a READ_LE_UINT64
+ SWAP(buffer[0], buffer[7]);
+ SWAP(buffer[1], buffer[6]);
+ SWAP(buffer[2], buffer[5]);
+ SWAP(buffer[3], buffer[4]);
+#endif
+
+ double ret = *(double *)(buffer);
+ _iP += 8; // Hardcode the double-size used originally.
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *CScScript::getString() {
+ char *ret = (char *)(_buffer + _iP);
+ while (*(char *)(_buffer + _iP) != '\0') _iP++;
+ _iP++; // string terminator
+ _scriptStream->seek(_iP);
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::executeInstruction() {
+ bool ret = STATUS_OK;
+
+ uint32 dw;
+ const char *str = NULL;
+
+ //CScValue* op = new CScValue(_gameRef);
+ _operand->cleanup();
+
+ CScValue *op1;
+ CScValue *op2;
+
+ uint32 inst = getDWORD();
+ switch (inst) {
+
+ case II_DEF_VAR:
+ _operand->setNULL();
+ dw = getDWORD();
+ if (_scopeStack->_sP < 0) {
+ _globals->setProp(_symbols[dw], _operand);
+ if (_gameRef->getDebugMgr()->_enabled)
+ _gameRef->getDebugMgr()->onVariableInit(WME_DBGVAR_SCRIPT, this, NULL, _globals->getProp(_symbols[dw]), _symbols[dw]);
+ } else {
+ _scopeStack->getTop()->setProp(_symbols[dw], _operand);
+ if (_gameRef->getDebugMgr()->_enabled)
+ _gameRef->getDebugMgr()->onVariableInit(WME_DBGVAR_SCOPE, this, _scopeStack->getTop(), _scopeStack->getTop()->getProp(_symbols[dw]), _symbols[dw]);
+ }
+
+ break;
+
+ case II_DEF_GLOB_VAR:
+ case II_DEF_CONST_VAR: {
+ dw = getDWORD();
+ /* char *Temp = _symbols[dw]; // TODO delete */
+ // only create global var if it doesn't exist
+ if (!_engine->_globals->propExists(_symbols[dw])) {
+ _operand->setNULL();
+ _engine->_globals->setProp(_symbols[dw], _operand, false, inst == II_DEF_CONST_VAR);
+
+ if (_gameRef->getDebugMgr()->_enabled)
+ _gameRef->getDebugMgr()->onVariableInit(WME_DBGVAR_GLOBAL, this, NULL, _engine->_globals->getProp(_symbols[dw]), _symbols[dw]);
+ }
+ break;
+ }
+
+ case II_RET:
+ if (_scopeStack->_sP >= 0 && _callStack->_sP >= 0) {
+ _gameRef->getDebugMgr()->onScriptShutdownScope(this, _scopeStack->getTop());
+
+ _scopeStack->pop();
+ _iP = (uint32)_callStack->pop()->getInt();
+
+ if (_scopeStack->_sP < 0) _gameRef->getDebugMgr()->onScriptChangeScope(this, NULL);
+ else _gameRef->getDebugMgr()->onScriptChangeScope(this, _scopeStack->getTop());
+ } else {
+ if (_thread) {
+ _state = SCRIPT_THREAD_FINISHED;
+ } else {
+ if (_numEvents == 0 && _numMethods == 0) _state = SCRIPT_FINISHED;
+ else _state = SCRIPT_PERSISTENT;
+ }
+ }
+
+ break;
+
+ case II_RET_EVENT:
+ _state = SCRIPT_FINISHED;
+ break;
+
+
+ case II_CALL:
+ dw = getDWORD();
+
+ _operand->setInt(_iP);
+ _callStack->push(_operand);
+
+ _iP = dw;
+
+ break;
+
+ case II_CALL_BY_EXP: {
+ // push var
+ // push string
+ str = _stack->pop()->getString();
+ char *MethodName = new char[strlen(str) + 1];
+ strcpy(MethodName, str);
+
+ CScValue *var = _stack->pop();
+ if (var->_type == VAL_VARIABLE_REF) var = var->_valRef;
+
+ bool res = STATUS_FAILED;
+ bool TriedNative = false;
+
+ // we are already calling this method, try native
+ if (_thread && _methodThread && strcmp(MethodName, _threadEvent) == 0 && var->_type == VAL_NATIVE && _owner == var->getNative()) {
+ TriedNative = true;
+ res = var->_valNative->scCallMethod(this, _stack, _thisStack, MethodName);
+ }
+
+ if (DID_FAIL(res)) {
+ if (var->isNative() && var->getNative()->canHandleMethod(MethodName)) {
+ if (!_unbreakable) {
+ _waitScript = var->getNative()->invokeMethodThread(MethodName);
+ if (!_waitScript) {
+ _stack->correctParams(0);
+ runtimeError("Error invoking method '%s'.", MethodName);
+ _stack->pushNULL();
+ } else {
+ _state = SCRIPT_WAITING_SCRIPT;
+ _waitScript->copyParameters(_stack);
+ }
+ } else {
+ // can call methods in unbreakable mode
+ _stack->correctParams(0);
+ runtimeError("Cannot call method '%s'. Ignored.", MethodName);
+ _stack->pushNULL();
+ }
+ delete [] MethodName;
+ break;
+ }
+ /*
+ CScValue* val = var->getProp(MethodName);
+ if(val){
+ dw = GetFuncPos(val->getString());
+ if(dw==0){
+ TExternalFunction* f = GetExternal(val->getString());
+ if(f){
+ ExternalCall(_stack, _thisStack, f);
+ }
+ else{
+ // not an internal nor external, try for native function
+ _gameRef->ExternalCall(this, _stack, _thisStack, val->getString());
+ }
+ }
+ else{
+ _operand->setInt(_iP);
+ _callStack->Push(_operand);
+ _iP = dw;
+ }
+ }
+ */
+ else {
+ res = STATUS_FAILED;
+ if (var->_type == VAL_NATIVE && !TriedNative) res = var->_valNative->scCallMethod(this, _stack, _thisStack, MethodName);
+
+ if (DID_FAIL(res)) {
+ _stack->correctParams(0);
+ runtimeError("Call to undefined method '%s'. Ignored.", MethodName);
+ _stack->pushNULL();
+ }
+ }
+ }
+ delete [] MethodName;
+ }
+ break;
+
+ case II_EXTERNAL_CALL: {
+ uint32 SymbolIndex = getDWORD();
+
+ TExternalFunction *f = getExternal(_symbols[SymbolIndex]);
+ if (f) {
+ externalCall(_stack, _thisStack, f);
+ } else _gameRef->ExternalCall(this, _stack, _thisStack, _symbols[SymbolIndex]);
+
+ break;
+ }
+ case II_SCOPE:
+ _operand->setNULL();
+ _scopeStack->push(_operand);
+
+ if (_scopeStack->_sP < 0) _gameRef->getDebugMgr()->onScriptChangeScope(this, NULL);
+ else _gameRef->getDebugMgr()->onScriptChangeScope(this, _scopeStack->getTop());
+
+ break;
+
+ case II_CORRECT_STACK:
+ dw = getDWORD(); // params expected
+ _stack->correctParams(dw);
+ break;
+
+ case II_CREATE_OBJECT:
+ _operand->setObject();
+ _stack->push(_operand);
+ break;
+
+ case II_POP_EMPTY:
+ _stack->pop();
+ break;
+
+ case II_PUSH_VAR: {
+ CScValue *var = getVar(_symbols[getDWORD()]);
+ if (false && /*var->_type==VAL_OBJECT ||*/ var->_type == VAL_NATIVE) {
+ _operand->setReference(var);
+ _stack->push(_operand);
+ } else _stack->push(var);
+ break;
+ }
+
+ case II_PUSH_VAR_REF: {
+ CScValue *var = getVar(_symbols[getDWORD()]);
+ _operand->setReference(var);
+ _stack->push(_operand);
+ break;
+ }
+
+ case II_POP_VAR: {
+ char *VarName = _symbols[getDWORD()];
+ CScValue *var = getVar(VarName);
+ if (var) {
+ CScValue *val = _stack->pop();
+ if (!val) {
+ runtimeError("Script stack corruption detected. Please report this script at WME bug reports forum.");
+ var->setNULL();
+ } else {
+ if (val->getType() == VAL_VARIABLE_REF) val = val->_valRef;
+ if (val->_type == VAL_NATIVE) var->setValue(val);
+ else {
+ var->copy(val);
+ }
+ }
+
+ if (_gameRef->getDebugMgr()->_enabled)
+ _gameRef->getDebugMgr()->onVariableChangeValue(var, val);
+ }
+
+ break;
+ }
+
+ case II_PUSH_VAR_THIS:
+ _stack->push(_thisStack->getTop());
+ break;
+
+ case II_PUSH_INT:
+ _stack->pushInt((int)getDWORD());
+ break;
+
+ case II_PUSH_FLOAT:
+ _stack->pushFloat(getFloat());
+ break;
+
+
+ case II_PUSH_BOOL:
+ _stack->pushBool(getDWORD() != 0);
+
+ break;
+
+ case II_PUSH_STRING:
+ _stack->pushString(getString());
+ break;
+
+ case II_PUSH_NULL:
+ _stack->pushNULL();
+ break;
+
+ case II_PUSH_THIS_FROM_STACK:
+ _operand->setReference(_stack->getTop());
+ _thisStack->push(_operand);
+ break;
+
+ case II_PUSH_THIS:
+ _operand->setReference(getVar(_symbols[getDWORD()]));
+ _thisStack->push(_operand);
+ break;
+
+ case II_POP_THIS:
+ _thisStack->pop();
+ break;
+
+ case II_PUSH_BY_EXP: {
+ str = _stack->pop()->getString();
+ CScValue *val = _stack->pop()->getProp(str);
+ if (val) _stack->push(val);
+ else _stack->pushNULL();
+
+ break;
+ }
+
+ case II_POP_BY_EXP: {
+ str = _stack->pop()->getString();
+ CScValue *var = _stack->pop();
+ CScValue *val = _stack->pop();
+
+ if (val == NULL) {
+ runtimeError("Script stack corruption detected. Please report this script at WME bug reports forum.");
+ var->setNULL();
+ } else var->setProp(str, val);
+
+ if (_gameRef->getDebugMgr()->_enabled)
+ _gameRef->getDebugMgr()->onVariableChangeValue(var, NULL);
+
+ break;
+ }
+
+ case II_PUSH_REG1:
+ _stack->push(_reg1);
+ break;
+
+ case II_POP_REG1:
+ _reg1->copy(_stack->pop());
+ break;
+
+ case II_JMP:
+ _iP = getDWORD();
+ break;
+
+ case II_JMP_FALSE: {
+ dw = getDWORD();
+ //if(!_stack->pop()->getBool()) _iP = dw;
+ CScValue *val = _stack->pop();
+ if (!val) {
+ runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?");
+ } else {
+ if (!val->getBool()) _iP = dw;
+ }
+ break;
+ }
+
+ case II_ADD:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ if (op1->isNULL() || op2->isNULL())
+ _operand->setNULL();
+ else if (op1->getType() == VAL_STRING || op2->getType() == VAL_STRING) {
+ char *tempStr = new char [strlen(op1->getString()) + strlen(op2->getString()) + 1];
+ strcpy(tempStr, op1->getString());
+ strcat(tempStr, op2->getString());
+ _operand->setString(tempStr);
+ delete [] tempStr;
+ } else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT)
+ _operand->setInt(op1->getInt() + op2->getInt());
+ else _operand->setFloat(op1->getFloat() + op2->getFloat());
+
+ _stack->push(_operand);
+
+ break;
+
+ case II_SUB:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ if (op1->isNULL() || op2->isNULL())
+ _operand->setNULL();
+ else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT)
+ _operand->setInt(op1->getInt() - op2->getInt());
+ else _operand->setFloat(op1->getFloat() - op2->getFloat());
+
+ _stack->push(_operand);
+
+ break;
+
+ case II_MUL:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ if (op1->isNULL() || op2->isNULL()) _operand->setNULL();
+ else if (op1->getType() == VAL_INT && op2->getType() == VAL_INT)
+ _operand->setInt(op1->getInt() * op2->getInt());
+ else _operand->setFloat(op1->getFloat() * op2->getFloat());
+
+ _stack->push(_operand);
+
+ break;
+
+ case II_DIV:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ if (op2->getFloat() == 0.0f)
+ runtimeError("Division by zero.");
+
+ if (op1->isNULL() || op2->isNULL() || op2->getFloat() == 0.0f) _operand->setNULL();
+ else _operand->setFloat(op1->getFloat() / op2->getFloat());
+
+ _stack->push(_operand);
+
+ break;
+
+ case II_MODULO:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ if (op2->getInt() == 0)
+ runtimeError("Division by zero.");
+
+ if (op1->isNULL() || op2->isNULL() || op2->getInt() == 0)
+ _operand->setNULL();
+ else _operand->setInt(op1->getInt() % op2->getInt());
+
+ _stack->push(_operand);
+
+ break;
+
+ case II_NOT:
+ op1 = _stack->pop();
+ //if(op1->isNULL()) _operand->setNULL();
+ if (op1->isNULL()) _operand->setBool(true);
+ else _operand->setBool(!op1->getBool());
+ _stack->push(_operand);
+
+ break;
+
+ case II_AND:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+ if (op1 == NULL || op2 == NULL) {
+ runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?");
+ _operand->setBool(false);
+ } else {
+ _operand->setBool(op1->getBool() && op2->getBool());
+ }
+ _stack->push(_operand);
+ break;
+
+ case II_OR:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+ if (op1 == NULL || op2 == NULL) {
+ runtimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?");
+ _operand->setBool(false);
+ } else {
+ _operand->setBool(op1->getBool() || op2->getBool());
+ }
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_EQ:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if((op1->isNULL() && !op2->isNULL()) || (!op1->isNULL() && op2->isNULL())) _operand->setBool(false);
+ else if(op1->isNative() && op2->isNative()){
+ _operand->setBool(op1->getNative() == op2->getNative());
+ }
+ else if(op1->getType()==VAL_STRING || op2->getType()==VAL_STRING){
+ _operand->setBool(scumm_stricmp(op1->getString(), op2->getString())==0);
+ }
+ else if(op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() == op2->getFloat());
+ }
+ else{
+ _operand->setBool(op1->getInt() == op2->getInt());
+ }
+ */
+
+ _operand->setBool(CScValue::compare(op1, op2) == 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_NE:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if((op1->isNULL() && !op2->isNULL()) || (!op1->isNULL() && op2->isNULL())) _operand->setBool(true);
+ else if(op1->isNative() && op2->isNative()){
+ _operand->setBool(op1->getNative() != op2->getNative());
+ }
+ else if(op1->getType()==VAL_STRING || op2->getType()==VAL_STRING){
+ _operand->setBool(scumm_stricmp(op1->getString(), op2->getString())!=0);
+ }
+ else if(op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() != op2->getFloat());
+ }
+ else{
+ _operand->setBool(op1->getInt() != op2->getInt());
+ }
+ */
+
+ _operand->setBool(CScValue::compare(op1, op2) != 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_L:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if(op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() < op2->getFloat());
+ }
+ else _operand->setBool(op1->getInt() < op2->getInt());
+ */
+
+ _operand->setBool(CScValue::compare(op1, op2) < 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_G:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if(op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() > op2->getFloat());
+ }
+ else _operand->setBool(op1->getInt() > op2->getInt());
+ */
+
+ _operand->setBool(CScValue::compare(op1, op2) > 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_LE:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if(op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() <= op2->getFloat());
+ }
+ else _operand->setBool(op1->getInt() <= op2->getInt());
+ */
+
+ _operand->setBool(CScValue::compare(op1, op2) <= 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_GE:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ /*
+ if(op1->getType()==VAL_FLOAT && op2->getType()==VAL_FLOAT){
+ _operand->setBool(op1->getFloat() >= op2->getFloat());
+ }
+ else _operand->setBool(op1->getInt() >= op2->getInt());
+ */
+
+ _operand->setBool(CScValue::compare(op1, op2) >= 0);
+ _stack->push(_operand);
+ break;
+
+ case II_CMP_STRICT_EQ:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ //_operand->setBool(op1->getType()==op2->getType() && op1->getFloat()==op2->getFloat());
+ _operand->setBool(CScValue::compareStrict(op1, op2) == 0);
+ _stack->push(_operand);
+
+ break;
+
+ case II_CMP_STRICT_NE:
+ op2 = _stack->pop();
+ op1 = _stack->pop();
+
+ //_operand->setBool(op1->getType()!=op2->getType() || op1->getFloat()!=op2->getFloat());
+ _operand->setBool(CScValue::compareStrict(op1, op2) != 0);
+ _stack->push(_operand);
+ break;
+
+ case II_DBG_LINE: {
+ int newLine = getDWORD();
+ if (newLine != _currentLine) {
+ _currentLine = newLine;
+ if (_gameRef->getDebugMgr()->_enabled) {
+ _gameRef->getDebugMgr()->onScriptChangeLine(this, _currentLine);
+ for (int i = 0; i < _breakpoints.getSize(); i++) {
+ if (_breakpoints[i] == _currentLine) {
+ _gameRef->getDebugMgr()->onScriptHitBreakpoint(this);
+ sleep(0);
+ break;
+ }
+ }
+ if (_tracingMode) {
+ _gameRef->getDebugMgr()->onScriptHitBreakpoint(this);
+ sleep(0);
+ break;
+ }
+ }
+ }
+ break;
+
+ }
+ default:
+ _gameRef->LOG(0, "Fatal: Invalid instruction %d ('%s', line %d, IP:0x%x)\n", inst, _filename, _currentLine, _iP - sizeof(uint32));
+ _state = SCRIPT_FINISHED;
+ ret = STATUS_FAILED;
+ } // switch(instruction)
+
+ //delete op;
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CScScript::getFuncPos(const char *name) {
+ for (uint32 i = 0; i < _numFunctions; i++) {
+ if (strcmp(name, _functions[i].name) == 0)
+ return _functions[i].pos;
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CScScript::getMethodPos(const char *name) {
+ for (uint32 i = 0; i < _numMethods; i++) {
+ if (strcmp(name, _methods[i].name) == 0)
+ return _methods[i].pos;
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScScript::getVar(char *name) {
+ CScValue *ret = NULL;
+
+ // scope locals
+ if (_scopeStack->_sP >= 0) {
+ if (_scopeStack->getTop()->propExists(name))
+ ret = _scopeStack->getTop()->getProp(name);
+ }
+
+ // script globals
+ if (ret == NULL) {
+ if (_globals->propExists(name))
+ ret = _globals->getProp(name);
+ }
+
+ // engine globals
+ if (ret == NULL) {
+ if (_engine->_globals->propExists(name))
+ ret = _engine->_globals->getProp(name);
+ }
+
+ if (ret == NULL) {
+ //RuntimeError("Variable '%s' is inaccessible in the current block. Consider changing the script.", name);
+ _gameRef->LOG(0, "Warning: variable '%s' is inaccessible in the current block. Consider changing the script (script:%s, line:%d)", name, _filename, _currentLine);
+ CScValue *val = new CScValue(_gameRef);
+ CScValue *scope = _scopeStack->getTop();
+ if (scope) {
+ scope->setProp(name, val);
+ ret = _scopeStack->getTop()->getProp(name);
+ } else {
+ _globals->setProp(name, val);
+ ret = _globals->getProp(name);
+ }
+ delete val;
+ }
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::waitFor(CBObject *object) {
+ if (_unbreakable) {
+ runtimeError("Script cannot be interrupted.");
+ return STATUS_OK;
+ }
+
+ _state = SCRIPT_WAITING;
+ _waitObject = object;
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::waitForExclusive(CBObject *object) {
+ _engine->resetObject(object);
+ return waitFor(object);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::sleep(uint32 duration) {
+ if (_unbreakable) {
+ runtimeError("Script cannot be interrupted.");
+ return STATUS_OK;
+ }
+
+ _state = SCRIPT_SLEEPING;
+ if (_gameRef->_state == GAME_FROZEN) {
+ _waitTime = CBPlatform::getTime() + duration;
+ _waitFrozen = true;
+ } else {
+ _waitTime = _gameRef->_timer + duration;
+ _waitFrozen = false;
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::finish(bool includingThreads) {
+ if (_state != SCRIPT_FINISHED && includingThreads) {
+ _state = SCRIPT_FINISHED;
+ finishThreads();
+ } else _state = SCRIPT_FINISHED;
+
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::run() {
+ _state = SCRIPT_RUNNING;
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void CScScript::runtimeError(const char *fmt, ...) {
+ char buff[256];
+ va_list va;
+
+ va_start(va, fmt);
+ vsprintf(buff, fmt, va);
+ va_end(va);
+
+ _gameRef->LOG(0, "Runtime error. Script '%s', line %d", _filename, _currentLine);
+ _gameRef->LOG(0, " %s", buff);
+
+ if (!_gameRef->_suppressScriptErrors)
+ _gameRef->quickMessage("Script runtime error. View log for details.");
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::persist(CBPersistMgr *persistMgr) {
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ // buffer
+ if (persistMgr->_saving) {
+ if (_state != SCRIPT_PERSISTENT && _state != SCRIPT_FINISHED && _state != SCRIPT_THREAD_FINISHED) {
+ persistMgr->transfer(TMEMBER(_bufferSize));
+ persistMgr->putBytes(_buffer, _bufferSize);
+ } else {
+ // don't save idle/finished scripts
+ int bufferSize = 0;
+ persistMgr->transfer(TMEMBER(bufferSize));
+ }
+ } else {
+ persistMgr->transfer(TMEMBER(_bufferSize));
+ if (_bufferSize > 0) {
+ _buffer = new byte[_bufferSize];
+ persistMgr->getBytes(_buffer, _bufferSize);
+ _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize);
+ initTables();
+ } else {
+ _buffer = NULL;
+ _scriptStream = NULL;
+ }
+ }
+
+ persistMgr->transfer(TMEMBER(_callStack));
+ persistMgr->transfer(TMEMBER(_currentLine));
+ persistMgr->transfer(TMEMBER(_engine));
+ persistMgr->transfer(TMEMBER(_filename));
+ persistMgr->transfer(TMEMBER(_freezable));
+ persistMgr->transfer(TMEMBER(_globals));
+ persistMgr->transfer(TMEMBER(_iP));
+ persistMgr->transfer(TMEMBER(_scopeStack));
+ persistMgr->transfer(TMEMBER(_stack));
+ persistMgr->transfer(TMEMBER_INT(_state));
+ persistMgr->transfer(TMEMBER(_operand));
+ persistMgr->transfer(TMEMBER_INT(_origState));
+ persistMgr->transfer(TMEMBER(_owner));
+ persistMgr->transfer(TMEMBER(_reg1));
+ persistMgr->transfer(TMEMBER(_thread));
+ persistMgr->transfer(TMEMBER(_threadEvent));
+ persistMgr->transfer(TMEMBER(_thisStack));
+ persistMgr->transfer(TMEMBER(_timeSlice));
+ persistMgr->transfer(TMEMBER(_waitObject));
+ persistMgr->transfer(TMEMBER(_waitScript));
+ persistMgr->transfer(TMEMBER(_waitTime));
+ persistMgr->transfer(TMEMBER(_waitFrozen));
+
+ persistMgr->transfer(TMEMBER(_methodThread));
+ persistMgr->transfer(TMEMBER(_methodThread));
+ persistMgr->transfer(TMEMBER(_unbreakable));
+ persistMgr->transfer(TMEMBER(_parentScript));
+
+ if (!persistMgr->_saving) _tracingMode = false;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScScript *CScScript::invokeEventHandler(const char *eventName, bool unbreakable) {
+ //if(_state!=SCRIPT_PERSISTENT) return NULL;
+
+ uint32 pos = getEventPos(eventName);
+ if (!pos) return NULL;
+
+ CScScript *thread = new CScScript(_gameRef, _engine);
+ if (thread) {
+ bool ret = thread->createThread(this, pos, eventName);
+ if (DID_SUCCEED(ret)) {
+ thread->_unbreakable = unbreakable;
+ _engine->_scripts.add(thread);
+ _gameRef->getDebugMgr()->onScriptEventThreadInit(thread, this, eventName);
+ return thread;
+ } else {
+ delete thread;
+ return NULL;
+ }
+ } else return NULL;
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CScScript::getEventPos(const char *name) {
+ for (int i = _numEvents - 1; i >= 0; i--) {
+ if (scumm_stricmp(name, _events[i].name) == 0) return _events[i].pos;
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::canHandleEvent(const char *eventName) {
+ return getEventPos(eventName) != 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::canHandleMethod(const char *methodName) {
+ return getMethodPos(methodName) != 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::pause() {
+ if (_state == SCRIPT_PAUSED) {
+ _gameRef->LOG(0, "Attempting to pause a paused script ('%s', line %d)", _filename, _currentLine);
+ return STATUS_FAILED;
+ }
+
+ if (!_freezable || _state == SCRIPT_PERSISTENT) return STATUS_OK;
+
+ _origState = _state;
+ _state = SCRIPT_PAUSED;
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::resume() {
+ if (_state != SCRIPT_PAUSED) return STATUS_OK;
+
+ _state = _origState;
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScScript::TExternalFunction *CScScript::getExternal(char *name) {
+ for (uint32 i = 0; i < _numExternals; i++) {
+ if (strcmp(name, _externals[i].name) == 0)
+ return &_externals[i];
+ }
+ return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::externalCall(CScStack *stack, CScStack *thisStack, CScScript::TExternalFunction *function) {
+
+ _gameRef->LOG(0, "External functions are not supported on this platform.");
+ stack->correctParams(0);
+ stack->pushNULL();
+ return STATUS_FAILED;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::copyParameters(CScStack *stack) {
+ int i;
+ int NumParams = stack->pop()->getInt();
+ for (i = NumParams - 1; i >= 0; i--) {
+ _stack->push(stack->getAt(i));
+ }
+ _stack->pushInt(NumParams);
+
+ for (i = 0; i < NumParams; i++) stack->pop();
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::finishThreads() {
+ for (int i = 0; i < _engine->_scripts.getSize(); i++) {
+ CScScript *scr = _engine->_scripts[i];
+ if (scr->_thread && scr->_state != SCRIPT_FINISHED && scr->_owner == _owner && scumm_stricmp(scr->_filename, _filename) == 0)
+ scr->finish(true);
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// IWmeDebugScript interface implementation
+int CScScript::dbgGetLine() {
+ return _currentLine;
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *CScScript::dbgGetFilename() {
+ return _filename;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::dbgSendScript(IWmeDebugClient *client) {
+ if (_methodThread) client->onScriptMethodThreadInit(this, _parentScript, _threadEvent);
+ else if (_thread) client->onScriptEventThreadInit(this, _parentScript, _threadEvent);
+ else client->onScriptInit(this);
+
+ return dbgSendVariables(client);
+ return STATUS_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::dbgSendVariables(IWmeDebugClient *client) {
+ // send script globals
+ _globals->dbgSendVariables(client, WME_DBGVAR_SCRIPT, this, 0);
+
+ // send scope variables
+ if (_scopeStack->_sP >= 0) {
+ for (int i = 0; i <= _scopeStack->_sP; i++) {
+ // CScValue *Scope = _scopeStack->GetAt(i);
+ //Scope->DbgSendVariables(Client, WME_DBGVAR_SCOPE, this, (unsigned int)Scope);
+ }
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+TScriptState CScScript::dbgGetState() {
+ return _state;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int CScScript::dbgGetNumBreakpoints() {
+ return _breakpoints.getSize();
+}
+
+//////////////////////////////////////////////////////////////////////////
+int CScScript::dbgGetBreakpoint(int index) {
+ if (index >= 0 && index < _breakpoints.getSize()) return _breakpoints[index];
+ else return -1;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::dbgSetTracingMode(bool isTracing) {
+ _tracingMode = isTracing;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::dbgGetTracingMode() {
+ return _tracingMode;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScScript::afterLoad() {
+ if (_buffer == NULL) {
+ byte *buffer = _engine->getCompiledScript(_filename, &_bufferSize);
+ if (!buffer) {
+ _gameRef->LOG(0, "Error reinitializing script '%s' after load. Script will be terminated.", _filename);
+ _state = SCRIPT_ERROR;
+ return;
+ }
+
+ _buffer = new byte [_bufferSize];
+ memcpy(_buffer, buffer, _bufferSize);
+
+ delete _scriptStream;
+ _scriptStream = new Common::MemoryReadStream(_buffer, _bufferSize);
+
+ initTables();
+ }
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/base/scriptables/ScScript.h b/engines/wintermute/base/scriptables/ScScript.h
new file mode 100644
index 0000000000..c031f8186f
--- /dev/null
+++ b/engines/wintermute/base/scriptables/ScScript.h
@@ -0,0 +1,183 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SCSCRIPT_H
+#define WINTERMUTE_SCSCRIPT_H
+
+
+#include "engines/wintermute/base/BBase.h"
+#include "engines/wintermute/dcscript.h" // Added by ClassView
+#include "engines/wintermute/coll_templ.h"
+
+#include "engines/wintermute/wme_debugger.h"
+
+namespace WinterMute {
+class CBScriptHolder;
+class CBObject;
+class CScEngine;
+class CScStack;
+class CScScript : public CBBase, public IWmeDebugScript {
+public:
+ bool dbgSendScript(IWmeDebugClient *client);
+ bool dbgSendVariables(IWmeDebugClient *client);
+
+ CBArray<int, int> _breakpoints;
+ bool _tracingMode;
+
+ CScScript *_parentScript;
+ bool _unbreakable;
+ bool finishThreads();
+ bool copyParameters(CScStack *stack);
+
+ void afterLoad();
+
+ CScValue *_operand;
+ CScValue *_reg1;
+ bool _freezable;
+ bool resume();
+ bool pause();
+ bool canHandleEvent(const char *eventName);
+ bool canHandleMethod(const char *methodName);
+ bool createThread(CScScript *original, uint32 initIP, const char *eventName);
+ bool createMethodThread(CScScript *original, const char *methodName);
+ CScScript *invokeEventHandler(const char *eventName, bool unbreakable = false);
+ uint32 _timeSlice;
+ DECLARE_PERSISTENT(CScScript, CBBase)
+ void runtimeError(const char *fmt, ...);
+ bool run();
+ bool finish(bool includingThreads = false);
+ bool sleep(uint32 duration);
+ bool waitForExclusive(CBObject *object);
+ bool waitFor(CBObject *object);
+ uint32 _waitTime;
+ bool _waitFrozen;
+ CBObject *_waitObject;
+ CScScript *_waitScript;
+ TScriptState _state;
+ TScriptState _origState;
+ CScValue *getVar(char *name);
+ uint32 getFuncPos(const char *name);
+ uint32 getEventPos(const char *name);
+ uint32 getMethodPos(const char *name);
+ typedef struct {
+ uint32 magic;
+ uint32 version;
+ uint32 code_start;
+ uint32 func_table;
+ uint32 symbol_table;
+ uint32 event_table;
+ uint32 externals_table;
+ uint32 method_table;
+ } TScriptHeader;
+
+ TScriptHeader _header;
+
+ typedef struct {
+ char *name;
+ uint32 pos;
+ } TFunctionPos;
+
+ typedef struct {
+ char *name;
+ uint32 pos;
+ } TMethodPos;
+
+ typedef struct {
+ char *name;
+ uint32 pos;
+ } TEventPos;
+
+ typedef struct {
+ char *name;
+ char *dll_name;
+ TCallType call_type;
+ TExternalType returns;
+ int nu_params;
+ TExternalType *params;
+ } TExternalFunction;
+
+
+ CScStack *_callStack;
+ CScStack *_thisStack;
+ CScStack *_scopeStack;
+ CScStack *_stack;
+ CScValue *_globals;
+ CScEngine *_engine;
+ int _currentLine;
+ bool executeInstruction();
+ char *getString();
+ uint32 getDWORD();
+ double getFloat();
+ void cleanup();
+ bool create(const char *filename, byte *buffer, uint32 size, CBScriptHolder *owner);
+ uint32 _iP;
+private:
+ void readHeader();
+ uint32 _bufferSize;
+ byte *_buffer;
+public:
+ Common::SeekableReadStream *_scriptStream;
+ CScScript(CBGame *inGame, CScEngine *Engine);
+ virtual ~CScScript();
+ char *_filename;
+ char **_symbols;
+ uint32 _numSymbols;
+ TFunctionPos *_functions;
+ TMethodPos *_methods;
+ TEventPos *_events;
+ uint32 _numExternals;
+ TExternalFunction *_externals;
+ uint32 _numFunctions;
+ uint32 _numMethods;
+ uint32 _numEvents;
+ bool _thread;
+ bool _methodThread;
+ char *_threadEvent;
+ CBScriptHolder *_owner;
+ CScScript::TExternalFunction *getExternal(char *name);
+ bool externalCall(CScStack *stack, CScStack *thisStack, CScScript::TExternalFunction *function);
+private:
+ bool initScript();
+ bool initTables();
+
+
+// IWmeDebugScript interface implementation
+public:
+ virtual int dbgGetLine();
+ virtual const char *dbgGetFilename();
+ virtual TScriptState dbgGetState();
+ virtual int dbgGetNumBreakpoints();
+ virtual int dbgGetBreakpoint(int Index);
+
+ virtual bool dbgSetTracingMode(bool IsTracing);
+ virtual bool dbgGetTracingMode();
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/ScStack.cpp b/engines/wintermute/base/scriptables/ScStack.cpp
new file mode 100644
index 0000000000..252cd21dda
--- /dev/null
+++ b/engines/wintermute/base/scriptables/ScStack.cpp
@@ -0,0 +1,226 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/base/scriptables/ScStack.h"
+#include "engines/wintermute/base/scriptables/ScValue.h"
+#include "engines/wintermute/base/BGame.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CScStack, false)
+
+//////////////////////////////////////////////////////////////////////////
+CScStack::CScStack(CBGame *inGame): CBBase(inGame) {
+ _sP = -1;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScStack::~CScStack() {
+
+#if _DEBUG
+ //_gameRef->LOG(0, "STAT: Stack size: %d, SP=%d", _values.getSize(), _sP);
+#endif
+
+ for (int i = 0; i < _values.getSize(); i++) {
+ delete _values[i];
+ }
+ _values.removeAll();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScStack::pop() {
+ if (_sP < 0) {
+ _gameRef->LOG(0, "Fatal: Stack underflow");
+ return NULL;
+ }
+
+ return _values[_sP--];
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::push(CScValue *val) {
+ _sP++;
+
+ if (_sP < _values.getSize()) {
+ _values[_sP]->cleanup();
+ _values[_sP]->copy(val);
+ } else {
+ CScValue *copyVal = new CScValue(_gameRef);
+ copyVal->copy(val);
+ _values.add(copyVal);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScStack::getPushValue() {
+ _sP++;
+
+ if (_sP >= _values.getSize()) {
+ CScValue *val = new CScValue(_gameRef);
+ _values.add(val);
+ }
+ _values[_sP]->cleanup();
+ return _values[_sP];
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScStack::getTop() {
+ if (_sP < 0 || _sP >= _values.getSize()) return NULL;
+ else return _values[_sP];
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScStack::getAt(int index) {
+ index = _sP - index;
+ if (index < 0 || index >= _values.getSize()) return NULL;
+ else return _values[index];
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::correctParams(uint32 expectedParams) {
+ uint32 nuParams = (uint32)pop()->getInt();
+
+ if (expectedParams < nuParams) { // too many params
+ while (expectedParams < nuParams) {
+ //Pop();
+ delete _values[_sP - expectedParams];
+ _values.removeAt(_sP - expectedParams);
+ nuParams--;
+ _sP--;
+ }
+ } else if (expectedParams > nuParams) { // need more params
+ while (expectedParams > nuParams) {
+ //Push(null_val);
+ CScValue *nullVal = new CScValue(_gameRef);
+ nullVal->setNULL();
+ _values.insertAt(_sP - nuParams + 1, nullVal);
+ nuParams++;
+ _sP++;
+
+ if (_values.getSize() > _sP + 1) {
+ delete _values[_values.getSize() - 1];
+ _values.removeAt(_values.getSize() - 1);
+ }
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::pushNULL() {
+ /*
+ CScValue* val = new CScValue(_gameRef);
+ val->setNULL();
+ Push(val);
+ delete val;
+ */
+ getPushValue()->setNULL();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::pushInt(int val) {
+ /*
+ CScValue* val = new CScValue(_gameRef);
+ val->setInt(Val);
+ Push(val);
+ delete val;
+ */
+ getPushValue()->setInt(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::pushFloat(double val) {
+ /*
+ CScValue* val = new CScValue(_gameRef);
+ val->setFloat(Val);
+ Push(val);
+ delete val;
+ */
+ getPushValue()->setFloat(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::pushBool(bool val) {
+ /*
+ CScValue* val = new CScValue(_gameRef);
+ val->setBool(Val);
+ Push(val);
+ delete val;
+ */
+ getPushValue()->setBool(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::pushString(const char *val) {
+ /*
+ CScValue* val = new CScValue(_gameRef);
+ val->setString(Val);
+ Push(val);
+ delete val;
+ */
+ getPushValue()->setString(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::pushNative(CBScriptable *val, bool persistent) {
+ /*
+ CScValue* val = new CScValue(_gameRef);
+ val->setNative(Val, Persistent);
+ Push(val);
+ delete val;
+ */
+
+ getPushValue()->setNative(val, persistent);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScStack::persist(CBPersistMgr *persistMgr) {
+
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ persistMgr->transfer(TMEMBER(_sP));
+ _values.persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/base/scriptables/ScStack.h b/engines/wintermute/base/scriptables/ScStack.h
new file mode 100644
index 0000000000..22dae63060
--- /dev/null
+++ b/engines/wintermute/base/scriptables/ScStack.h
@@ -0,0 +1,66 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SCSTACK_H
+#define WINTERMUTE_SCSTACK_H
+
+
+#include "engines/wintermute/base/BBase.h"
+#include "engines/wintermute/coll_templ.h"
+#include "engines/wintermute/persistent.h"
+
+namespace WinterMute {
+
+class CScValue;
+class CBScriptable;
+
+class CScStack : public CBBase {
+public:
+ CScValue *getAt(int Index);
+ CScValue *getPushValue();
+ DECLARE_PERSISTENT(CScStack, CBBase)
+ void pushNative(CBScriptable *val, bool persistent);
+ void pushString(const char *val);
+ void pushBool(bool val);
+ void pushInt(int val);
+ void pushFloat(double val);
+ void pushNULL();
+ void correctParams(uint32 expectedParams);
+ CScValue *getTop();
+ void push(CScValue *val);
+ CScValue *pop();
+ CScStack(CBGame *inGame);
+ virtual ~CScStack();
+ CBArray<CScValue *, CScValue *> _values;
+ int _sP;
+
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/ScValue.cpp b/engines/wintermute/base/scriptables/ScValue.cpp
new file mode 100644
index 0000000000..e9d5645682
--- /dev/null
+++ b/engines/wintermute/base/scriptables/ScValue.cpp
@@ -0,0 +1,1054 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "engines/wintermute/PlatformSDL.h"
+#include "engines/wintermute/base/BDynBuffer.h"
+#include "engines/wintermute/base/BGame.h"
+#include "engines/wintermute/base/scriptables/ScValue.h"
+#include "engines/wintermute/base/scriptables/ScScript.h"
+#include "engines/wintermute/utils/StringUtil.h"
+#include "engines/wintermute/base/BScriptable.h"
+
+namespace WinterMute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_PERSISTENT(CScValue, false)
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::CScValue(CBGame *inGame): CBBase(inGame) {
+ _type = VAL_NULL;
+
+ _valBool = false;
+ _valInt = 0;
+ _valFloat = 0.0f;
+ _valNative = NULL;
+ _valString = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::CScValue(CBGame *inGame, bool val): CBBase(inGame) {
+ _type = VAL_BOOL;
+ _valBool = val;
+
+ _valInt = 0;
+ _valFloat = 0.0f;
+ _valNative = NULL;
+ _valString = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::CScValue(CBGame *inGame, int val): CBBase(inGame) {
+ _type = VAL_INT;
+ _valInt = val;
+
+ _valFloat = 0.0f;
+ _valBool = false;
+ _valNative = NULL;
+ _valString = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::CScValue(CBGame *inGame, double val): CBBase(inGame) {
+ _type = VAL_FLOAT;
+ _valFloat = val;
+
+ _valInt = 0;
+ _valBool = false;
+ _valNative = NULL;
+ _valString = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::CScValue(CBGame *inGame, const char *val): CBBase(inGame) {
+ _type = VAL_STRING;
+ _valString = NULL;
+ setStringVal(val);
+
+ _valBool = false;
+ _valInt = 0;
+ _valFloat = 0.0f;
+ _valNative = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::cleanup(bool ignoreNatives) {
+ deleteProps();
+
+ if (_valString) delete [] _valString;
+
+ if (!ignoreNatives) {
+ if (_valNative && !_persistent) {
+ _valNative->_refCount--;
+ if (_valNative->_refCount <= 0) {
+ delete _valNative;
+ _valNative = NULL;
+ }
+ }
+ }
+
+
+ _type = VAL_NULL;
+
+ _valBool = false;
+ _valInt = 0;
+ _valFloat = 0.0f;
+ _valNative = NULL;
+ _valString = NULL;
+ _valRef = NULL;
+ _persistent = false;
+ _isConstVar = false;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::~CScValue() {
+ cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScValue::getProp(const char *name) {
+ if (_type == VAL_VARIABLE_REF) return _valRef->getProp(name);
+
+ if (_type == VAL_STRING && strcmp(name, "Length") == 0) {
+ _gameRef->_scValue->_type = VAL_INT;
+
+#if 0 // TODO: Remove FreeType-dependency
+ if (_gameRef->_textEncoding == TEXT_ANSI) {
+#else
+ if (true) {
+#endif
+ _gameRef->_scValue->setInt(strlen(_valString));
+ } else {
+ WideString wstr = StringUtil::utf8ToWide(_valString);
+ _gameRef->_scValue->setInt(wstr.size());
+ }
+
+ return _gameRef->_scValue;
+ }
+
+ CScValue *ret = NULL;
+
+ if (_type == VAL_NATIVE && _valNative) ret = _valNative->scGetProperty(name);
+
+ if (ret == NULL) {
+ _valIter = _valObject.find(name);
+ if (_valIter != _valObject.end()) ret = _valIter->_value;
+ }
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::deleteProp(const char *name) {
+ if (_type == VAL_VARIABLE_REF) return _valRef->deleteProp(name);
+
+ _valIter = _valObject.find(name);
+ if (_valIter != _valObject.end()) {
+ delete _valIter->_value;
+ _valIter->_value = NULL;
+ }
+
+ return STATUS_OK;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::setProp(const char *name, CScValue *val, bool copyWhole, bool setAsConst) {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->setProp(name, val);
+
+ bool ret = STATUS_FAILED;
+ if (_type == VAL_NATIVE && _valNative) {
+ ret = _valNative->scSetProperty(name, val);
+ }
+
+ if (DID_FAIL(ret)) {
+ CScValue *newVal = NULL;
+
+ _valIter = _valObject.find(name);
+ if (_valIter != _valObject.end()) {
+ newVal = _valIter->_value;
+ }
+ if (!newVal)
+ newVal = new CScValue(_gameRef);
+ else newVal->cleanup();
+
+ newVal->copy(val, copyWhole);
+ newVal->_isConstVar = setAsConst;
+ _valObject[name] = newVal;
+
+ if (_type != VAL_NATIVE) _type = VAL_OBJECT;
+
+ /*
+ _valIter = _valObject.find(Name);
+ if (_valIter != _valObject.end()){
+ delete _valIter->_value;
+ _valIter->_value = NULL;
+ }
+ CScValue* val = new CScValue(_gameRef);
+ val->Copy(Val, CopyWhole);
+ val->_isConstVar = SetAsConst;
+ _valObject[Name] = val;
+
+ if(_type!=VAL_NATIVE) _type = VAL_OBJECT;
+ */
+ }
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::propExists(const char *name) {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->propExists(name);
+ _valIter = _valObject.find(name);
+
+ return (_valIter != _valObject.end());
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::deleteProps() {
+ _valIter = _valObject.begin();
+ while (_valIter != _valObject.end()) {
+ delete(CScValue *)_valIter->_value;
+ _valIter++;
+ }
+ _valObject.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::CleanProps(bool includingNatives) {
+ _valIter = _valObject.begin();
+ while (_valIter != _valObject.end()) {
+ if (!_valIter->_value->_isConstVar && (!_valIter->_value->isNative() || includingNatives)) _valIter->_value->setNULL();
+ _valIter++;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::isNULL() {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->isNULL();
+
+ return (_type == VAL_NULL);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::isNative() {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->isNative();
+
+ return (_type == VAL_NATIVE);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::isString() {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->isString();
+
+ return (_type == VAL_STRING);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::isFloat() {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->isFloat();
+
+ return (_type == VAL_FLOAT);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::isInt() {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->isInt();
+
+ return (_type == VAL_INT);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::isBool() {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->isBool();
+
+ return (_type == VAL_BOOL);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::isObject() {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->isObject();
+
+ return (_type == VAL_OBJECT);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+TValType CScValue::getTypeTolerant() {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->getType();
+
+ return _type;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::setBool(bool val) {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setBool(val);
+ return;
+ }
+
+ if (_type == VAL_NATIVE) {
+ _valNative->scSetBool(val);
+ return;
+ }
+
+ _valBool = val;
+ _type = VAL_BOOL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::setInt(int val) {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setInt(val);
+ return;
+ }
+
+ if (_type == VAL_NATIVE) {
+ _valNative->scSetInt(val);
+ return;
+ }
+
+ _valInt = val;
+ _type = VAL_INT;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::setFloat(double val) {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setFloat(val);
+ return;
+ }
+
+ if (_type == VAL_NATIVE) {
+ _valNative->scSetFloat(val);
+ return;
+ }
+
+ _valFloat = val;
+ _type = VAL_FLOAT;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::setString(const char *val) {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setString(val);
+ return;
+ }
+
+ if (_type == VAL_NATIVE) {
+ _valNative->scSetString(val);
+ return;
+ }
+
+ setStringVal(val);
+ if (_valString) _type = VAL_STRING;
+ else _type = VAL_NULL;
+}
+
+void CScValue::setString(const Common::String &val) {
+ setString(val.c_str());
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::setStringVal(const char *val) {
+ if (_valString) {
+ delete [] _valString;
+ _valString = NULL;
+ }
+
+ if (val == NULL) {
+ _valString = NULL;
+ return;
+ }
+
+ _valString = new char [strlen(val) + 1];
+ if (_valString) {
+ strcpy(_valString, val);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::setNULL() {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setNULL();
+ return;
+ }
+
+ if (_valNative && !_persistent) {
+ _valNative->_refCount--;
+ if (_valNative->_refCount <= 0) delete _valNative;
+ }
+ _valNative = NULL;
+ deleteProps();
+
+ _type = VAL_NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::setNative(CBScriptable *val, bool persistent) {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setNative(val, persistent);
+ return;
+ }
+
+ if (val == NULL) {
+ setNULL();
+ } else {
+ if (_valNative && !_persistent) {
+ _valNative->_refCount--;
+ if (_valNative->_refCount <= 0) {
+ if (_valNative != val) delete _valNative;
+ _valNative = NULL;
+ }
+ }
+
+ _type = VAL_NATIVE;
+ _persistent = persistent;
+
+ _valNative = val;
+ if (_valNative && !_persistent) _valNative->_refCount++;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::setObject() {
+ if (_type == VAL_VARIABLE_REF) {
+ _valRef->setObject();
+ return;
+ }
+
+ deleteProps();
+ _type = VAL_OBJECT;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::setReference(CScValue *val) {
+ _valRef = val;
+ _type = VAL_VARIABLE_REF;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::getBool(bool defaultVal) {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->getBool();
+
+ switch (_type) {
+ case VAL_BOOL:
+ return _valBool;
+
+ case VAL_NATIVE:
+ return _valNative->scToBool();
+
+ case VAL_INT:
+ return (_valInt != 0);
+
+ case VAL_FLOAT:
+ return (_valFloat != 0.0f);
+
+ case VAL_STRING:
+ return (scumm_stricmp(_valString, "1") == 0 || scumm_stricmp(_valString, "yes") == 0 || scumm_stricmp(_valString, "true") == 0);
+
+ default:
+ return defaultVal;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CScValue::getInt(int defaultVal) {
+ if (_type == VAL_VARIABLE_REF) return _valRef->getInt();
+
+ switch (_type) {
+ case VAL_BOOL:
+ return _valBool ? 1 : 0;
+
+ case VAL_NATIVE:
+ return _valNative->scToInt();
+
+ case VAL_INT:
+ return _valInt;
+
+ case VAL_FLOAT:
+ return (int)_valFloat;
+
+ case VAL_STRING:
+ return atoi(_valString);
+
+ default:
+ return defaultVal;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double CScValue::getFloat(double defaultVal) {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->getFloat();
+
+ switch (_type) {
+ case VAL_BOOL:
+ return _valBool ? 1.0f : 0.0f;
+
+ case VAL_NATIVE:
+ return _valNative->scToFloat();
+
+ case VAL_INT:
+ return (double)_valInt;
+
+ case VAL_FLOAT:
+ return _valFloat;
+
+ case VAL_STRING:
+ return atof(_valString);
+
+ default:
+ return defaultVal;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void *CScValue::getMemBuffer() {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->getMemBuffer();
+
+ if (_type == VAL_NATIVE)
+ return _valNative->scToMemBuffer();
+ else return (void *)NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *CScValue::getString() {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->getString();
+
+ switch (_type) {
+ case VAL_OBJECT:
+ setStringVal("[object]");
+ break;
+
+ case VAL_NULL:
+ setStringVal("[null]");
+ break;
+
+ case VAL_NATIVE: {
+ const char *strVal = _valNative->scToString();
+ setStringVal(strVal);
+ return strVal;
+ break;
+ }
+
+ case VAL_BOOL:
+ setStringVal(_valBool ? "yes" : "no");
+ break;
+
+ case VAL_INT: {
+ char dummy[50];
+ sprintf(dummy, "%d", _valInt);
+ setStringVal(dummy);
+ break;
+ }
+
+ case VAL_FLOAT: {
+ char dummy[50];
+ sprintf(dummy, "%f", _valFloat);
+ setStringVal(dummy);
+ break;
+ }
+
+ case VAL_STRING:
+ break;
+
+ default:
+ setStringVal("");
+ }
+
+ return _valString;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CBScriptable *CScValue::getNative() {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->getNative();
+
+ if (_type == VAL_NATIVE) return _valNative;
+ else return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+TValType CScValue::getType() {
+ return _type;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::copy(CScValue *orig, bool copyWhole) {
+ _gameRef = orig->_gameRef;
+
+ if (_valNative && !_persistent) {
+ _valNative->_refCount--;
+ if (_valNative->_refCount <= 0) {
+ if (_valNative != orig->_valNative) delete _valNative;
+ _valNative = NULL;
+ }
+ }
+
+ if (orig->_type == VAL_VARIABLE_REF && orig->_valRef && copyWhole) orig = orig->_valRef;
+
+ cleanup(true);
+
+ _type = orig->_type;
+ _valBool = orig->_valBool;
+ _valInt = orig->_valInt;
+ _valFloat = orig->_valFloat;
+ setStringVal(orig->_valString);
+
+ _valRef = orig->_valRef;
+ _persistent = orig->_persistent;
+
+ _valNative = orig->_valNative;
+ if (_valNative && !_persistent) _valNative->_refCount++;
+//!!!! ref->native++
+
+ // copy properties
+ if (orig->_type == VAL_OBJECT && orig->_valObject.size() > 0) {
+ orig->_valIter = orig->_valObject.begin();
+ while (orig->_valIter != orig->_valObject.end()) {
+ _valObject[orig->_valIter->_key] = new CScValue(_gameRef);
+ _valObject[orig->_valIter->_key]->copy(orig->_valIter->_value);
+ orig->_valIter++;
+ }
+ } else _valObject.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::setValue(CScValue *val) {
+ if (val->_type == VAL_VARIABLE_REF) {
+ setValue(val->_valRef);
+ return;
+ }
+
+ // if being assigned a simple type, preserve native state
+ if (_type == VAL_NATIVE && (val->_type == VAL_INT || val->_type == VAL_STRING || val->_type == VAL_BOOL)) {
+ switch (val->_type) {
+ case VAL_INT:
+ _valNative->scSetInt(val->getInt());
+ break;
+ case VAL_FLOAT:
+ _valNative->scSetFloat(val->getFloat());
+ break;
+ case VAL_BOOL:
+ _valNative->scSetBool(val->getBool());
+ break;
+ case VAL_STRING:
+ _valNative->scSetString(val->getString());
+ break;
+ default:
+ warning("CScValue::setValue - unhandled enum");
+ break;
+ }
+ }
+ // otherwise just copy everything
+ else copy(val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::persist(CBPersistMgr *persistMgr) {
+ persistMgr->transfer(TMEMBER(_gameRef));
+
+ persistMgr->transfer(TMEMBER(_persistent));
+ persistMgr->transfer(TMEMBER(_isConstVar));
+ persistMgr->transfer(TMEMBER_INT(_type));
+ persistMgr->transfer(TMEMBER(_valBool));
+ persistMgr->transfer(TMEMBER(_valFloat));
+ persistMgr->transfer(TMEMBER(_valInt));
+ persistMgr->transfer(TMEMBER(_valNative));
+
+ int size;
+ const char *str;
+ if (persistMgr->_saving) {
+ size = _valObject.size();
+ persistMgr->transfer("", &size);
+ _valIter = _valObject.begin();
+ while (_valIter != _valObject.end()) {
+ str = _valIter->_key.c_str();
+ persistMgr->transfer("", &str);
+ persistMgr->transfer("", &_valIter->_value);
+
+ _valIter++;
+ }
+ } else {
+ CScValue *val;
+ persistMgr->transfer("", &size);
+ for (int i = 0; i < size; i++) {
+ persistMgr->transfer("", &str);
+ persistMgr->transfer("", &val);
+
+ _valObject[str] = val;
+ delete [] str;
+ }
+ }
+
+ persistMgr->transfer(TMEMBER(_valRef));
+ persistMgr->transfer(TMEMBER(_valString));
+
+ /*
+ FILE* f = fopen("c:\\val.log", "a+");
+ switch(_type)
+ {
+ case VAL_STRING:
+ fprintf(f, "str %s\n", _valString);
+ break;
+
+ case VAL_INT:
+ fprintf(f, "int %d\n", _valInt);
+ break;
+
+ case VAL_BOOL:
+ fprintf(f, "bool %d\n", _valBool);
+ break;
+
+ case VAL_NULL:
+ fprintf(f, "null\n");
+ break;
+
+ case VAL_NATIVE:
+ fprintf(f, "native\n");
+ break;
+
+ case VAL_VARIABLE_REF:
+ fprintf(f, "ref\n");
+ break;
+
+ case VAL_OBJECT:
+ fprintf(f, "obj\n");
+ break;
+
+ case VAL_FLOAT:
+ fprintf(f, "float\n");
+ break;
+
+ }
+ fclose(f);
+ */
+
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::saveAsText(CBDynBuffer *buffer, int indent) {
+ _valIter = _valObject.begin();
+ while (_valIter != _valObject.end()) {
+ buffer->putTextIndent(indent, "PROPERTY {\n");
+ buffer->putTextIndent(indent + 2, "NAME=\"%s\"\n", _valIter->_key.c_str());
+ buffer->putTextIndent(indent + 2, "VALUE=\"%s\"\n", _valIter->_value->getString());
+ buffer->putTextIndent(indent, "}\n\n");
+
+ _valIter++;
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// -1 ... left is less, 0 ... equals, 1 ... left is greater
+int CScValue::compare(CScValue *val1, CScValue *val2) {
+ // both natives?
+ if (val1->isNative() && val2->isNative()) {
+ // same class?
+ if (strcmp(val1->getNative()->getClassName(), val2->getNative()->getClassName()) == 0) {
+ return val1->getNative()->scCompare(val2->getNative());
+ } else return strcmp(val1->getString(), val2->getString());
+ }
+
+ // both objects?
+ if (val1->isObject() && val2->isObject()) return -1;
+
+
+ // null states
+ if (val1->isNULL() && !val2->isNULL()) return -1;
+ else if (!val1->isNULL() && val2->isNULL()) return 1;
+ else if (val1->isNULL() && val2->isNULL()) return 0;
+
+ // one of them is string? convert both to string
+ if (val1->isString() || val2->isString()) return strcmp(val1->getString(), val2->getString());
+
+ // one of them is float?
+ if (val1->isFloat() || val2->isFloat()) {
+ if (val1->getFloat() < val2->getFloat()) return -1;
+ else if (val1->getFloat() > val2->getFloat()) return 1;
+ else return 0;
+ }
+
+ // otherwise compare as int's
+ if (val1->getInt() < val2->getInt()) return -1;
+ else if (val1->getInt() > val2->getInt()) return 1;
+ else return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CScValue::compareStrict(CScValue *val1, CScValue *val2) {
+ if (val1->getTypeTolerant() != val2->getTypeTolerant()) return -1;
+ else return CScValue::compare(val1, val2);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::dbgSendVariables(IWmeDebugClient *client, EWmeDebuggerVariableType type, CScScript *script, unsigned int scopeID) {
+ _valIter = _valObject.begin();
+ while (_valIter != _valObject.end()) {
+ client->onVariableInit(type, script, scopeID, _valIter->_value, _valIter->_key.c_str());
+ _valIter++;
+ }
+ return STATUS_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::setProperty(const char *propName, int value) {
+ CScValue *val = new CScValue(_gameRef, value);
+ bool ret = DID_SUCCEED(setProp(propName, val));
+ delete val;
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::setProperty(const char *propName, const char *value) {
+ CScValue *val = new CScValue(_gameRef, value);
+ bool ret = DID_SUCCEED(setProp(propName, val));
+ delete val;
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::setProperty(const char *propName, double value) {
+ CScValue *val = new CScValue(_gameRef, value);
+ bool ret = DID_SUCCEED(setProp(propName, val));
+ delete val;
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::setProperty(const char *propName, bool value) {
+ CScValue *val = new CScValue(_gameRef, value);
+ bool ret = DID_SUCCEED(setProp(propName, val));
+ delete val;
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::setProperty(const char *propName) {
+ CScValue *val = new CScValue(_gameRef);
+ bool ret = DID_SUCCEED(setProp(propName, val));
+ delete val;
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// IWmeDebugProp
+//////////////////////////////////////////////////////////////////////////
+EWmeDebuggerPropType CScValue::dbgGetType() {
+ switch (getType()) {
+ case VAL_NULL:
+ return WME_DBGPROP_NULL;
+ case VAL_STRING:
+ return WME_DBGPROP_STRING;
+ case VAL_INT:
+ return WME_DBGPROP_INT;
+ case VAL_BOOL:
+ return WME_DBGPROP_BOOL;
+ case VAL_FLOAT:
+ return WME_DBGPROP_FLOAT;
+ case VAL_OBJECT:
+ return WME_DBGPROP_OBJECT;
+ case VAL_NATIVE:
+ return WME_DBGPROP_NATIVE;
+ default:
+ return WME_DBGPROP_UNKNOWN;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+int CScValue::dbgGetValInt() {
+ return getInt();
+}
+
+//////////////////////////////////////////////////////////////////////////
+double CScValue::dbgGetValFloat() {
+ return getFloat();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::dbgGetValBool() {
+ return getBool();
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *CScValue::dbgGetValString() {
+ return getString();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IWmeDebugObject *CScValue::dbgGetValNative() {
+ return getNative();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::dbgSetVal(int value) {
+ setInt(value);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::dbgSetVal(double value) {
+ setFloat(value);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::dbgSetVal(bool value) {
+ setBool(value);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::dbgSetVal(const char *value) {
+ setString(value);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::dbgSetVal() {
+ setNULL();
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CScValue::dbgGetNumProperties() {
+ if (_valNative && _valNative->_scProp)
+ return _valNative->_scProp->dbgGetNumProperties();
+ else return _valObject.size();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::dbgGetProperty(int index, const char **name, IWmeDebugProp **value) {
+ if (_valNative && _valNative->_scProp)
+ return _valNative->_scProp->dbgGetProperty(index, name, value);
+ else {
+ int count = 0;
+ _valIter = _valObject.begin();
+ while (_valIter != _valObject.end()) {
+ if (count == index) {
+ *name = _valIter->_key.c_str();
+ *value = _valIter->_value;
+ return true;
+ }
+ _valIter++;
+ count++;
+ }
+ return false;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::dbgGetDescription(char *buf, int bufSize) {
+ if (_type == VAL_VARIABLE_REF)
+ return _valRef->dbgGetDescription(buf, bufSize);
+
+ if (_type == VAL_NATIVE) {
+ _valNative->scDebuggerDesc(buf, bufSize);
+ } else {
+ strncpy(buf, getString(), bufSize);
+ }
+ return true;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/base/scriptables/ScValue.h b/engines/wintermute/base/scriptables/ScValue.h
new file mode 100644
index 0000000000..c66a60c22a
--- /dev/null
+++ b/engines/wintermute/base/scriptables/ScValue.h
@@ -0,0 +1,141 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SCVALUE_H
+#define WINTERMUTE_SCVALUE_H
+
+
+#include "engines/wintermute/base/BBase.h"
+#include "engines/wintermute/persistent.h"
+#include "engines/wintermute/dcscript.h" // Added by ClassView
+#include "engines/wintermute/wme_debugger.h"
+#include "common/str.h"
+
+namespace WinterMute {
+
+class CScScript;
+class CBScriptable;
+
+class CScValue : public CBBase, public IWmeDebugProp {
+public:
+ bool dbgSendVariables(IWmeDebugClient *client, EWmeDebuggerVariableType type, CScScript *script, unsigned int scopeID);
+
+ static int compare(CScValue *val1, CScValue *val2);
+ static int compareStrict(CScValue *val1, CScValue *val2);
+ TValType getTypeTolerant();
+ void cleanup(bool ignoreNatives = false);
+ DECLARE_PERSISTENT(CScValue, CBBase)
+
+ bool _isConstVar;
+ bool saveAsText(CBDynBuffer *buffer, int indent);
+ void setValue(CScValue *val);
+ bool _persistent;
+ bool propExists(const char *name);
+ void copy(CScValue *orig, bool copyWhole = false);
+ void setStringVal(const char *val);
+ TValType getType();
+ bool getBool(bool defaultVal = false);
+ int getInt(int defaultVal = 0);
+ double getFloat(double defaultVal = 0.0f);
+ const char *getString();
+ void *getMemBuffer();
+ CBScriptable *getNative();
+ bool deleteProp(const char *name);
+ void deleteProps();
+ void CleanProps(bool includingNatives);
+ void setBool(bool val);
+ void setInt(int val);
+ void setFloat(double val);
+ void setString(const char *val);
+ void setString(const Common::String &val);
+ void setNULL();
+ void setNative(CBScriptable *val, bool persistent = false);
+ void setObject();
+ void setReference(CScValue *val);
+ bool isNULL();
+ bool isNative();
+ bool isString();
+ bool isBool();
+ bool isFloat();
+ bool isInt();
+ bool isObject();
+ bool setProp(const char *name, CScValue *val, bool copyWhole = false, bool setAsConst = false);
+ CScValue *getProp(const char *name);
+ CBScriptable *_valNative;
+ CScValue *_valRef;
+protected:
+ bool _valBool;
+ int _valInt;
+ double _valFloat;
+ char *_valString;
+public:
+ TValType _type;
+ CScValue(CBGame *inGame);
+ CScValue(CBGame *inGame, bool Val);
+ CScValue(CBGame *inGame, int Val);
+ CScValue(CBGame *inGame, double Val);
+ CScValue(CBGame *inGame, const char *Val);
+ virtual ~CScValue();
+ Common::HashMap<Common::String, CScValue *> _valObject;
+ Common::HashMap<Common::String, CScValue *>::iterator _valIter;
+
+ bool setProperty(const char *propName, int value);
+ bool setProperty(const char *propName, const char *value);
+ bool setProperty(const char *propName, double value);
+ bool setProperty(const char *propName, bool value);
+ bool setProperty(const char *propName);
+
+
+// IWmeDebugProp interface implementation
+public:
+ virtual EWmeDebuggerPropType dbgGetType();
+
+ // getters
+ virtual int dbgGetValInt();
+ virtual double dbgGetValFloat();
+ virtual bool dbgGetValBool();
+ virtual const char *dbgGetValString();
+ virtual IWmeDebugObject *dbgGetValNative();
+
+ // setters
+ virtual bool dbgSetVal(int value);
+ virtual bool dbgSetVal(double value);
+ virtual bool dbgSetVal(bool value);
+ virtual bool dbgSetVal(const char *value);
+ virtual bool dbgSetVal();
+
+ // properties
+ virtual int dbgGetNumProperties();
+ virtual bool dbgGetProperty(int index, const char **mame, IWmeDebugProp **value);
+
+ virtual bool dbgGetDescription(char *buf, int bufSize);
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/base/scriptables/SxObject.cpp b/engines/wintermute/base/scriptables/SxObject.cpp
new file mode 100644
index 0000000000..ba961ed2ae
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SxObject.cpp
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#include "SxObject.h"
+#include "ScValue.h"
+#include "ScStack.h"
+
+namespace WinterMute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_PERSISTENT(CSXObject, false)
+
+CBScriptable *makeSXObject(CBGame *inGame, CScStack *stack) {
+ return new CSXObject(inGame, stack);
+}
+
+//////////////////////////////////////////////////////////////////////////
+CSXObject::CSXObject(CBGame *inGame, CScStack *stack): CBObject(inGame) {
+ int numParams = stack->pop()->getInt(0);
+ for (int i = 0; i < numParams; i++) {
+ addScript(stack->pop()->getString());
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXObject::~CSXObject() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXObject::persist(CBPersistMgr *persistMgr) {
+ CBObject::persist(persistMgr);
+
+ return STATUS_OK;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/base/scriptables/SxObject.h b/engines/wintermute/base/scriptables/SxObject.h
new file mode 100644
index 0000000000..b4ec7c6cde
--- /dev/null
+++ b/engines/wintermute/base/scriptables/SxObject.h
@@ -0,0 +1,47 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+/*
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
+
+#ifndef WINTERMUTE_SXOBJECT_H
+#define WINTERMUTE_SXOBJECT_H
+
+
+#include "engines/wintermute/base/BObject.h"
+
+namespace WinterMute {
+
+class CSXObject : public CBObject {
+public:
+ DECLARE_PERSISTENT(CSXObject, CBObject)
+ CSXObject(CBGame *inGame, CScStack *Stack);
+ virtual ~CSXObject();
+
+};
+
+} // end of namespace WinterMute
+
+#endif