aboutsummaryrefslogtreecommitdiff
path: root/engines/wintermute/Base/scriptables
diff options
context:
space:
mode:
authorEinar Johan Trøan Sømåen2012-06-02 02:05:11 +0200
committerEinar Johan Trøan Sømåen2012-06-02 13:09:24 +0200
commite6729615ea471891be17d5681b63fe3492f127bd (patch)
tree9707aaf8cd8bb4e826d349f2a89aac193c227b95 /engines/wintermute/Base/scriptables
parent2317b3538fc5148dc9c7b3d26c2d60fdb06c85e4 (diff)
downloadscummvm-rg350-e6729615ea471891be17d5681b63fe3492f127bd.tar.gz
scummvm-rg350-e6729615ea471891be17d5681b63fe3492f127bd.tar.bz2
scummvm-rg350-e6729615ea471891be17d5681b63fe3492f127bd.zip
WINTERMUTE: Move the B and Part files into Base/
Diffstat (limited to 'engines/wintermute/Base/scriptables')
-rw-r--r--engines/wintermute/Base/scriptables/SXArray.cpp234
-rw-r--r--engines/wintermute/Base/scriptables/SXArray.h54
-rw-r--r--engines/wintermute/Base/scriptables/SXDate.cpp277
-rw-r--r--engines/wintermute/Base/scriptables/SXDate.h53
-rw-r--r--engines/wintermute/Base/scriptables/SXFile.cpp723
-rw-r--r--engines/wintermute/Base/scriptables/SXFile.h63
-rw-r--r--engines/wintermute/Base/scriptables/SXMath.cpp290
-rw-r--r--engines/wintermute/Base/scriptables/SXMath.h53
-rw-r--r--engines/wintermute/Base/scriptables/SXMemBuffer.cpp479
-rw-r--r--engines/wintermute/Base/scriptables/SXMemBuffer.h59
-rw-r--r--engines/wintermute/Base/scriptables/SXStore.cpp511
-rw-r--r--engines/wintermute/Base/scriptables/SXStore.h175
-rw-r--r--engines/wintermute/Base/scriptables/SXString.cpp394
-rw-r--r--engines/wintermute/Base/scriptables/SXString.h58
-rw-r--r--engines/wintermute/Base/scriptables/ScEngine.cpp854
-rw-r--r--engines/wintermute/Base/scriptables/ScEngine.h174
-rw-r--r--engines/wintermute/Base/scriptables/ScScript.cpp1600
-rw-r--r--engines/wintermute/Base/scriptables/ScScript.h185
-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.cpp1028
-rw-r--r--engines/wintermute/Base/scriptables/ScValue.h142
-rw-r--r--engines/wintermute/Base/scriptables/SxObject.cpp63
-rw-r--r--engines/wintermute/Base/scriptables/SxObject.h47
24 files changed, 7808 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..bd8962cc0c
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/SXArray.cpp
@@ -0,0 +1,234 @@
+/* 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/SysInstance.h"
+#include "engines/wintermute/Base/scriptables/SXArray.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXArray, false)
+
+//////////////////////////////////////////////////////////////////////////
+CSXArray::CSXArray(CBGame *inGame, CScStack *Stack): CBScriptable(inGame) {
+ _length = 0;
+ _values = new CScValue(Game);
+
+ 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(Game);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXArray::~CSXArray() {
+ delete _values;
+ _values = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *CSXArray::ScToString() {
+ static char Dummy[32768];
+ 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;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_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 S_OK;
+ }
+
+ else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+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;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_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 E_FAIL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXArray::Persist(CBPersistMgr *PersistMgr) {
+ CBScriptable::Persist(PersistMgr);
+
+ PersistMgr->Transfer(TMEMBER(_length));
+ PersistMgr->Transfer(TMEMBER(_values));
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXArray::ValidNumber(const char *OrigStr, char *OutStr) {
+ bool IsNumber = true;
+ for (int 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;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXArray::Push(CScValue *Val) {
+ char ParamName[20];
+ _length++;
+ sprintf(ParamName, "%d", _length - 1);
+ _values->SetProp(ParamName, Val, true);
+ return S_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..d1cc4ce4db
--- /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:
+ HRESULT 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);
+ HRESULT ScSetProperty(const char *Name, CScValue *Value);
+ HRESULT 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..e9cfc4b406
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/SXDate.cpp
@@ -0,0 +1,277 @@
+/* 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)
+
+//////////////////////////////////////////////////////////////////////////
+CSXDate::CSXDate(CBGame *inGame, CScStack *Stack): CBScriptable(inGame) {
+ Stack->CorrectParams(6);
+#if 0
+ memset(&_tm, 0, sizeof(_tm));
+
+ CScValue *valYear = Stack->Pop();
+ _tm.t_year = valYear->GetInt() - 1900;
+ _tm.t_mon = Stack->Pop()->GetInt() - 1;
+ _tm.t_mday = Stack->Pop()->GetInt();
+ _tm.t_hour = Stack->Pop()->GetInt();
+ _tm.t_min = Stack->Pop()->GetInt();
+ _tm.t_sec = Stack->Pop()->GetInt();
+
+ if (valYear->IsNULL()) {
+ time_t TimeNow;
+ time(&TimeNow);
+ memcpy(&_tm, localtime(&TimeNow), sizeof(_tm));
+ }
+
+ mktime(&_tm);
+#endif
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXDate::~CSXDate() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+const char *CSXDate::ScToString() {
+#if 0
+ return asctime(&_tm);
+#endif
+ return "";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXDate::ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, const char *Name) {
+#if 0
+ //////////////////////////////////////////////////////////////////////////
+ // GetYear
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "GetYear") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(_tm.t_year + 1900);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetMonth
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetMonth") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(_tm.t_mon + 1);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetDate
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetDate") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(_tm.t_mday);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetHours
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetHours") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(_tm.t_hour);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetMinutes
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetMinutes") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(_tm.t_min);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetSeconds
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetSeconds") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(_tm.t_sec);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetWeekday
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetWeekday") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(_tm.t_wday);
+ return S_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetYear
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetYear") == 0) {
+ Stack->CorrectParams(1);
+ _tm.t_year = Stack->Pop()->GetInt() - 1900;
+ mktime(&_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetMonth
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetMonth") == 0) {
+ Stack->CorrectParams(1);
+ _tm.t_mon = Stack->Pop()->GetInt() - 1;
+ mktime(&_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetDate
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetDate") == 0) {
+ Stack->CorrectParams(1);
+ _tm.t_mday = Stack->Pop()->GetInt();
+ mktime(&_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetHours
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetHours") == 0) {
+ Stack->CorrectParams(1);
+ _tm.t_hour = Stack->Pop()->GetInt();
+ mktime(&_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetMinutes
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetMinutes") == 0) {
+ Stack->CorrectParams(1);
+ _tm.t_min = Stack->Pop()->GetInt();
+ mktime(&_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetSeconds
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetSeconds") == 0) {
+ Stack->CorrectParams(1);
+ _tm.t_sec = Stack->Pop()->GetInt();
+ mktime(&_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetCurrentTime
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetCurrentTime") == 0) {
+ Stack->CorrectParams(0);
+ time_t TimeNow;
+ time(&TimeNow);
+ memcpy(&_tm, localtime(&TimeNow), sizeof(_tm));
+ mktime(&_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+
+ else
+#endif
+ return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CSXDate::ScGetProperty(const char *Name) {
+ _scValue->SetNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "Type") == 0) {
+ _scValue->SetString("date");
+ return _scValue;
+ }
+
+ else return _scValue;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXDate::ScSetProperty(const char *Name, CScValue *Value) {
+ /*
+ //////////////////////////////////////////////////////////////////////////
+ // Name
+ //////////////////////////////////////////////////////////////////////////
+ if(strcmp(Name, "Name")==0){
+ SetName(Value->GetString());
+ return S_OK;
+ }
+
+ else*/ return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXDate::Persist(CBPersistMgr *PersistMgr) {
+
+ CBScriptable::Persist(PersistMgr);
+#if 0
+ if (PersistMgr->_saving)
+ PersistMgr->PutBytes((byte *)&_tm, sizeof(_tm));
+ else
+ PersistMgr->GetBytes((byte *)&_tm, sizeof(_tm));
+#endif
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CSXDate::ScCompare(CBScriptable *Value) {
+#if 0
+ time_t time1 = mktime(&_tm);
+ time_t time2 = mktime(&((CSXDate *)Value)->_tm);
+
+ if (time1 < time2) return -1;
+ else if (time1 > time2) return 1;
+ else
+#endif
+ return 0;
+}
+
+} // 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..7d4d29af93
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/SXDate.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_SXDATE_H
+#define WINTERMUTE_SXDATE_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);
+ HRESULT ScSetProperty(const char *Name, CScValue *Value);
+ HRESULT ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, const char *Name);
+ const char *ScToString();
+ char *_string;
+ //struct tm _tm; // TODO!
+};
+
+} // 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..ffff0e736d
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/SXFile.cpp
@@ -0,0 +1,723 @@
+/* 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/SysClassRegistry.h"
+#include "engines/wintermute/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.h"
+#include "engines/wintermute/Base/BGame.h"
+#include "engines/wintermute/Base/BFile.h"
+#include "engines/wintermute/Base/BFileManager.h"
+#include "engines/wintermute/PlatformSDL.h"
+#include "engines/wintermute/Base/scriptables/SXFile.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXFile, false)
+
+//////////////////////////////////////////////////////////////////////////
+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) {
+ Game->_fileManager->CloseFile(_readFile);
+ _readFile = NULL;
+ }
+ if (_writeFile) {
+ fclose(_writeFile);
+ _writeFile = NULL;
+ }
+ _mode = 0;
+ _textMode = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *CSXFile::ScToString() {
+ if (_filename) return _filename;
+ else return "[file object]";
+}
+
+#define FILE_BUFFER_SIZE 32768
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_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 = Game->_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 = fopen(_filename, "w+");
+ else _writeFile = fopen(_filename, "a+");
+ } else {
+ if (_mode == 2) _writeFile = fopen(_filename, "wb+");
+ else _writeFile = fopen(_filename, "ab+");
+ }
+
+ 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 S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Close
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Close") == 0) {
+ Stack->CorrectParams(0);
+ Close();
+ Stack->PushNULL();
+ return S_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 S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Delete
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Delete") == 0) {
+ Stack->CorrectParams(0);
+ Close();
+ Stack->PushBool(CBPlatform::DeleteFile(_filename) != false);
+ return S_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 S_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 S_OK;
+ }
+ uint32 BufSize = FILE_BUFFER_SIZE;
+ byte *Buf = (byte *)malloc(BufSize);
+ uint32 Counter = 0;
+ byte b;
+ bool FoundNewLine = false;
+ HRESULT Ret = E_FAIL;
+ do {
+ Ret = _readFile->Read(&b, 1);
+ if (FAILED(Ret)) 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 (SUCCEEDED(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 S_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 S_OK;
+ }
+ uint32 BufSize = FILE_BUFFER_SIZE;
+ byte *Buf = (byte *)malloc(BufSize);
+ uint32 Counter = 0;
+ byte b;
+
+ HRESULT Ret = E_FAIL;
+ while (Counter < TextLen) {
+ Ret = _readFile->Read(&b, 1);
+ if (FAILED(Ret)) 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 S_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 S_OK;
+ }
+ if (strcmp(Name, "WriteLine") == 0)
+ fprintf(_writeFile, "%s\n", Line);
+ else
+ fprintf(_writeFile, "%s", Line);
+
+ Stack->PushBool(true);
+
+ return S_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 S_OK;
+ }
+ bool Val;
+ if (SUCCEEDED(_readFile->Read(&Val, sizeof(bool)))) Stack->PushBool(Val);
+ else Stack->PushNULL();
+
+ return S_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 S_OK;
+ }
+ byte Val;
+ if (SUCCEEDED(_readFile->Read(&Val, sizeof(byte)))) Stack->PushInt(Val);
+ else Stack->PushNULL();
+
+ return S_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 S_OK;
+ }
+ short Val;
+ if (SUCCEEDED(_readFile->Read(&Val, sizeof(short)))) Stack->PushInt(65536 + Val);
+ else Stack->PushNULL();
+
+ return S_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 S_OK;
+ }
+ int Val;
+ if (SUCCEEDED(_readFile->Read(&Val, sizeof(int)))) Stack->PushInt(Val);
+ else Stack->PushNULL();
+
+ return S_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 S_OK;
+ }
+ float Val;
+ if (SUCCEEDED(_readFile->Read(&Val, sizeof(float)))) Stack->PushFloat(Val);
+ else Stack->PushNULL();
+
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ReadDouble
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "ReadDouble") == 0) {
+ Stack->CorrectParams(0);
+ if (_textMode || !_readFile) {
+ Script->RuntimeError("File.%s: File must be open for reading in binary mode.", Name);
+ Stack->PushNULL();
+ return S_OK;
+ }
+ double Val;
+ if (SUCCEEDED(_readFile->Read(&Val, sizeof(double)))) Stack->PushFloat(Val);
+ else Stack->PushNULL();
+
+ return S_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 S_OK;
+ }
+ uint32 Size;
+ if (SUCCEEDED(_readFile->Read(&Size, sizeof(uint32)))) {
+ byte *Str = new byte[Size + 1];
+ if (Str) {
+ if (SUCCEEDED(_readFile->Read(Str, Size))) {
+ Str[Size] = '\0';
+ Stack->PushString((char *)Str);
+ }
+ delete [] Str;
+ } else Stack->PushNULL();
+ } else Stack->PushNULL();
+
+ return S_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 S_OK;
+ }
+ fwrite(&Val, sizeof(Val), 1, _writeFile);
+ Stack->PushBool(true);
+
+ return S_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 S_OK;
+ }
+ fwrite(&Val, sizeof(Val), 1, _writeFile);
+ Stack->PushBool(true);
+
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteShort
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "WriteShort") == 0) {
+ Stack->CorrectParams(1);
+ short 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 S_OK;
+ }
+ fwrite(&Val, sizeof(Val), 1, _writeFile);
+ Stack->PushBool(true);
+
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteInt / WriteLong
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "WriteInt") == 0 || strcmp(Name, "WriteLong") == 0) {
+ Stack->CorrectParams(1);
+ int 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 S_OK;
+ }
+ fwrite(&Val, sizeof(Val), 1, _writeFile);
+ Stack->PushBool(true);
+
+ return S_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 S_OK;
+ }
+ fwrite(&Val, sizeof(Val), 1, _writeFile);
+ Stack->PushBool(true);
+
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // WriteDouble
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "WriteDouble") == 0) {
+ 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 S_OK;
+ }
+ fwrite(&Val, sizeof(Val), 1, _writeFile);
+ Stack->PushBool(true);
+
+ return S_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 S_OK;
+ }
+
+ uint32 Size = strlen(Val);
+ fwrite(&Size, sizeof(Size), 1, _writeFile);
+ fwrite(Val, Size, 1, _writeFile);
+
+ Stack->PushBool(true);
+
+ return S_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);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_OK;
+ }
+ else*/ return CBScriptable::ScSetProperty(Name, Value);
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CSXFile::GetPos() {
+ if (_mode == 1 && _readFile) return _readFile->GetPos();
+ else if ((_mode == 2 || _mode == 3) && _writeFile) return ftell(_writeFile);
+ else return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXFile::SetPos(uint32 Pos, TSeek Origin) {
+ if (_mode == 1 && _readFile) return SUCCEEDED(_readFile->Seek(Pos, Origin));
+ else if ((_mode == 2 || _mode == 3) && _writeFile) return fseek(_writeFile, Pos, (int)Origin) == 0;
+ else return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CSXFile::GetLength() {
+ if (_mode == 1 && _readFile) return _readFile->GetSize();
+ else if ((_mode == 2 || _mode == 3) && _writeFile) {
+ uint32 CurrentPos = ftell(_writeFile);
+ fseek(_writeFile, 0, SEEK_END);
+ int Ret = ftell(_writeFile);
+ fseek(_writeFile, CurrentPos, SEEK_SET);
+ return Ret;
+ } else return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 = Game->_fileManager->OpenFile(_filename);
+ if (!_readFile) Close();
+ }
+ // open for writing / appending
+ else {
+ if (_textMode) {
+ if (_mode == 2) _writeFile = fopen(_filename, "w+");
+ else _writeFile = fopen(_filename, "a+");
+ } else {
+ if (_mode == 2) _writeFile = fopen(_filename, "wb+");
+ else _writeFile = fopen(_filename, "ab+");
+ }
+ if (_writeFile) Close();
+ }
+ SetPos(Pos);
+ }
+ }
+
+ return S_OK;
+}
+
+} // 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..f05a0e11ec
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/SXFile.h
@@ -0,0 +1,63 @@
+/* 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"
+
+namespace WinterMute {
+
+class CBFile;
+
+class CSXFile : public CBScriptable {
+public:
+ DECLARE_PERSISTENT(CSXFile, CBScriptable)
+ CScValue *ScGetProperty(const char *Name);
+ HRESULT ScSetProperty(const char *Name, CScValue *Value);
+ HRESULT ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, const char *Name);
+ const char *ScToString();
+ CSXFile(CBGame *inGame, CScStack *Stack);
+ virtual ~CSXFile();
+private:
+ CBFile *_readFile;
+ FILE *_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, TSeek Origin = SEEK_TO_BEGIN);
+ char *_filename;
+};
+
+} // 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..8ac70581b9
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/SXMath.cpp
@@ -0,0 +1,290 @@
+/* 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 <cmath>
+
+namespace WinterMute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+
+IMPLEMENT_PERSISTENT(CSXMath, true)
+
+//////////////////////////////////////////////////////////////////////////
+CSXMath::CSXMath(CBGame *inGame): CBScriptable(inGame) {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXMath::~CSXMath() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Acos
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Acos") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(acos(Stack->Pop()->GetFloat()));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Asin
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Asin") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(asin(Stack->Pop()->GetFloat()));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Atan
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Atan") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(atan(Stack->Pop()->GetFloat()));
+ return S_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 S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Ceil
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Ceil") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(ceil(Stack->Pop()->GetFloat()));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Cos
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Cos") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(cos(DegreeToRadian(Stack->Pop()->GetFloat())));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Cosh
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Cosh") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(cosh(DegreeToRadian(Stack->Pop()->GetFloat())));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Exp
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Exp") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(exp(Stack->Pop()->GetFloat()));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Floor
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Floor") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(floor(Stack->Pop()->GetFloat()));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Log
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Log") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(log(Stack->Pop()->GetFloat()));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Log10
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Log10") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(log10(Stack->Pop()->GetFloat()));
+ return S_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 S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sin
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Sin") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(sin(DegreeToRadian(Stack->Pop()->GetFloat())));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sinh
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Sinh") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(sinh(DegreeToRadian(Stack->Pop()->GetFloat())));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Tan
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Tan") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(tan(DegreeToRadian(Stack->Pop()->GetFloat())));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Tanh
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Tanh") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(tanh(DegreeToRadian(Stack->Pop()->GetFloat())));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Sqrt
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Sqrt") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(sqrt(Stack->Pop()->GetFloat()));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DegToRad
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "DegToRad") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(DegreeToRadian(Stack->Pop()->GetFloat()));
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // RadToDeg
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "RadToDeg") == 0) {
+ Stack->CorrectParams(1);
+ Stack->PushFloat(RadianToDegree(Stack->Pop()->GetFloat()));
+ return S_OK;
+ }
+
+ else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+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(PI);
+ return _scValue;
+ }
+
+ else return _scValue;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double CSXMath::DegreeToRadian(double Value) {
+ return Value * (PI / 180.0f);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double CSXMath::RadianToDegree(double Value) {
+ return Value * (180.0f / PI);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXMath::Persist(CBPersistMgr *PersistMgr) {
+
+ CBScriptable::Persist(PersistMgr);
+ return S_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..0e61738161
--- /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 HRESULT 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..dd869477a1
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/SXMemBuffer.cpp
@@ -0,0 +1,479 @@
+/* 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
+ */
+
+#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
+#define FORBIDDEN_SYMBOL_EXCEPTION_fopen
+#define FORBIDDEN_SYMBOL_EXCEPTION_fwrite
+#define FORBIDDEN_SYMBOL_EXCEPTION_fclose
+#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"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXMemBuffer, false)
+
+//////////////////////////////////////////////////////////////////////////
+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 = NULL;
+ _buffer = Buffer;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXMemBuffer::~CSXMemBuffer() {
+ Cleanup();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void *CSXMemBuffer::ScToMemBuffer() {
+ return _buffer;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXMemBuffer::Cleanup() {
+ if (_size) free(_buffer);
+ _buffer = NULL;
+ _size = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 E_FAIL;
+ } else {
+ _buffer = NewBuf;
+ _size = NewSize;
+ }
+ }
+
+ if (_buffer && _size > OldSize) {
+ memset((byte *)_buffer + OldSize, 0, _size - OldSize);
+ }
+ return S_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]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 (SUCCEEDED(Resize(NewSize))) Stack->PushBool(true);
+ else Stack->PushBool(false);
+
+ return S_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 S_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 S_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 S_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 S_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 S_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 S_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 S_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(Game, Pointer);
+ Stack->PushNative(Buf, false);
+ }
+ return S_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 S_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 S_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 S_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 S_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 S_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 S_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 S_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 S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DEBUG_Dump
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "DEBUG_Dump") == 0) {
+ Stack->CorrectParams(0);
+ if (_buffer && _size) {
+ FILE *f = fopen("c:\\!!buffer.bin", "wb");
+ fwrite(_buffer, _size, 1, f);
+ fclose(f);
+ }
+ Stack->PushNULL();
+ return S_OK;
+ }
+
+ else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+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);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_OK;
+ }
+ else*/ return CBScriptable::ScSetProperty(Name, Value);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_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..54074b183d
--- /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);
+ HRESULT ScSetProperty(const char *Name, CScValue *Value);
+ HRESULT 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:
+ HRESULT 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/SXStore.cpp b/engines/wintermute/Base/scriptables/SXStore.cpp
new file mode 100644
index 0000000000..3562392b13
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/SXStore.cpp
@@ -0,0 +1,511 @@
+/* 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/BRegistry.h"
+#include "engines/wintermute/Base/scriptables/SXStore.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/StringUtil.h"
+
+#ifdef __IPHONEOS__
+# include "IOS_StoreKit_interface.h"
+#endif
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXStore, false)
+
+//////////////////////////////////////////////////////////////////////////
+CSXStore::CSXStore(CBGame *inGame) : CBObject(inGame) {
+#ifdef __IPHONEOS__
+ StoreKit_SetExternalData((void *)this);
+#endif
+
+ _eventsEnabled = false;
+ _lastProductRequestOwner = NULL;
+ _lastPurchaseOwner = NULL;
+ _lastRestoreOwner = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXStore::~CSXStore() {
+ Cleanup();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::Cleanup() {
+ SetEventsEnabled(NULL, false);
+
+ for (int i = 0; i < _validProducts.GetSize(); i++) {
+ delete _validProducts[i];
+ }
+ _validProducts.RemoveAll();
+
+
+ for (int i = 0; i < _transactions.GetSize(); i++) {
+ delete _transactions[i];
+ }
+ _transactions.RemoveAll();
+
+
+ _lastProductRequestOwner = _lastPurchaseOwner = _lastRestoreOwner = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXStore::ScCallMethod(CScScript *script, CScStack *stack, CScStack *thisStack, const char *name) {
+ //////////////////////////////////////////////////////////////////////////
+ // EnableEvents
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "EnableEvents") == 0) {
+ stack->CorrectParams(0);
+ SetEventsEnabled(script, true);
+ stack->PushBool(GetEventsEnabled() == true);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // DisableEvents
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "DisableEvents") == 0) {
+ stack->CorrectParams(0);
+ SetEventsEnabled(script, false);
+ stack->PushBool(GetEventsEnabled() == false);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // ValidateProducts
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "ValidateProducts") == 0) {
+ stack->CorrectParams(1);
+ const char *prodIdList = stack->Pop()->GetString();
+ _lastProductRequestOwner = script->_owner;
+ ValidateProducts(prodIdList);
+ stack->PushNULL();
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetValidProduct
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetValidProduct") == 0) {
+ stack->CorrectParams(1);
+ int index = stack->Pop()->GetInt();
+ if (index >= 0 && index < _validProducts.GetSize()) {
+ CScValue *prod = stack->GetPushValue();
+ if (prod) {
+ prod->SetProperty("Id", _validProducts[index]->GetId());
+ prod->SetProperty("Name", _validProducts[index]->GetName());
+ prod->SetProperty("Description", _validProducts[index]->GetDesc());
+ prod->SetProperty("Price", _validProducts[index]->GetPrice());
+ }
+ } else
+ stack->PushNULL();
+
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetInvalidProduct
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetInvalidProduct") == 0) {
+ stack->CorrectParams(1);
+ int index = stack->Pop()->GetInt();
+ if (index >= 0 && index < _invalidProducts.size())
+ stack->PushString(_invalidProducts[index].c_str());
+ else
+ stack->PushNULL();
+
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetTransaction
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "GetTransaction") == 0) {
+ stack->CorrectParams(1);
+ int index = stack->Pop()->GetInt();
+ if (index >= 0 && index < _transactions.GetSize()) {
+ CScValue *trans = stack->GetPushValue();
+ if (trans) {
+ trans->SetProperty("Id", _transactions[index]->GetId());
+ trans->SetProperty("ProductId", _transactions[index]->GetProductId());
+ trans->SetProperty("State", _transactions[index]->GetState());
+ }
+ } else
+ stack->PushNULL();
+
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Purchase
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Purchase") == 0) {
+ stack->CorrectParams(1);
+ const char *prodId = stack->Pop()->GetString();
+ stack->PushBool(Purchase(script, prodId));
+
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // FinishTransaction
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "FinishTransaction") == 0) {
+ stack->CorrectParams(1);
+ const char *transId = stack->Pop()->GetString();
+ stack->PushBool(FinishTransaction(script, transId));
+
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // RestoreTransactions
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "RestoreTransactions") == 0) {
+ stack->CorrectParams(0);
+ RestoreTransactions(script);
+ stack->PushNULL();
+
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // UnlockProduct
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "UnlockProduct") == 0) {
+ stack->CorrectParams(1);
+ const char *prodId = stack->Pop()->GetString();
+
+ Game->_registry->WriteBool("Purchases", prodId, true);
+ Game->_registry->SaveValues();
+
+ stack->PushBool(true);
+
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // IsProductUnlocked
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "IsProductUnlocked") == 0) {
+ stack->CorrectParams(1);
+ const char *prodId = stack->Pop()->GetString();
+
+ stack->PushBool(Game->_registry->ReadBool("Purchases", prodId, false));
+
+ return S_OK;
+ }
+
+ else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CSXStore::ScGetProperty(const char *name) {
+ _scValue->SetNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(name, "Type") == 0) {
+ _scValue->SetString("store");
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Available (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "Available") == 0) {
+ _scValue->SetBool(IsAvailable());
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // EventsEnabled (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "EventsEnabled") == 0) {
+ _scValue->SetBool(GetEventsEnabled());
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // NumValidProducts (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "NumValidProducts") == 0) {
+ _scValue->SetInt(_validProducts.GetSize());
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // NumInvalidProducts (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "NumInvalidProducts") == 0) {
+ _scValue->SetInt(_invalidProducts.size());
+ return _scValue;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // NumTransactions (RO)
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(name, "NumTransactions") == 0) {
+ _scValue->SetInt(_transactions.GetSize());
+ return _scValue;
+ }
+
+ else return _scValue;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXStore::Persist(CBPersistMgr *PersistMgr) {
+ if (!PersistMgr->_saving) Cleanup();
+
+ CBObject::Persist(PersistMgr);
+
+ PersistMgr->Transfer(TMEMBER(_eventsEnabled));
+ PersistMgr->Transfer(TMEMBER(_lastProductRequestOwner));
+ PersistMgr->Transfer(TMEMBER(_lastPurchaseOwner));
+ PersistMgr->Transfer(TMEMBER(_lastRestoreOwner));
+ PersistMgr->Transfer(TMEMBER(_invalidProducts));
+
+ // persist valid products
+ int numProducts;
+ if (PersistMgr->_saving) {
+ numProducts = _validProducts.GetSize();
+ PersistMgr->Transfer(TMEMBER(numProducts));
+ for (int i = 0; i < numProducts; i++) _validProducts[i]->Persist(PersistMgr);
+ } else {
+ numProducts = _validProducts.GetSize();
+ PersistMgr->Transfer(TMEMBER(numProducts));
+ for (int i = 0; i < numProducts; i++) {
+ CBStoreProduct *prod = new CBStoreProduct;
+ prod->Persist(PersistMgr);
+ _validProducts.Add(prod);
+ }
+ }
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::AfterLoad() {
+ if (_eventsEnabled) {
+ SetEventsEnabled(NULL, true);
+ }
+#ifdef __IPHONEOS__
+ StoreKit_SetExternalData((void *)this);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::OnObjectDestroyed(CBScriptHolder *obj) {
+ if (_lastProductRequestOwner == obj) _lastProductRequestOwner = NULL;
+ if (_lastPurchaseOwner == obj) _lastPurchaseOwner = NULL;
+ if (_lastRestoreOwner == obj) _lastRestoreOwner = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::SetEventsEnabled(CScScript *script, bool val) {
+ _eventsEnabled = val;
+
+ if (val) {
+ if (script) _lastPurchaseOwner = script->_owner;
+#ifdef __IPHONEOS__
+ StoreKit_EnableEvents();
+#endif
+ } else {
+ _lastPurchaseOwner = NULL;
+#ifdef __IPHONEOS__
+ StoreKit_DisableEvents();
+#endif
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::ValidateProducts(const char *prodIdList) {
+#ifdef __IPHONEOS__
+ StoreKit_ValidateProducts(prodIdList);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXStore::IsAvailable() {
+#ifdef __IPHONEOS__
+ return StoreKit_IsStoreAvailable() > 0;
+#else
+ return false;
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::ReceiveProductsStart() {
+ for (int i = 0; i < _validProducts.GetSize(); i++) {
+ delete _validProducts[i];
+ }
+ _validProducts.RemoveAll();
+
+ _invalidProducts.clear();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::ReceiveProductsEnd() {
+ if (_lastProductRequestOwner) _lastProductRequestOwner->ApplyEvent("ProductsValidated");
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::AddValidProduct(const char *id, const char *name, const char *desc, const char *price) {
+ CBStoreProduct *prod = new CBStoreProduct(id, name, desc, price);
+ _validProducts.Add(prod);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::AddInvalidProduct(const char *id) {
+ _invalidProducts.push_back(id);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::ReceiveTransactionsStart() {
+ for (int i = 0; i < _transactions.GetSize(); i++) {
+ delete _transactions[i];
+ }
+ _transactions.RemoveAll();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::ReceiveTransactionsEnd() {
+ if (_lastPurchaseOwner) _lastPurchaseOwner->ApplyEvent("TransactionsUpdated");
+ else Game->ApplyEvent("TransactionsUpdated");
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::AddTransaction(const char *id, const char *productId, const char *state) {
+ CBStoreTransaction *trans = new CBStoreTransaction(id, productId, state);
+ _transactions.Add(trans);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXStore::Purchase(CScScript *script, const char *productId) {
+ if (!productId) return false;
+
+#ifdef __IPHONEOS__
+ for (int i = 0; i < _validProducts.GetSize(); i++) {
+ if (strcmp(productId, _validProducts[i]->GetId()) == 0) {
+ _lastPurchaseOwner = script->_owner;
+
+ StoreKit_Purchase(productId);
+ return true;
+ }
+ }
+#endif
+ script->RuntimeError("Store.Purchase() - '%s' is not a valid product id", productId);
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXStore::FinishTransaction(CScScript *script, const char *transId) {
+ if (!transId) return false;
+#ifdef __IPHONEOS__
+ for (int i = 0; i < _transactions.GetSize(); i++) {
+ if (strcmp(transId, _transactions[i]->GetId()) == 0) {
+ if (StoreKit_FinishTransaction(transId) > 0) {
+ SAFE_DELETE(_transactions[i]);
+ _transactions.RemoveAt(i);
+ return true;
+ } else return false;
+ }
+ }
+#endif
+ script->RuntimeError("Store.FinishTransaction() - '%s' is not a valid transaction id", transId);
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::RestoreTransactions(CScScript *script) {
+ _lastRestoreOwner = script->_owner;
+#ifdef __IPHONEOS__
+ StoreKit_RestoreTransactions();
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXStore::OnRestoreFinished(bool error) {
+ if (_lastRestoreOwner) {
+ if (error) _lastRestoreOwner->ApplyEvent("TransactionsRestoreFailed");
+ else _lastRestoreOwner->ApplyEvent("TransactionsRestoreFinished");
+ }
+}
+
+
+
+#ifdef __IPHONEOS__
+
+//////////////////////////////////////////////////////////////////////////
+// StoreKit callbacks (called from ObjC)
+//////////////////////////////////////////////////////////////////////////
+void StoreKit_AddValidProductCallback(const char *id, const char *name, const char *desc, const char *price, void *data) {
+ CSXStore *store = static_cast<CSXStore *>(data);
+ if (store) store->AddValidProduct(id, name, desc, price);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void StoreKit_AddInvalidProductCallback(const char *id, void *data) {
+ CSXStore *store = static_cast<CSXStore *>(data);
+ if (store) store->AddInvalidProduct(id);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void StoreKit_ReceiveProductsStartCallback(void *data) {
+ CSXStore *store = static_cast<CSXStore *>(data);
+ if (store) store->ReceiveProductsStart();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void StoreKit_ReceiveProductsEndCallback(void *data) {
+ CSXStore *store = static_cast<CSXStore *>(data);
+ if (store) store->ReceiveProductsEnd();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void StoreKit_AddTransactionCallback(const char *id, const char *productId, const char *state, void *data) {
+ CSXStore *store = static_cast<CSXStore *>(data);
+ if (store) store->AddTransaction(id, productId, state);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void StoreKit_ReceiveTransactionsStartCallback(void *data) {
+ CSXStore *store = static_cast<CSXStore *>(data);
+ if (store) store->ReceiveTransactionsStart();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void StoreKit_ReceiveTransactionsEndCallback(void *data) {
+ CSXStore *store = static_cast<CSXStore *>(data);
+ if (store) store->ReceiveTransactionsEnd();
+}
+//////////////////////////////////////////////////////////////////////////
+void StoreKit_RestoreFinishedCallback(void *data, int error) {
+ CSXStore *store = static_cast<CSXStore *>(data);
+ if (store) store->OnRestoreFinished(error > 0);
+}
+
+#endif // __IPHONEOS__
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/Base/scriptables/SXStore.h b/engines/wintermute/Base/scriptables/SXStore.h
new file mode 100644
index 0000000000..42fe6983e4
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/SXStore.h
@@ -0,0 +1,175 @@
+/* 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_SXSTORE_H
+#define WINTERMUTE_SXSTORE_H
+#include "engines/wintermute/Base/BPersistMgr.h"
+#include "engines/wintermute/utils.h"
+#include "engines/wintermute/Base/BObject.h"
+
+namespace WinterMute {
+
+class CSXStore : public CBObject {
+public:
+
+ //////////////////////////////////////////////////////////////////////////
+ class CBStoreProduct {
+ public:
+ CBStoreProduct() {
+ _id = _name = _desc = _price = NULL;
+ }
+
+ CBStoreProduct(const char *id, const char *name, const char *desc, const char *price) {
+ _id = _name = _desc = _price = NULL;
+
+ CBUtils::SetString(&_id, id);
+ CBUtils::SetString(&_name, name);
+ CBUtils::SetString(&_desc, desc);
+ CBUtils::SetString(&_price, price);
+ }
+
+ ~CBStoreProduct() {
+ delete [] _id;
+ delete [] _name;
+ delete [] _desc;
+ delete [] _price;
+ }
+
+ HRESULT Persist(CBPersistMgr *PersistMgr) {
+ PersistMgr->Transfer(TMEMBER(_id));
+ PersistMgr->Transfer(TMEMBER(_name));
+ PersistMgr->Transfer(TMEMBER(_desc));
+ PersistMgr->Transfer(TMEMBER(_price));
+ return S_OK;
+ }
+
+ const char *GetId() {
+ return _id;
+ }
+ const char *GetName() {
+ return _name;
+ }
+ const char *GetDesc() {
+ return _desc;
+ }
+ const char *GetPrice() {
+ return _price;
+ }
+
+ private:
+ char *_id;
+ char *_name;
+ char *_desc;
+ char *_price;
+ };
+
+ //////////////////////////////////////////////////////////////////////////
+ class CBStoreTransaction {
+ public:
+ CBStoreTransaction() {
+ _id = _productId = _state = NULL;
+ }
+
+ CBStoreTransaction(const char *id, const char *productId, const char *state) {
+ _id = _productId = _state = NULL;
+
+ CBUtils::SetString(&_id, id);
+ CBUtils::SetString(&_productId, productId);
+ CBUtils::SetString(&_state, state);
+ }
+
+ ~CBStoreTransaction() {
+ delete [] _id;
+ delete [] _productId;
+ delete [] _state;
+ }
+
+ const char *GetId() {
+ return _id;
+ }
+ const char *GetProductId() {
+ return _productId;
+ }
+ const char *GetState() {
+ return _state;
+ }
+
+ private:
+ char *_id;
+ char *_productId;
+ char *_state;
+ };
+
+
+ DECLARE_PERSISTENT(CSXStore, CBObject)
+ CSXStore(CBGame *inGame);
+ virtual ~CSXStore();
+ virtual CScValue *ScGetProperty(const char *Name);
+ virtual HRESULT ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, const char *Name);
+
+ void AfterLoad();
+ void OnObjectDestroyed(CBScriptHolder *obj);
+
+ bool IsAvailable();
+ void SetEventsEnabled(CScScript *script, bool val);
+ bool GetEventsEnabled() const {
+ return _eventsEnabled;
+ }
+ void ValidateProducts(const char *prodIdList);
+
+ void ReceiveTransactionsStart();
+ void ReceiveTransactionsEnd();
+ void AddTransaction(const char *id, const char *productId, const char *state);
+
+ void ReceiveProductsStart();
+ void ReceiveProductsEnd();
+ void AddValidProduct(const char *id, const char *name, const char *desc, const char *price);
+ void AddInvalidProduct(const char *id);
+
+ void OnRestoreFinished(bool error);
+
+private:
+ void Cleanup();
+ bool Purchase(CScScript *script, const char *productId);
+ bool FinishTransaction(CScScript *script, const char *transId);
+ void RestoreTransactions(CScScript *script);
+
+ bool _eventsEnabled;
+ CBArray<CBStoreProduct *, CBStoreProduct *> _validProducts;
+ AnsiStringArray _invalidProducts;
+ CBScriptHolder *_lastProductRequestOwner;
+ CBScriptHolder *_lastPurchaseOwner;
+ CBScriptHolder *_lastRestoreOwner;
+
+ CBArray<CBStoreTransaction *, CBStoreTransaction *> _transactions;
+
+};
+
+} // 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..408b892c05
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/SXString.cpp
@@ -0,0 +1,394 @@
+/* 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.h"
+#include "engines/wintermute/Base/scriptables/SXString.h"
+#include "engines/wintermute/Base/scriptables/SXArray.h"
+#include "engines/wintermute/StringUtil.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXString, false)
+
+//////////////////////////////////////////////////////////////////////////
+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);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 (Game->_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 (Game->_textEncoding == TEXT_UTF8)
+ Stack->PushString(StringUtil::WideToUtf8(subStr).c_str());
+ else
+ Stack->PushString(StringUtil::WideToAnsi(subStr).c_str());
+ // } catch (std::exception &) {
+ // Stack->PushNULL();
+ // }
+
+ return S_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 S_OK;
+ }
+
+ if (val->IsNULL()) len = strlen(_string) - start;
+
+// try {
+ WideString str;
+ if (Game->_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 (Game->_textEncoding == TEXT_UTF8)
+ Stack->PushString(StringUtil::WideToUtf8(subStr).c_str());
+ else
+ Stack->PushString(StringUtil::WideToAnsi(subStr).c_str());
+// } catch (std::exception &) {
+// Stack->PushNULL();
+// }
+
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ToUpperCase
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "ToUpperCase") == 0) {
+ Stack->CorrectParams(0);
+
+ WideString str;
+ if (Game->_textEncoding == TEXT_UTF8)
+ str = StringUtil::Utf8ToWide(_string);
+ else
+ str = StringUtil::AnsiToWide(_string);
+
+ StringUtil::ToUpperCase(str);
+
+ if (Game->_textEncoding == TEXT_UTF8)
+ Stack->PushString(StringUtil::WideToUtf8(str).c_str());
+ else
+ Stack->PushString(StringUtil::WideToAnsi(str).c_str());
+
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // ToLowerCase
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "ToLowerCase") == 0) {
+ Stack->CorrectParams(0);
+
+ WideString str;
+ if (Game->_textEncoding == TEXT_UTF8)
+ str = StringUtil::Utf8ToWide(_string);
+ else
+ str = StringUtil::AnsiToWide(_string);
+
+ StringUtil::ToLowerCase(str);
+
+ if (Game->_textEncoding == TEXT_UTF8)
+ Stack->PushString(StringUtil::WideToUtf8(str).c_str());
+ else
+ Stack->PushString(StringUtil::WideToAnsi(str).c_str());
+
+ return S_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 (Game->_textEncoding == TEXT_UTF8)
+ str = StringUtil::Utf8ToWide(_string);
+ else
+ str = StringUtil::AnsiToWide(_string);
+
+ WideString toFind;
+ if (Game->_textEncoding == TEXT_UTF8)
+ toFind = StringUtil::Utf8ToWide(strToFind);
+ else
+ toFind = StringUtil::AnsiToWide(strToFind);
+
+ int indexOf = StringUtil::IndexOf(str, toFind, index);
+ Stack->PushInt(indexOf);
+
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Split
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Split") == 0) {
+ Stack->CorrectParams(1);
+ CScValue *Val = Stack->Pop();
+ char Separators[MAX_PATH] = ",";
+ if (!Val->IsNULL()) strcpy(Separators, Val->GetString());
+
+ CSXArray *Array = new CSXArray(Game);
+ if (!Array) {
+ Stack->PushNULL();
+ return S_OK;
+ }
+
+
+ WideString str;
+ if (Game->_textEncoding == TEXT_UTF8)
+ str = StringUtil::Utf8ToWide(_string);
+ else
+ str = StringUtil::AnsiToWide(_string);
+
+ WideString delims;
+ if (Game->_textEncoding == TEXT_UTF8)
+ delims = StringUtil::Utf8ToWide(Separators);
+ else
+ delims = StringUtil::AnsiToWide(Separators);
+
+ Common::Array<WideString> parts;
+
+
+ size_t start, pos;
+ start = 0;
+ 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 (Game->_textEncoding == TEXT_UTF8)
+ Val = new CScValue(Game, StringUtil::WideToUtf8(part).c_str());
+ else
+ Val = new CScValue(Game, StringUtil::WideToAnsi(part).c_str());
+
+ Array->Push(Val);
+ delete[] Val;
+ Val = NULL;
+ }
+
+ Stack->PushNative(Array, false);
+ return S_OK;
+ }
+
+ else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+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 (Game->_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;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXString::ScSetProperty(const char *Name, CScValue *Value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Capacity
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "Capacity") == 0) {
+ int NewCap = Value->GetInt();
+ if (NewCap < strlen(_string) + 1) Game->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 S_OK;
+ }
+
+ else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_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..649a6c6f24
--- /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);
+ HRESULT ScSetProperty(const char *Name, CScValue *Value);
+ HRESULT 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..9929fd7ce2
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/ScEngine.cpp
@@ -0,0 +1,854 @@
+/* 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/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"
+#include <algorithm>
+#include <vector>
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CScEngine, true)
+
+#define COMPILER_DLL "dcscomp.dll"
+//////////////////////////////////////////////////////////////////////////
+CScEngine::CScEngine(CBGame *inGame): CBBase(inGame) {
+ Game->LOG(0, "Initializing scripting engine...");
+
+/*
+#ifdef __WIN32__
+ char CompilerPath[MAX_PATH];
+ strcpy(CompilerPath, COMPILER_DLL);
+
+ _compilerDLL = ::LoadLibrary(CompilerPath);
+ if (_compilerDLL == NULL) {
+ char ModuleName[MAX_PATH];
+ ::GetModuleFileName(NULL, ModuleName, MAX_PATH);
+
+ // switch to exe's dir
+ char *ExeDir = CBUtils::GetPath(ModuleName);
+ sprintf(CompilerPath, "%s%s", ExeDir, COMPILER_DLL);
+ _compilerDLL = ::LoadLibrary(CompilerPath);
+
+ delete [] ExeDir;
+ }
+ if (_compilerDLL != NULL) {
+ // bind compiler's functionality
+ ExtCompileBuffer = (DLL_COMPILE_BUFFER) ::GetProcAddress(_compilerDLL, "CompileBuffer");
+ ExtCompileFile = (DLL_COMPILE_FILE) ::GetProcAddress(_compilerDLL, "CompileFile");
+ ExtReleaseBuffer = (DLL_RELEASE_BUFFER) ::GetProcAddress(_compilerDLL, "ReleaseBuffer");
+ ExtSetCallbacks = (DLL_SET_CALLBACKS) ::GetProcAddress(_compilerDLL, "SetCallbacks");
+ ExtDefineFunction = (DLL_DEFINE_FUNCTION)::GetProcAddress(_compilerDLL, "DefineFunction");
+ ExtDefineVariable = (DLL_DEFINE_VARIABLE)::GetProcAddress(_compilerDLL, "DefineVariable");
+
+ if (!ExtCompileBuffer || !ExtCompileFile || !ExtReleaseBuffer || !ExtSetCallbacks || !ExtDefineFunction || !ExtDefineVariable) {
+ _compilerAvailable = false;
+ ::FreeLibrary(_compilerDLL);
+ _compilerDLL = NULL;
+ } else {
+ */ /*
+ // publish external methods to the compiler
+ CALLBACKS c;
+ c.Dll_AddError = AddError;
+ c.Dll_CloseFile = CloseFile;
+ c.Dll_LoadFile = LoadFile;
+ ExtSetCallbacks(&c, Game);
+ */
+/*
+ _compilerAvailable = true;
+ }
+ } else _compilerAvailable = false;
+#else
+*/
+ _compilerAvailable = false;
+ _compilerDLL = NULL;
+//#endif
+
+
+ if (_compilerAvailable) Game->LOG(0, " Script compiler bound successfuly");
+ else Game->LOG(0, " Script compiler is NOT available");
+
+ _globals = new CScValue(Game);
+
+
+ // register 'Game' as global variable
+ if (!_globals->PropExists("Game")) {
+ CScValue val(Game);
+ val.SetNative(Game, true);
+ _globals->SetProp("Game", &val);
+ }
+
+ // register 'Math' as global variable
+ if (!_globals->PropExists("Math")) {
+ CScValue val(Game);
+ val.SetNative(Game->_mathClass, true);
+ _globals->SetProp("Math", &val);
+ }
+
+ // prepare script cache
+ for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) _cachedScripts[i] = NULL;
+
+ _currentScript = NULL;
+
+ _fileToCompile = NULL;
+
+ _compileErrorCallback = NULL;
+ _compileErrorCallbackData = NULL;
+
+ _parseElementCallback = NULL;
+ _parseElementCallbackData = NULL;
+
+ _isProfiling = false;
+ _profilingStartTime = 0;
+
+ //EnableProfiling();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScEngine::~CScEngine() {
+ Game->LOG(0, "Shutting down scripting engine");
+ SaveBreakpoints();
+
+ DisableProfiling();
+#ifdef __WIN32__
+ if (_compilerAvailable && _compilerDLL) ::FreeLibrary(_compilerDLL);
+#endif
+ Cleanup();
+
+ for (int i = 0; i < _breakpoints.GetSize(); i++) {
+ delete _breakpoints[i];
+ _breakpoints[i] = NULL;
+ }
+ _breakpoints.RemoveAll();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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
+
+ delete[] _fileToCompile;
+ _fileToCompile = NULL;
+
+ _compileErrorCallback = NULL;
+ _compileErrorCallbackData = NULL;
+
+ _parseElementCallback = NULL;
+ _parseElementCallbackData = NULL;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+byte *WINAPI CScEngine::LoadFile(void *Data, char *Filename, uint32 *Size) {
+ CBGame *Game = (CBGame *)Data;
+ return Game->_fileManager->ReadWholeFile(Filename, Size);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void WINAPI CScEngine::CloseFile(void *Data, byte *Buffer) {
+ delete [] Buffer;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void WINAPI CScEngine::AddError(void *Data, int Line, char *Text) {
+ CBGame *Game = (CBGame *)Data;
+
+ if (Game) {
+ if (Game->_scEngine && Game->_scEngine->_fileToCompile)
+ Game->LOG(0, "Compiling script '%s'...", Game->_scEngine->_fileToCompile);
+ Game->LOG(0, " Error@line %d: %s", Line, Text);
+
+
+ // redirect to an engine's own callback
+ if (Game->_scEngine && Game->_scEngine->_compileErrorCallback) {
+ Game->_scEngine->_compileErrorCallback(Line, Text, Game->_scEngine->_compileErrorCallbackData);
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void WINAPI CScEngine::ParseElement(void *Data, int Line, int Type, void *ElementData) {
+ CBGame *Game = (CBGame *)Data;
+
+ if (Game) {
+ // redirect to an engine's own callback
+ if (Game->_scEngine && Game->_scEngine->_parseElementCallback) {
+ Game->_scEngine->_parseElementCallback(Line, Type, ElementData, Game->_scEngine->_compileErrorCallbackData);
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+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(Game, this);
+ HRESULT ret = script->Create(Filename, CompBuffer, CompSize, Owner);
+ if (FAILED(ret)) {
+ Game->LOG(ret, "Error running script '%s'...", Filename);
+ delete script;
+ return NULL;
+ } else {
+ // publish the "self" pseudo-variable
+ CScValue val(Game);
+ if (Owner)val.SetNative(Owner, true);
+ else val.SetNULL();
+
+ script->_globals->SetProp("self", &val);
+ script->_globals->SetProp("this", &val);
+
+ _scripts.Add(script);
+ Game->GetDebugMgr()->OnScriptInit(script);
+
+ return script;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+byte *CScEngine::GetCompiledScript(const char *Filename, uint32 *OutSize, bool IgnoreCache) {
+ int i;
+
+ // is script in cache?
+ if (!IgnoreCache) {
+ for (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;
+ bool CompiledNow = false;
+
+ uint32 Size;
+
+ byte *Buffer = Game->_fileManager->ReadWholeFile(Filename, &Size);
+ if (!Buffer) {
+ Game->LOG(0, "CScEngine::GetCompiledScript - error opening script '%s'", Filename);
+ return NULL;
+ }
+
+ // needs to be compiled?
+ if (*(uint32 *)Buffer == SCRIPT_MAGIC) {
+ CompBuffer = Buffer;
+ CompSize = Size;
+ } else {
+ if (!_compilerAvailable) {
+ Game->LOG(0, "CScEngine::GetCompiledScript - script '%s' needs to be compiled but compiler is not available", Filename);
+ delete [] Buffer;
+ return NULL;
+ }
+
+ CompiledNow = true;
+
+ // publish external methods to the compiler
+ CALLBACKS c;
+ c.Dll_AddError = AddError;
+ c.Dll_CloseFile = CloseFile;
+ c.Dll_LoadFile = LoadFile;
+ c.Dll_ParseElement = ParseElement;
+ ExtSetCallbacks(&c, Game);
+
+ // publish native interfaces
+ Game->PublishNatives();
+
+ // We have const char* everywhere but in the DLL-interfaces...
+ char *tempFileName = new char[strlen(Filename) + 1];
+ memcpy(tempFileName, Filename, strlen(Filename) + 1);
+
+ SetFileToCompile(Filename);
+ CompBuffer = ExtCompileFile(tempFileName, &CompSize);
+ delete[] tempFileName;
+ if (!CompBuffer) {
+ Game->QuickMessage("Script compiler error. View log for details.");
+ delete [] Buffer;
+ return NULL;
+ }
+ }
+
+ byte *ret = NULL;
+
+ // add script to cache
+ CScCachedScript *CachedScript = new CScCachedScript(Filename, CompBuffer, CompSize);
+ if (CachedScript) {
+ int index;
+ uint32 MinTime = CBPlatform::GetTime();
+ for (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;
+ if (CompiledNow) ExtReleaseBuffer(CompBuffer);
+
+ return ret;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::Tick() {
+ int i;
+
+ if (_scripts.GetSize() == 0) return S_OK;
+
+
+ // resolve waiting scripts
+ for (i = 0; i < _scripts.GetSize(); i++) {
+
+ switch (_scripts[i]->_state) {
+ case SCRIPT_WAITING: {
+ /*
+ bool obj_found=false;
+ for(int j=0; j<Game->_regObjects.GetSize(); j++)
+ {
+ if(Game->_regObjects[j] == _scripts[i]->_waitObject)
+ {
+ if(Game->_regObjects[j]->IsReady()) _scripts[i]->Run();
+ obj_found = true;
+ break;
+ }
+ }
+ if(!obj_found) _scripts[i]->Finish(); // _waitObject no longer exists
+ */
+ if (Game->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 <= Game->_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;
+ }
+ } // switch
+ } // for each script
+
+
+ // execute scripts
+ for (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;
+ 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 S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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]);
+ Game->GetDebugMgr()->OnScriptShutdown(_scripts[i]);
+ delete _scripts[i];
+ _scripts.RemoveAt(i);
+ i--;
+ }
+ }
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CScEngine::GetNumScripts(int *Running, int *Waiting, int *Persistent) {
+ int running = 0, waiting = 0, persistent = 0, total = 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:
+ running++;
+ break;
+ case SCRIPT_WAITING:
+ waiting++;
+ break;
+ case SCRIPT_PERSISTENT:
+ persistent++;
+ break;
+ }
+ total++;
+ }
+ if (Running) *Running = running;
+ if (Waiting) *Waiting = waiting;
+ if (Persistent) *Persistent = persistent;
+
+ return total;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::EmptyScriptCache() {
+ for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) {
+ if (_cachedScripts[i]) {
+ delete _cachedScripts[i];
+ _cachedScripts[i] = NULL;
+ }
+ }
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 (!Game->_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 S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::Persist(CBPersistMgr *PersistMgr) {
+ if (!PersistMgr->_saving) Cleanup();
+
+ PersistMgr->Transfer(TMEMBER(Game));
+ PersistMgr->Transfer(TMEMBER(_currentScript));
+ PersistMgr->Transfer(TMEMBER(_fileToCompile));
+ PersistMgr->Transfer(TMEMBER(_globals));
+ _scripts.Persist(PersistMgr);
+
+ return S_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--;
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::PauseAll() {
+ for (int i = 0; i < _scripts.GetSize(); i++) {
+ if (_scripts[i] != _currentScript) _scripts[i]->Pause();
+ }
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::ResumeAll() {
+ for (int i = 0; i < _scripts.GetSize(); i++)
+ _scripts[i]->Resume();
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::SetFileToCompile(const char *Filename) {
+ delete[] _fileToCompile;
+ _fileToCompile = new char[strlen(Filename) + 1];
+ if (_fileToCompile) {
+ strcpy(_fileToCompile, Filename);
+ return S_OK;
+ } else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::SetCompileErrorCallback(COMPILE_ERROR_CALLBACK Callback, void *Data) {
+ _compileErrorCallback = Callback;
+ _compileErrorCallbackData = Data;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::SetParseElementCallback(PARSE_ELEMENT_CALLBACK Callback, void *Data) {
+ _parseElementCallback = Callback;
+ _parseElementCallbackData = Data;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::IsValidScript(CScScript *Script) {
+ for (int i = 0; i < _scripts.GetSize(); i++) {
+ if (_scripts[i] == Script) return true;
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::ClearGlobals(bool IncludingNatives) {
+ _globals->CleanProps(IncludingNatives);
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::AddBreakpoint(const char *ScriptFilename, int Line) {
+ if (!Game->GetDebugMgr()->_enabled) return S_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);
+ }
+ bool Found = false;
+ for (int i = 0; i < Bp->_lines.GetSize(); i++) {
+ if (Bp->_lines[i] == Line) return S_OK;
+ }
+ Bp->_lines.Add(Line);
+
+ // refresh changes
+ RefreshScriptBreakpoints();
+
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::RemoveBreakpoint(const char *ScriptFilename, int Line) {
+ if (!Game->GetDebugMgr()->_enabled) return S_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 S_OK;
+ }
+ }
+ break;
+ }
+ }
+ return E_FAIL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::RefreshScriptBreakpoints() {
+ if (!Game->GetDebugMgr()->_enabled) return S_OK;
+
+ for (int i = 0; i < _scripts.GetSize(); i++) {
+ RefreshScriptBreakpoints(_scripts[i]);
+ }
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::RefreshScriptBreakpoints(CScScript *Script) {
+ if (!Game->GetDebugMgr()->_enabled) return S_OK;
+
+ if (!Script || !Script->_filename) return E_FAIL;
+
+ 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 S_OK;
+ }
+ }
+ if (Script->_breakpoints.GetSize() > 0) Script->_breakpoints.RemoveAll();
+
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::SaveBreakpoints() {
+ if (!Game->GetDebugMgr()->_enabled) return S_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]);
+
+ Game->_registry->WriteString("Debug", Key, Text);
+ }
+ }
+ Game->_registry->WriteInt("Debug", "NumBreakpoints", Count);
+
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::LoadBreakpoints() {
+ if (!Game->GetDebugMgr()->_enabled) return S_OK;
+
+ char Key[100];
+
+ int Count = Game->_registry->ReadInt("Debug", "NumBreakpoints", 0);
+ for (int i = 1; i <= Count; i++) {
+ /* uint32 BufSize = 512; */
+ sprintf(Key, "Breakpoint%d", i);
+ AnsiString breakpoint = Game->_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 S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::AddScriptTime(const char *Filename, uint32 Time) {
+ if (!_isProfiling) return;
+
+ AnsiString fileName = Filename;
+ StringUtil::ToLowerCase(fileName);
+ _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;
+
+ Game->LOG(0, "***** Script profiling information: *****");
+ Game->LOG(0, " %-40s %fs", "Total execution time", (float)totalTime / 1000);
+
+ for (tit = times.rbegin(); tit != times.rend(); tit++) {
+ Game->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..5e2e5861e7
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/ScEngine.h
@@ -0,0 +1,174 @@
+/* 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.h"
+#include "engines/wintermute/PlatformSDL.h"
+
+namespace WinterMute {
+
+typedef byte *(*DLL_COMPILE_BUFFER)(byte *Buffer, char *Source, uint32 BufferSize, uint32 *CompiledSize);
+typedef byte *(*DLL_COMPILE_FILE)(char *Filename, uint32 *CompiledSize);
+typedef void (*DLL_RELEASE_BUFFER)(unsigned char *Buffer);
+typedef void (*DLL_SET_CALLBACKS)(CALLBACKS *callbacks, void *Data);
+typedef int (*DLL_DEFINE_FUNCTION)(const char *Name); /* Was non-const, changed to silence warnings */
+typedef int (*DLL_DEFINE_VARIABLE)(const char *Name); /* Was non-const, changed to silence warnings */
+
+typedef void (*COMPILE_ERROR_CALLBACK)(int Line, char *Text , void *Data);
+typedef void (*PARSE_ELEMENT_CALLBACK)(int Line, int Type, void *ElementData, void *Data);
+
+#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:
+ HRESULT DbgSendScripts(IWmeDebugClient *Client);
+
+ CBArray<CScBreakpoint *, CScBreakpoint *> _breakpoints;
+ HRESULT AddBreakpoint(const char *ScriptFilename, int Line);
+ HRESULT RemoveBreakpoint(const char *ScriptFilename, int Line);
+ HRESULT RefreshScriptBreakpoints();
+ HRESULT RefreshScriptBreakpoints(CScScript *Script);
+ HRESULT SaveBreakpoints();
+ HRESULT LoadBreakpoints();
+
+ HRESULT ClearGlobals(bool IncludingNatives = false);
+ HRESULT TickUnbreakable();
+ HRESULT RemoveFinishedScripts();
+ bool IsValidScript(CScScript *Script);
+ void SetCompileErrorCallback(COMPILE_ERROR_CALLBACK Callback, void *Data);
+ void SetParseElementCallback(PARSE_ELEMENT_CALLBACK Callback, void *Data);
+
+ COMPILE_ERROR_CALLBACK _compileErrorCallback;
+ void *_compileErrorCallbackData;
+
+ PARSE_ELEMENT_CALLBACK _parseElementCallback;
+ void *_parseElementCallbackData;
+
+ HRESULT SetFileToCompile(const char *Filename);
+ char *_fileToCompile;
+ CScScript *_currentScript;
+ HRESULT ResumeAll();
+ HRESULT PauseAll();
+ void EditorCleanup();
+ HRESULT ResetObject(CBObject *Object);
+ HRESULT ResetScript(CScScript *Script);
+ HRESULT EmptyScriptCache();
+ byte *GetCompiledScript(const char *Filename, uint32 *OutSize, bool IgnoreCache = false);
+ DECLARE_PERSISTENT(CScEngine, CBBase)
+ HRESULT Cleanup();
+ int GetNumScripts(int *Running = NULL, int *Waiting = NULL, int *Persistent = NULL);
+ HRESULT Tick();
+ CScValue *_globals;
+ CScScript *RunScript(const char *Filename, CBScriptHolder *Owner = NULL);
+ bool _compilerAvailable;
+ HINSTANCE _compilerDLL;
+ CScEngine(CBGame *inGame);
+ virtual ~CScEngine();
+ static void WINAPI AddError(void *Data, int Line, char *Text);
+ static byte *WINAPI LoadFile(void *Data, char *Filename, uint32 *Size);
+ static void WINAPI CloseFile(void *Data, byte *Buffer);
+ static void WINAPI ParseElement(void *Data, int Line, int Type, void *ElementData);
+ DLL_COMPILE_BUFFER ExtCompileBuffer;
+ DLL_COMPILE_FILE ExtCompileFile;
+ DLL_RELEASE_BUFFER ExtReleaseBuffer;
+ DLL_SET_CALLBACKS ExtSetCallbacks;
+ DLL_DEFINE_FUNCTION ExtDefineFunction;
+ DLL_DEFINE_VARIABLE ExtDefineVariable;
+
+ CBArray<CScScript *, CScScript *> _scripts;
+
+ void EnableProfiling();
+ void DisableProfiling();
+ bool IsProfiling() {
+ 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..7a2f0db62a
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/ScScript.cpp
@@ -0,0 +1,1600 @@
+/* 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"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CScScript, false)
+
+//////////////////////////////////////////////////////////////////////////
+CScScript::CScScript(CBGame *inGame, CScEngine *Engine): CBBase(inGame) {
+ _buffer = NULL;
+ _bufferSize = _iP = 0;
+ _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();
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::InitScript() {
+ TScriptHeader *Header = (TScriptHeader *)_buffer;
+ if (Header->magic != SCRIPT_MAGIC) {
+ Game->LOG(0, "File '%s' is not a valid compiled script", _filename);
+ Cleanup();
+ return E_FAIL;
+ }
+
+ if (Header->version > SCRIPT_VERSION) {
+ Game->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 E_FAIL;
+ }
+
+ InitTables();
+
+ // init stacks
+ _scopeStack = new CScStack(Game);
+ _callStack = new CScStack(Game);
+ _thisStack = new CScStack(Game);
+ _stack = new CScStack(Game);
+
+ _operand = new CScValue(Game);
+ _reg1 = new CScValue(Game);
+
+
+ // skip to the beginning
+ _iP = Header->code_start;
+ _currentLine = 0;
+
+ // init breakpoints
+ _engine->RefreshScriptBreakpoints(this);
+
+
+ // ready to rumble...
+ _state = SCRIPT_RUNNING;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::InitTables() {
+ uint32 OrigIP = _iP;
+
+ TScriptHeader *Header = (TScriptHeader *)_buffer;
+
+ int32 i;
+
+ // load symbol table
+ _iP = Header->symbol_table;
+
+ _numSymbols = GetDWORD();
+ _symbols = new char*[_numSymbols];
+ for (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 (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 (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 (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 (i = 0; i < _numMethods; i++) {
+ _methods[i].pos = GetDWORD();
+ _methods[i].name = GetString();
+ }
+
+
+ _iP = OrigIP;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 E_FAIL;
+
+ memcpy(_buffer, Buffer, Size);
+
+ _bufferSize = Size;
+
+ HRESULT res = InitScript();
+ if (FAILED(res)) return res;
+
+ // establish global variables table
+ _globals = new CScValue(Game);
+
+ _owner = Owner;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 E_FAIL;
+
+ memcpy(_buffer, Original->_buffer, Original->_bufferSize);
+ _bufferSize = Original->_bufferSize;
+
+ // initialize
+ HRESULT res = InitScript();
+ if (FAILED(res)) return res;
+
+ // copy globals
+ _globals = Original->_globals;
+
+ // skip to the beginning of the event
+ _iP = InitIP;
+
+ _timeSlice = Original->_timeSlice;
+ _freezable = Original->_freezable;
+ _owner = Original->_owner;
+
+ _engine = Original->_engine;
+ _parentScript = Original;
+
+ return S_OK;
+}
+
+
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::CreateMethodThread(CScScript *Original, const char *MethodName) {
+ uint32 IP = Original->GetMethodPos(MethodName);
+ if (IP == 0) return E_FAIL;
+
+ 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 E_FAIL;
+
+ memcpy(_buffer, Original->_buffer, Original->_bufferSize);
+ _bufferSize = Original->_bufferSize;
+
+ // initialize
+ HRESULT res = InitScript();
+ if (FAILED(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 S_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;
+
+ if (_scopeStack) delete _scopeStack;
+ _scopeStack = NULL;
+
+ if (_callStack) delete _callStack;
+ _callStack = NULL;
+
+ if (_thisStack) delete _thisStack;
+ _thisStack = NULL;
+
+ if (_stack) 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 (int 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
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CScScript::GetDWORD() {
+ uint32 ret = *(uint32 *)(_buffer + _iP);
+ _iP += sizeof(uint32);
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double CScScript::GetFloat() {
+ double ret = *(double *)(_buffer + _iP);
+ _iP += sizeof(double);
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *CScScript::GetString() {
+ char *ret = (char *)(_buffer + _iP);
+ while (*(char *)(_buffer + _iP) != '\0') _iP++;
+ _iP++; // string terminator
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::ExecuteInstruction() {
+ HRESULT ret = S_OK;
+
+ uint32 dw;
+ const char *str = NULL;
+
+ //CScValue* op = new CScValue(Game);
+ _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 (Game->GetDebugMgr()->_enabled)
+ Game->GetDebugMgr()->OnVariableInit(WME_DBGVAR_SCRIPT, this, NULL, _globals->GetProp(_symbols[dw]), _symbols[dw]);
+ } else {
+ _scopeStack->GetTop()->SetProp(_symbols[dw], _operand);
+ if (Game->GetDebugMgr()->_enabled)
+ Game->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 (Game->GetDebugMgr()->_enabled)
+ Game->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) {
+ Game->GetDebugMgr()->OnScriptShutdownScope(this, _scopeStack->GetTop());
+
+ _scopeStack->Pop();
+ _iP = (uint32)_callStack->Pop()->GetInt();
+
+ if (_scopeStack->_sP < 0) Game->GetDebugMgr()->OnScriptChangeScope(this, NULL);
+ else Game->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;
+
+ HRESULT res = E_FAIL;
+ 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 (FAILED(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
+ Game->ExternalCall(this, _stack, _thisStack, val->GetString());
+ }
+ }
+ else{
+ _operand->SetInt(_iP);
+ _callStack->Push(_operand);
+ _iP = dw;
+ }
+ }
+ */
+ else {
+ res = E_FAIL;
+ if (var->_type == VAL_NATIVE && !TriedNative) res = var->_valNative->ScCallMethod(this, _stack, _thisStack, MethodName);
+
+ if (FAILED(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 Game->ExternalCall(this, _stack, _thisStack, _symbols[SymbolIndex]);
+
+ break;
+ }
+ case II_SCOPE:
+ _operand->SetNULL();
+ _scopeStack->Push(_operand);
+
+ if (_scopeStack->_sP < 0) Game->GetDebugMgr()->OnScriptChangeScope(this, NULL);
+ else Game->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 (Game->GetDebugMgr()->_enabled)
+ Game->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 (Game->GetDebugMgr()->_enabled)
+ Game->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(str);
+ 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 (Game->GetDebugMgr()->_enabled) {
+ Game->GetDebugMgr()->OnScriptChangeLine(this, _currentLine);
+ for (int i = 0; i < _breakpoints.GetSize(); i++) {
+ if (_breakpoints[i] == _currentLine) {
+ Game->GetDebugMgr()->OnScriptHitBreakpoint(this);
+ Sleep(0);
+ break;
+ }
+ }
+ if (_tracingMode) {
+ Game->GetDebugMgr()->OnScriptHitBreakpoint(this);
+ Sleep(0);
+ break;
+ }
+ }
+ }
+ break;
+
+ }
+ default:
+ Game->LOG(0, "Fatal: Invalid instruction %d ('%s', line %d, IP:0x%x)\n", inst, _filename, _currentLine, _iP - sizeof(uint32));
+ _state = SCRIPT_FINISHED;
+ ret = E_FAIL;
+ } // switch(instruction)
+
+ //delete op;
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CScScript::GetFuncPos(const char *Name) {
+ for (int 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 (int 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);
+ Game->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(Game);
+ 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;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::WaitFor(CBObject *Object) {
+ if (_unbreakable) {
+ RuntimeError("Script cannot be interrupted.");
+ return S_OK;
+ }
+
+ _state = SCRIPT_WAITING;
+ _waitObject = Object;
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::WaitForExclusive(CBObject *Object) {
+ _engine->ResetObject(Object);
+ return WaitFor(Object);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Sleep(uint32 Duration) {
+ if (_unbreakable) {
+ RuntimeError("Script cannot be interrupted.");
+ return S_OK;
+ }
+
+ _state = SCRIPT_SLEEPING;
+ if (Game->_state == GAME_FROZEN) {
+ _waitTime = CBPlatform::GetTime() + Duration;
+ _waitFrozen = true;
+ } else {
+ _waitTime = Game->_timer + Duration;
+ _waitFrozen = false;
+ }
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Finish(bool IncludingThreads) {
+ if (_state != SCRIPT_FINISHED && IncludingThreads) {
+ _state = SCRIPT_FINISHED;
+ FinishThreads();
+ } else _state = SCRIPT_FINISHED;
+
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Run() {
+ _state = SCRIPT_RUNNING;
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void CScScript::RuntimeError(LPCSTR fmt, ...) {
+ char buff[256];
+ va_list va;
+
+ va_start(va, fmt);
+ vsprintf(buff, fmt, va);
+ va_end(va);
+
+ Game->LOG(0, "Runtime error. Script '%s', line %d", _filename, _currentLine);
+ Game->LOG(0, " %s", buff);
+
+ if (!Game->_suppressScriptErrors)
+ Game->QuickMessage("Script runtime error. View log for details.");
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Persist(CBPersistMgr *PersistMgr) {
+
+ PersistMgr->Transfer(TMEMBER(Game));
+
+ // 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);
+ InitTables();
+ } else _buffer = 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 S_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(Game, _engine);
+ if (thread) {
+ HRESULT ret = thread->CreateThread(this, pos, EventName);
+ if (SUCCEEDED(ret)) {
+ thread->_unbreakable = Unbreakable;
+ _engine->_scripts.Add(thread);
+ Game->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;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Pause() {
+ if (_state == SCRIPT_PAUSED) {
+ Game->LOG(0, "Attempting to pause a paused script ('%s', line %d)", _filename, _currentLine);
+ return E_FAIL;
+ }
+
+ if (!_freezable || _state == SCRIPT_PERSISTENT) return S_OK;
+
+ _origState = _state;
+ _state = SCRIPT_PAUSED;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Resume() {
+ if (_state != SCRIPT_PAUSED) return S_OK;
+
+ _state = _origState;
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScScript::TExternalFunction *CScScript::GetExternal(char *Name) {
+ for (int i = 0; i < _numExternals; i++) {
+ if (strcmp(Name, _externals[i].name) == 0) return &_externals[i];
+ }
+ return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::ExternalCall(CScStack *Stack, CScStack *ThisStack, CScScript::TExternalFunction *Function) {
+
+#ifndef __WIN32__
+
+ Game->LOG(0, "External functions are not supported on this platform.");
+ Stack->CorrectParams(0);
+ Stack->PushNULL();
+ return E_FAIL;
+
+#else
+
+ bool Success = false;
+ HMODULE hDll = LoadLibrary(Function->dll_name);
+
+ if (hDll) {
+ FARPROC pFunc = GetProcAddress(hDll, Function->name);
+ if (pFunc) {
+ int i;
+ Success = true;
+ Stack->CorrectParams(Function->nu_params);
+ CBDynBuffer *Buffer = new CBDynBuffer(Game, 20 * sizeof(uint32));
+
+ for (i = 0; i < Function->nu_params; i++) {
+ CScValue *Val = Stack->Pop();
+ switch (Function->params[i]) {
+ case TYPE_BOOL:
+ Buffer->PutDWORD((uint32)Val->GetBool());
+ break;
+ case TYPE_LONG:
+ Buffer->PutDWORD(Val->GetInt());
+ break;
+ case TYPE_BYTE:
+ Buffer->PutDWORD((byte)Val->GetInt());
+ break;
+ case TYPE_STRING:
+ if (Val->IsNULL()) Buffer->PutDWORD(0);
+ else Buffer->PutDWORD((uint32)Val->GetString());
+ break;
+ case TYPE_MEMBUFFER:
+ if (Val->IsNULL()) Buffer->PutDWORD(0);
+ else Buffer->PutDWORD((uint32)Val->GetMemBuffer());
+ break;
+ case TYPE_FLOAT: {
+ float f = Val->GetFloat();
+ Buffer->PutDWORD(*((uint32 *)&f));
+ break;
+ }
+ case TYPE_DOUBLE: {
+ double d = Val->GetFloat();
+ uint32 *pd = (uint32 *)&d;
+
+ Buffer->PutDWORD(pd[0]);
+ Buffer->PutDWORD(pd[1]);
+ break;
+ }
+ }
+ }
+
+ // call
+ uint32 ret;
+ bool StackCorrupted = false;
+ switch (Function->call_type) {
+ case CALL_CDECL:
+ ret = Call_cdecl(Buffer->_buffer, Buffer->GetSize(), (uint32)pFunc, &StackCorrupted);
+ break;
+ default:
+ ret = Call_stdcall(Buffer->_buffer, Buffer->GetSize(), (uint32)pFunc, &StackCorrupted);
+ }
+ delete Buffer;
+
+ // return
+ switch (Function->returns) {
+ case TYPE_BOOL:
+ Stack->PushBool((byte)ret != 0);
+ break;
+ case TYPE_LONG:
+ Stack->PushInt(ret);
+ break;
+ case TYPE_BYTE:
+ Stack->PushInt((byte)ret);
+ break;
+ break;
+ case TYPE_STRING:
+ Stack->PushString((char *)ret);
+ break;
+ case TYPE_MEMBUFFER: {
+ CSXMemBuffer *Buf = new CSXMemBuffer(Game, (void *)ret);
+ Stack->PushNative(Buf, false);
+ }
+ break;
+ case TYPE_FLOAT: {
+ uint32 dw = GetST0();
+ Stack->PushFloat(*((float *)&dw));
+ break;
+ }
+ case TYPE_DOUBLE:
+ Stack->PushFloat(GetST0Double());
+ break;
+
+ default:
+ Stack->PushNULL();
+ }
+
+ if (StackCorrupted) RuntimeError("Warning: Stack corrupted after calling '%s' in '%s'\n Check parameters and/or calling convention.", Function->name, Function->dll_name);
+ } else RuntimeError("Exported function '%s' not found in '%s'", Function->name, Function->dll_name);
+ } else RuntimeError("Error loading DLL '%s'", Function->dll_name);
+
+ if (!Success) {
+ Stack->CorrectParams(0);
+ Stack->PushNULL();
+ }
+
+ if (hDll) FreeLibrary(hDll);
+
+ return Success ? S_OK : E_FAIL;
+#endif
+}
+
+#ifdef __WIN32__
+//////////////////////////////////////////////////////////////////////////
+uint32 CScScript::Call_cdecl(const void *args, size_t sz, uint32 func, bool *StackCorrupt) {
+ uint32 rc; // here's our return value...
+ uint32 OrigESP;
+ bool StkCorrupt = false;
+ __asm {
+ mov OrigESP, esp
+ mov ecx, sz // get size of buffer
+ mov esi, args // get buffer
+ sub esp, ecx // allocate stack space
+ mov edi, esp // start of destination stack frame
+ shr ecx, 2 // make it dwords
+ rep movsd // copy params to real stack
+ call [func] // call the function
+ mov rc, eax // save the return value
+ add esp, sz // restore the stack pointer
+ cmp esp, OrigESP
+ jz finish
+ mov esp, OrigESP
+ mov StkCorrupt, 1
+ finish:
+ }
+
+ if (StackCorrupt) *StackCorrupt = StkCorrupt;
+ return rc;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CScScript::Call_stdcall(const void *args, size_t sz, uint32 func, bool *StackCorrupt) {
+ uint32 rc; // here's our return value...
+ uint32 OrigESP;
+ bool StkCorrupt = false;
+
+ __asm {
+ mov OrigESP, esp
+ mov ecx, sz // get size of buffer
+ mov esi, args // get buffer
+ sub esp, ecx // allocate stack space
+ mov edi, esp // start of destination stack frame
+ shr ecx, 2 // make it dwords
+ rep movsd // copy it
+ call [func] // call the function
+ mov rc, eax // save the return value
+ cmp esp, OrigESP
+ jz finish
+ mov esp, OrigESP
+ mov StkCorrupt, 1
+ finish:
+ }
+
+ if (StackCorrupt) *StackCorrupt = StkCorrupt;
+ return rc;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+__declspec(naked) uint32 CScScript::GetST0(void) {
+ uint32 f; // temp var
+ __asm {
+ fstp uint32 ptr [f] // pop ST0 into f
+ mov eax, uint32 ptr [f] // copy into eax
+ ret // done
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double CScScript::GetST0Double(void) {
+ double d; // temp var
+ __asm {
+ fstp qword ptr [d] // get ST0 into d
+ }
+ return d;
+}
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// IWmeDebugScript interface implementation
+int CScScript::DbgGetLine() {
+ return _currentLine;
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *CScScript::DbgGetFilename() {
+ return _filename;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_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) {
+ Game->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);
+
+ 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..226e6d8031
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/ScScript.h
@@ -0,0 +1,185 @@
+/* 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:
+ HRESULT DbgSendScript(IWmeDebugClient *Client);
+ HRESULT DbgSendVariables(IWmeDebugClient *Client);
+
+ CBArray<int, int> _breakpoints;
+ bool _tracingMode;
+
+ CScScript *_parentScript;
+ bool _unbreakable;
+ HRESULT FinishThreads();
+ HRESULT CopyParameters(CScStack *Stack);
+
+ void AfterLoad();
+
+#ifdef __WIN32__
+ static uint32 Call_cdecl(const void *args, size_t sz, uint32 func, bool *StackCorrupt);
+ static uint32 Call_stdcall(const void *args, size_t sz, uint32 func, bool *StackCorrupt);
+ static uint32 GetST0(void);
+ static double GetST0Double(void);
+#endif
+
+ CScValue *_operand;
+ CScValue *_reg1;
+ bool _freezable;
+ HRESULT Resume();
+ HRESULT Pause();
+ bool CanHandleEvent(const char *EventName);
+ bool CanHandleMethod(const char *MethodName);
+ HRESULT CreateThread(CScScript *Original, uint32 InitIP, const char *EventName);
+ HRESULT CreateMethodThread(CScScript *Original, const char *MethodName);
+ CScScript *InvokeEventHandler(const char *EventName, bool Unbreakable = false);
+ uint32 _timeSlice;
+ DECLARE_PERSISTENT(CScScript, CBBase)
+ void RuntimeError(LPCSTR fmt, ...);
+ HRESULT Run();
+ HRESULT Finish(bool IncludingThreads = false);
+ HRESULT Sleep(uint32 Duration);
+ HRESULT WaitForExclusive(CBObject *Object);
+ HRESULT 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;
+
+
+ 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;
+ HRESULT ExecuteInstruction();
+ char *GetString();
+ uint32 GetDWORD();
+ double GetFloat();
+ void Cleanup();
+ HRESULT Create(const char *Filename, byte *Buffer, uint32 Size, CBScriptHolder *Owner);
+ uint32 _iP;
+ uint32 _bufferSize;
+ byte *_buffer;
+ CScScript(CBGame *inGame, CScEngine *Engine);
+ virtual ~CScScript();
+ char *_filename;
+ char **_symbols;
+ int _numSymbols;
+ TFunctionPos *_functions;
+ TMethodPos *_methods;
+ TEventPos *_events;
+ int _numExternals;
+ TExternalFunction *_externals;
+ int _numFunctions;
+ int _numMethods;
+ int _numEvents;
+ bool _thread;
+ bool _methodThread;
+ char *_threadEvent;
+ CBScriptHolder *_owner;
+ CScScript::TExternalFunction *GetExternal(char *Name);
+ HRESULT ExternalCall(CScStack *Stack, CScStack *ThisStack, CScScript::TExternalFunction *Function);
+private:
+ HRESULT InitScript();
+ HRESULT 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..26e8aa118b
--- /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
+ //Game->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) {
+ Game->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 *val = new CScValue(Game);
+ val->Copy(Val);
+ _values.Add(val);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScStack::GetPushValue() {
+ _sP++;
+
+ if (_sP >= _values.GetSize()) {
+ CScValue *val = new CScValue(Game);
+ _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 expected_params) {
+ int nu_params = Pop()->GetInt();
+
+ if (expected_params < nu_params) { // too many params
+ while (expected_params < nu_params) {
+ //Pop();
+ delete _values[_sP - expected_params];
+ _values.RemoveAt(_sP - expected_params);
+ nu_params--;
+ _sP--;
+ }
+ } else if (expected_params > nu_params) { // need more params
+ while (expected_params > nu_params) {
+ //Push(null_val);
+ CScValue *null_val = new CScValue(Game);
+ null_val->SetNULL();
+ _values.InsertAt(_sP - nu_params + 1, null_val);
+ nu_params++;
+ _sP++;
+
+ if (_values.GetSize() > _sP + 1) {
+ delete _values[_values.GetSize() - 1];
+ _values.RemoveAt(_values.GetSize() - 1);
+ }
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::PushNULL() {
+ /*
+ CScValue* val = new CScValue(Game);
+ val->SetNULL();
+ Push(val);
+ delete val;
+ */
+ GetPushValue()->SetNULL();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::PushInt(int Val) {
+ /*
+ CScValue* val = new CScValue(Game);
+ val->SetInt(Val);
+ Push(val);
+ delete val;
+ */
+ GetPushValue()->SetInt(Val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::PushFloat(double Val) {
+ /*
+ CScValue* val = new CScValue(Game);
+ val->SetFloat(Val);
+ Push(val);
+ delete val;
+ */
+ GetPushValue()->SetFloat(Val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::PushBool(bool Val) {
+ /*
+ CScValue* val = new CScValue(Game);
+ val->SetBool(Val);
+ Push(val);
+ delete val;
+ */
+ GetPushValue()->SetBool(Val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::PushString(const char *Val) {
+ /*
+ CScValue* val = new CScValue(Game);
+ val->SetString(Val);
+ Push(val);
+ delete val;
+ */
+ GetPushValue()->SetString(Val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::PushNative(CBScriptable *Val, bool Persistent) {
+ /*
+ CScValue* val = new CScValue(Game);
+ val->SetNative(Val, Persistent);
+ Push(val);
+ delete val;
+ */
+
+ GetPushValue()->SetNative(Val, Persistent);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScStack::Persist(CBPersistMgr *PersistMgr) {
+
+ PersistMgr->Transfer(TMEMBER(Game));
+
+ PersistMgr->Transfer(TMEMBER(_sP));
+ _values.Persist(PersistMgr);
+
+ return S_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..3e7f557cdd
--- /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 expected_params);
+ 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..ed3778b635
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/ScValue.cpp
@@ -0,0 +1,1028 @@
+/* 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/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) {
+ Game->_scValue->_type = VAL_INT;
+
+#if 0 // TODO: Remove FreeType-dependency
+ if (Game->_textEncoding == TEXT_ANSI) {
+#else
+ if (true) {
+#endif
+ Game->_scValue->SetInt(strlen(_valString));
+ } else {
+ WideString wstr = StringUtil::Utf8ToWide(_valString);
+ Game->_scValue->SetInt(wstr.size());
+ }
+
+ return Game->_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;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_OK;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScValue::SetProp(const char *Name, CScValue *Val, bool CopyWhole, bool SetAsConst) {
+ if (_type == VAL_VARIABLE_REF) return _valRef->SetProp(Name, Val);
+
+ HRESULT ret = E_FAIL;
+ if (_type == VAL_NATIVE && _valNative) {
+ ret = _valNative->ScSetProperty(Name, Val);
+ }
+
+ if (FAILED(ret)) {
+ CScValue *val = NULL;
+
+ _valIter = _valObject.find(Name);
+ if (_valIter != _valObject.end()) {
+ val = _valIter->_value;
+ }
+ if (!val) val = new CScValue(Game);
+ else val->Cleanup();
+
+ val->Copy(Val, CopyWhole);
+ val->_isConstVar = SetAsConst;
+ _valObject[Name] = val;
+
+ 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(Game);
+ val->Copy(Val, CopyWhole);
+ val->_isConstVar = SetAsConst;
+ _valObject[Name] = val;
+
+ if(_type!=VAL_NATIVE) _type = VAL_OBJECT;
+ */
+ }
+
+ return S_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::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 Default) {
+ 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 Default;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CScValue::GetInt(int Default) {
+ 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 Default;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double CScValue::GetFloat(double Default) {
+ 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 Default;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+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) {
+ Game = orig->Game;
+
+ 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(Game);
+ _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;
+ }
+ }
+ // otherwise just copy everything
+ else Copy(Val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScValue::Persist(CBPersistMgr *PersistMgr) {
+ PersistMgr->Transfer(TMEMBER(Game));
+
+ 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 S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_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);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT 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 S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::SetProperty(const char *PropName, int Value) {
+ CScValue *Val = new CScValue(Game, Value);
+ bool Ret = SUCCEEDED(SetProp(PropName, Val));
+ delete Val;
+ return Ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::SetProperty(const char *PropName, const char *Value) {
+ CScValue *Val = new CScValue(Game, Value);
+ bool Ret = SUCCEEDED(SetProp(PropName, Val));
+ delete Val;
+ return Ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::SetProperty(const char *PropName, double Value) {
+ CScValue *Val = new CScValue(Game, Value);
+ bool Ret = SUCCEEDED(SetProp(PropName, Val));
+ delete Val;
+ return Ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::SetProperty(const char *PropName, bool Value) {
+ CScValue *Val = new CScValue(Game, Value);
+ bool Ret = SUCCEEDED(SetProp(PropName, Val));
+ delete Val;
+ return Ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::SetProperty(const char *PropName) {
+ CScValue *Val = new CScValue(Game);
+ bool Ret = SUCCEEDED(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..fedf3572f3
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/ScValue.h
@@ -0,0 +1,142 @@
+/* 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 <map>
+//#include <string>
+#include "engines/wintermute/wme_debugger.h"
+#include "common/str.h"
+
+namespace WinterMute {
+
+class CScScript;
+class CBScriptable;
+
+class CScValue : public CBBase, public IWmeDebugProp {
+public:
+ HRESULT 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;
+ HRESULT 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 Default = false);
+ int GetInt(int Default = 0);
+ double GetFloat(double Default = 0.0f);
+ const char *GetString();
+ void *GetMemBuffer();
+ CBScriptable *GetNative();
+ HRESULT 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 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();
+ HRESULT 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 **Name, 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..1af01c1045
--- /dev/null
+++ b/engines/wintermute/Base/scriptables/SxObject.cpp
@@ -0,0 +1,63 @@
+/* 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)
+
+//////////////////////////////////////////////////////////////////////////
+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() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXObject::Persist(CBPersistMgr *PersistMgr) {
+ CBObject::Persist(PersistMgr);
+
+ return S_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..2a6ad36a58
--- /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