aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorEinar Johan Trøan Sømåen2012-03-06 02:44:24 +0100
committerEinar Johan Trøan Sømåen2012-06-02 12:12:20 +0200
commit72a44cdc7b2040037a97f00c0c218b7f6bd9cd31 (patch)
tree15eecc583bf7f144188865fb2b113e2a9db5b9db /engines
parent997c5df43000b7db5b73aa883343d00df92c6cd0 (diff)
downloadscummvm-rg350-72a44cdc7b2040037a97f00c0c218b7f6bd9cd31.tar.gz
scummvm-rg350-72a44cdc7b2040037a97f00c0c218b7f6bd9cd31.tar.bz2
scummvm-rg350-72a44cdc7b2040037a97f00c0c218b7f6bd9cd31.zip
WINTERMUTE: Add the script-classes
Diffstat (limited to 'engines')
-rw-r--r--engines/wintermute/BDebugger.cpp200
-rw-r--r--engines/wintermute/BDebugger.h90
-rw-r--r--engines/wintermute/BGame.h17
-rw-r--r--engines/wintermute/BNamedObject.cpp64
-rw-r--r--engines/wintermute/BNamedObject.h50
-rw-r--r--engines/wintermute/BScriptHolder.h10
-rw-r--r--engines/wintermute/BScriptable.cpp188
-rw-r--r--engines/wintermute/BScriptable.h80
-rw-r--r--engines/wintermute/ConvertUTF.c612
-rw-r--r--engines/wintermute/ConvertUTF.h149
-rw-r--r--engines/wintermute/StringUtil.cpp320
-rw-r--r--engines/wintermute/StringUtil.h67
-rw-r--r--engines/wintermute/SysClass.h4
-rw-r--r--engines/wintermute/dcscript.h148
-rw-r--r--engines/wintermute/module.mk9
-rw-r--r--engines/wintermute/persistent.h44
-rw-r--r--engines/wintermute/scriptables/SXArray.cpp234
-rw-r--r--engines/wintermute/scriptables/SXArray.h54
-rw-r--r--engines/wintermute/scriptables/SXDate.cpp264
-rw-r--r--engines/wintermute/scriptables/SXDate.h53
-rw-r--r--engines/wintermute/scriptables/SXMath.cpp290
-rw-r--r--engines/wintermute/scriptables/SXMath.h53
-rw-r--r--engines/wintermute/scriptables/SXMemBuffer.cpp475
-rw-r--r--engines/wintermute/scriptables/SXMemBuffer.h59
-rw-r--r--engines/wintermute/scriptables/ScEngine.cpp842
-rw-r--r--engines/wintermute/scriptables/ScEngine.h178
-rw-r--r--engines/wintermute/scriptables/ScScript.cpp1600
-rw-r--r--engines/wintermute/scriptables/ScScript.h185
-rw-r--r--engines/wintermute/scriptables/ScStack.cpp226
-rw-r--r--engines/wintermute/scriptables/ScStack.h66
-rw-r--r--engines/wintermute/scriptables/ScValue.cpp1025
-rw-r--r--engines/wintermute/scriptables/ScValue.h140
-rw-r--r--engines/wintermute/utils.cpp340
-rw-r--r--engines/wintermute/utils.h68
-rw-r--r--engines/wintermute/wintypes.h49
-rw-r--r--engines/wintermute/wme_debugger.h167
36 files changed, 8362 insertions, 58 deletions
diff --git a/engines/wintermute/BDebugger.cpp b/engines/wintermute/BDebugger.cpp
new file mode 100644
index 0000000000..8e5faa8db1
--- /dev/null
+++ b/engines/wintermute/BDebugger.cpp
@@ -0,0 +1,200 @@
+/*
+This file is part of WME Lite.
+http://dead-code.org/redir.php?target=wmelite
+
+Copyright (c) 2011 Jan Nedoma
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "BDebugger.h"
+
+namespace WinterMute {
+
+//////////////////////////////////////////////////////////////////////////
+CBDebugger::CBDebugger(CBGame *inGame) : CBBase(inGame) {
+ m_Enabled = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+CBDebugger::~CBDebugger(void) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::Initialize() {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::Shutdown() {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnGameInit() {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnGameShutdown() {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnGameTick() {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnLog(unsigned int ErrorCode, const char *Text) {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnScriptInit(CScScript *Script) {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnScriptEventThreadInit(CScScript *Script, CScScript *ParentScript, const char *Name) {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnScriptMethodThreadInit(CScScript *Script, CScScript *ParentScript, const char *Name) {
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnScriptShutdown(CScScript *Script) {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnScriptChangeLine(CScScript *Script, int Line) {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnScriptChangeScope(CScScript *Script, CScValue *Scope) {
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnScriptShutdownScope(CScScript *Script, CScValue *Scope) {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnVariableInit(EWmeDebuggerVariableType Type, CScScript *Script, CScValue *Scope, CScValue *Var, const char *VariableName) {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnVariableChangeValue(CScValue *Var, CScValue *Value) {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBDebugger::OnScriptHitBreakpoint(CScScript *Script) {
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// IWmeDebugServer interface implementation
+bool CBDebugger::AttachClient(IWmeDebugClient *Client) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBDebugger::DetachClient(IWmeDebugClient *Client) {
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CBDebugger::QueryData(IWmeDebugClient *Client) {
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CBDebugger::GetPropInt(const char *PropName) {
+ return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+double CBDebugger::GetPropFloat(const char *PropName) {
+ return 0.0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *CBDebugger::GetPropString(const char *PropName) {
+ return "";
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBDebugger::GetPropBool(const char *PropName) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBDebugger::SetProp(const char *PropName, int PropValue) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBDebugger::SetProp(const char *PropName, double PropValue) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBDebugger::SetProp(const char *PropName, const char *PropValue) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBDebugger::SetProp(const char *PropName, bool PropValue) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBDebugger::ResolveFilename(const char *RelativeFilename, char *AbsFilenameBuf, int AbsBufSize) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBDebugger::AddBreakpoint(const char *ScriptFilename, int Line) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBDebugger::RemoveBreakpoint(const char *ScriptFilename, int Line) {
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBDebugger::ContinueExecution() {
+ return false;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/BDebugger.h b/engines/wintermute/BDebugger.h
new file mode 100644
index 0000000000..28b37cee4f
--- /dev/null
+++ b/engines/wintermute/BDebugger.h
@@ -0,0 +1,90 @@
+/*
+This file is part of WME Lite.
+http://dead-code.org/redir.php?target=wmelite
+
+Copyright (c) 2011 Jan Nedoma
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef __WmeBDebugger_H__
+#define __WmeBDebugger_H__
+
+
+#include "BBase.h"
+#include "wme_debugger.h"
+
+namespace WinterMute {
+class CScScript;
+class CScValue;
+class CBDebugger : public CBBase, public IWmeDebugServer {
+public:
+ CBDebugger(CBGame *inGame);
+ virtual ~CBDebugger(void);
+
+ // initialization
+ bool m_Enabled;
+ HRESULT Initialize();
+ HRESULT Shutdown();
+
+ // internal interface
+ HRESULT OnGameInit();
+ HRESULT OnGameShutdown();
+ HRESULT OnGameTick();
+ HRESULT OnLog(unsigned int ErrorCode, const char *Text);
+ HRESULT OnScriptInit(CScScript *Script);
+ HRESULT OnScriptEventThreadInit(CScScript *Script, CScScript *ParentScript, const char *Name);
+ HRESULT OnScriptMethodThreadInit(CScScript *Script, CScScript *ParentScript, const char *Name);
+
+ HRESULT OnScriptShutdown(CScScript *Script);
+ HRESULT OnScriptChangeLine(CScScript *Script, int Line);
+ HRESULT OnScriptChangeScope(CScScript *Script, CScValue *Scope);
+ HRESULT OnScriptShutdownScope(CScScript *Script, CScValue *Scope);
+ HRESULT OnVariableInit(EWmeDebuggerVariableType Type, CScScript *Script, CScValue *Scope, CScValue *Var, const char *VariableName);
+ HRESULT OnVariableChangeValue(CScValue *Var, CScValue *Value);
+
+ HRESULT OnScriptHitBreakpoint(CScScript *Script);
+
+ // IWmeDebugServer interface
+ virtual bool AttachClient(IWmeDebugClient *Client);
+ virtual bool DetachClient(IWmeDebugClient *Client);
+ virtual bool QueryData(IWmeDebugClient *Client);
+
+ virtual int GetPropInt(const char *PropName);
+ virtual double GetPropFloat(const char *PropName);
+ virtual const char *GetPropString(const char *PropName);
+ virtual bool GetPropBool(const char *PropName);
+
+ virtual bool SetProp(const char *PropName, int PropValue);
+ virtual bool SetProp(const char *PropName, double PropValue);
+ virtual bool SetProp(const char *PropName, const char *PropValue);
+ virtual bool SetProp(const char *PropName, bool PropValue);
+
+ virtual bool ResolveFilename(const char *RelativeFilename, char *AbsFilenameBuf, int AbsBufSize);
+
+ virtual bool AddBreakpoint(const char *ScriptFilename, int Line);
+ virtual bool RemoveBreakpoint(const char *ScriptFilename, int Line);
+
+ virtual bool ContinueExecution();
+private:
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/BGame.h b/engines/wintermute/BGame.h
index 199417ede6..a61ba1270d 100644
--- a/engines/wintermute/BGame.h
+++ b/engines/wintermute/BGame.h
@@ -29,7 +29,7 @@
#ifndef WINTERMUTE_BGAME_H
#define WINTERMUTE_BGAME_H
-//#include "BDebugger.h"
+#include "BDebugger.h"
//#include "BSaveThumbHelper.h"
//#include "BFader.h"
//#include "BRenderer.h"
@@ -43,6 +43,7 @@ namespace WinterMute {
typedef void (*ENGINE_LOG_CALLBACK)(char *Text, HRESULT Result, void *Data);
class CBSoundMgr;
+class CBFader;
class CBFont;
class CBFileManager;
class CBTransitionMgr;
@@ -171,8 +172,8 @@ public:
HRESULT Initialize3();
CBFileManager *m_FileManager;
CBTransitionMgr *m_TransMgr;
- CBDebugger *GetDebugMgr();
#endif //TODO: STUB
+ CBDebugger *GetDebugMgr();
void LOG(HRESULT res, LPCSTR fmt, ...) {}
#if 0
CBRenderer *m_Renderer;
@@ -195,6 +196,7 @@ public:
CBArray<CBQuickMsg *, CBQuickMsg *> m_QuickMessages;
CBArray<CUIWindow *, CUIWindow *> m_Windows;
CBArray<CBViewport *, CBViewport *> m_ViewportStack;
+#endif
int m_ViewportSP;
bool m_MouseLeftDown;
bool m_MouseRightDown;
@@ -214,13 +216,13 @@ public:
char *m_SettingsGameFile;
CBFader *m_Fader;
bool m_SuppressScriptErrors;
-
+#if 0
virtual HRESULT InvalidateDeviceObjects();
virtual HRESULT RestoreDeviceObjects();
-
+#endif
virtual void PublishNatives();
virtual HRESULT ExternalCall(CScScript *Script, CScStack *Stack, CScStack *ThisStack, char *Name);
-
+#if 0
// scripting interface
virtual CScValue *ScGetProperty(char *Name);
virtual HRESULT ScSetProperty(char *Name, CScValue *Value);
@@ -289,6 +291,7 @@ public:
virtual HRESULT LoadGame(char *Filename);
virtual HRESULT SaveGame(int slot, char *desc, bool quickSave = false);
virtual HRESULT ShowCursor();
+#endif
CBSprite *m_CursorNoninteractive;
CBObject *m_ActiveObject;
CBKeyboardState *m_KeyboardState;
@@ -303,13 +306,15 @@ public:
uint32 m_LiveTimer;
uint32 m_LiveTimerDelta;
uint32 m_LiveTimerLast;
-
+#if 0
CBObject *m_CapturedObject;
POINT m_MousePos;
bool ValidObject(CBObject *Object);
HRESULT UnregisterObject(CBObject *Object);
HRESULT RegisterObject(CBObject *Object);
+#endif
void QuickMessage(char *Text);
+#if 0
void QuickMessageForm(LPSTR fmt, ...);
HRESULT DisplayQuickMsg();
uint32 m_Fps;
diff --git a/engines/wintermute/BNamedObject.cpp b/engines/wintermute/BNamedObject.cpp
new file mode 100644
index 0000000000..0f2df63aea
--- /dev/null
+++ b/engines/wintermute/BNamedObject.cpp
@@ -0,0 +1,64 @@
+/* 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 "dcgf.h"
+#include "BNamedObject.h"
+
+namespace WinterMute {
+
+//////////////////////////////////////////////////////////////////////////
+CBNamedObject::CBNamedObject(CBGame *inGame) : CBBase(inGame) {
+ m_Name = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+CBNamedObject::CBNamedObject() : CBBase() {
+ m_Name = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CBNamedObject::CBNamedObject(TDynamicConstructor, TDynamicConstructor) {
+ m_Name = NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+CBNamedObject::~CBNamedObject(void) {
+ SAFE_DELETE_ARRAY(m_Name);
+}
+
+
+//////////////////////////////////////////////////////////////////////
+void CBNamedObject::SetName(char *Name) {
+ SAFE_DELETE_ARRAY(m_Name);
+
+ m_Name = new char [strlen(Name) + 1];
+ if (m_Name != NULL) strcpy(m_Name, Name);
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/BNamedObject.h b/engines/wintermute/BNamedObject.h
new file mode 100644
index 0000000000..6473748134
--- /dev/null
+++ b/engines/wintermute/BNamedObject.h
@@ -0,0 +1,50 @@
+/* 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_BNAMEDOBJECT_H
+#define WINTERMUTE_BNAMEDOBJECT_H
+
+
+#include "BBase.h"
+
+namespace WinterMute {
+
+class CBNamedObject : public CBBase {
+public:
+ CBNamedObject(CBGame *inGame);
+ CBNamedObject();
+ virtual ~CBNamedObject(void);
+ CBNamedObject(TDynamicConstructor, TDynamicConstructor);
+
+ char *m_Name;
+ void SetName(char *Name);
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/BScriptHolder.h b/engines/wintermute/BScriptHolder.h
index aaa5176bcf..6f37461431 100644
--- a/engines/wintermute/BScriptHolder.h
+++ b/engines/wintermute/BScriptHolder.h
@@ -31,18 +31,14 @@
#include "coll_templ.h"
#include "persistent.h"
-// TODO: Reinclude any of this.
-//#include "BScriptable.h"
+#include "BScriptable.h"
namespace WinterMute {
-//class CBScriptHolder : public CBScriptable {
-class CBScriptHolder : CBBase {
+class CBScriptHolder : public CBScriptable {
public:
- DECLARE_PERSISTENT(CBScriptHolder, CBBase) // <- TODO, quickfix for compile
-#if 0
DECLARE_PERSISTENT(CBScriptHolder, CBScriptable)
-
+#if 0
CBScriptHolder(CBGame *inGame);
virtual ~CBScriptHolder();
diff --git a/engines/wintermute/BScriptable.cpp b/engines/wintermute/BScriptable.cpp
new file mode 100644
index 0000000000..9b875eeffb
--- /dev/null
+++ b/engines/wintermute/BScriptable.cpp
@@ -0,0 +1,188 @@
+/* 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/BScriptable.h"
+#include "engines/wintermute/scriptables/ScValue.h"
+#include "engines/wintermute/BPersistMgr.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CBScriptable, false)
+
+//////////////////////////////////////////////////////////////////////////
+CBScriptable::CBScriptable(CBGame *inGame, bool NoValue, bool Persistable): CBNamedObject(inGame) {
+ m_RefCount = 0;
+
+ if (NoValue) m_ScValue = NULL;
+ else m_ScValue = new CScValue(Game);
+
+ m_Persistable = Persistable;
+
+ m_ScProp = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CBScriptable::~CBScriptable() {
+ //if(m_RefCount>0) Game->LOG(0, "Warning: Destroying object, m_RefCount=%d", m_RefCount);
+ delete m_ScValue;
+ delete m_ScProp;
+ m_ScValue = NULL;
+ m_ScProp = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// high level scripting interface
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBScriptable::ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, char *Name) {
+ /*
+ Stack->CorrectParams(0);
+ Stack->PushNULL();
+ Script->RuntimeError("Call to undefined method '%s'.", Name);
+
+ return S_OK;
+ */
+ return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CBScriptable::ScGetProperty(char *Name) {
+ if (!m_ScProp) m_ScProp = new CScValue(Game);
+ if (m_ScProp) return m_ScProp->GetProp(Name);
+ else return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBScriptable::ScSetProperty(char *Name, CScValue *Value) {
+ if (!m_ScProp) m_ScProp = new CScValue(Game);
+ if (m_ScProp) return m_ScProp->SetProp(Name, Value);
+ else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *CBScriptable::ScToString() {
+ return "[native object]";
+}
+
+//////////////////////////////////////////////////////////////////////////
+void *CBScriptable::ScToMemBuffer() {
+ return (void *)NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CBScriptable::ScToInt() {
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double CBScriptable::ScToFloat() {
+ return 0.0f;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CBScriptable::ScToBool() {
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CBScriptable::ScSetString(const char *Val) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CBScriptable::ScSetInt(int Val) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CBScriptable::ScSetFloat(double Val) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CBScriptable::ScSetBool(bool Val) {
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CBScriptable::Persist(CBPersistMgr *PersistMgr) {
+ PersistMgr->Transfer(TMEMBER(Game));
+ PersistMgr->Transfer(TMEMBER(m_RefCount));
+ PersistMgr->Transfer(TMEMBER(m_ScProp));
+ PersistMgr->Transfer(TMEMBER(m_ScValue));
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CBScriptable::ScCompare(CBScriptable *Val) {
+ if (this < Val) return -1;
+ else if (this > Val) return 1;
+ else return 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CBScriptable::ScDebuggerDesc(char *Buf, int BufSize) {
+ strcpy(Buf, ScToString());
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBScriptable::CanHandleMethod(char *EventMethod) {
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScScript *CBScriptable::InvokeMethodThread(char *MethodName) {
+ return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// IWmeDebugObject
+//////////////////////////////////////////////////////////////////////////
+const char *CBScriptable::DbgGetNativeClass() {
+ return GetClassName();
+}
+
+//////////////////////////////////////////////////////////////////////////
+IWmeDebugProp *CBScriptable::DbgGetProperty(const char *Name) {
+ return ScGetProperty((char *)Name);
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/BScriptable.h b/engines/wintermute/BScriptable.h
new file mode 100644
index 0000000000..2a87b9c305
--- /dev/null
+++ b/engines/wintermute/BScriptable.h
@@ -0,0 +1,80 @@
+/* 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_BSCRIPTABLE_H
+#define WINTERMUTE_BSCRIPTABLE_H
+
+
+#include "engines/wintermute/BNamedObject.h"
+#include "engines/wintermute/wme_debugger.h"
+#include "engines/wintermute/persistent.h"
+
+namespace WinterMute {
+
+class CScValue;
+class CScStack;
+class CScScript;
+
+class CBScriptable : public CBNamedObject, public IWmeDebugObject {
+public:
+ virtual CScScript *InvokeMethodThread(char *MethodName);
+ DECLARE_PERSISTENT(CBScriptable, CBNamedObject)
+
+ CBScriptable(CBGame *inGame, bool NoValue = false, bool Persistable = true);
+ virtual ~CBScriptable();
+
+ // high level scripting interface
+ virtual bool CanHandleMethod(char *EventMethod);
+ virtual HRESULT ScSetProperty(char *Name, CScValue *Value);
+ virtual CScValue *ScGetProperty(char *Name);
+ virtual HRESULT ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, char *Name);
+ virtual char *ScToString();
+ virtual void *ScToMemBuffer();
+ virtual int ScToInt();
+ virtual double ScToFloat();
+ virtual bool ScToBool();
+ virtual void ScSetString(const char *Val);
+ virtual void ScSetInt(int Val);
+ virtual void ScSetFloat(double Val);
+ virtual void ScSetBool(bool Val);
+ virtual int ScCompare(CBScriptable *Val);
+ virtual void ScDebuggerDesc(char *Buf, int BufSize);
+ int m_RefCount;
+ CScValue *m_ScValue;
+ CScValue *m_ScProp;
+
+public:
+ // IWmeDebugObject
+ const char *DbgGetNativeClass();
+ IWmeDebugProp *DbgGetProperty(const char *Name);
+
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/ConvertUTF.c b/engines/wintermute/ConvertUTF.c
new file mode 100644
index 0000000000..8f7d6d2124
--- /dev/null
+++ b/engines/wintermute/ConvertUTF.c
@@ -0,0 +1,612 @@
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Sept 2001: fixed const & error conditions per
+ mods suggested by S. Parent & A. Lillich.
+ June 2002: Tim Dodd added detection and handling of incomplete
+ source sequences, enhanced error detection, added casts
+ to eliminate compiler warnings.
+ July 2003: slight mods to back out aggressive FFFE detection.
+ Jan 2004: updated switches in from-UTF8 conversions.
+ Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
+
+ See the header file "ConvertUTF.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+
+#include "ConvertUTF.h"
+#ifdef CVTUTF_DEBUG
+#include <stdio.h>
+#endif
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+#define false 0
+#define true 1
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF16(
+ const UTF32 **sourceStart, const UTF32 *sourceEnd,
+ UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32 *source = *sourceStart;
+ UTF16 *target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ if (target >= targetEnd) {
+ result = targetExhausted;
+ break;
+ }
+ ch = *source++;
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_LEGAL_UTF32) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ --source; /* Back up source pointer! */
+ result = targetExhausted;
+ break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF32(
+ const UTF16 **sourceStart, const UTF16 *sourceEnd,
+ UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16 *source = *sourceStart;
+ UTF32 *target = *targetStart;
+ UTF32 ch, ch2;
+ while (source < sourceEnd) {
+ const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ if (target >= targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ result = targetExhausted;
+ break;
+ }
+ *target++ = ch;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+#ifdef CVTUTF_DEBUG
+ if (result == sourceIllegal) {
+ fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
+ fflush(stderr);
+ }
+#endif
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
+ * left as-is for anyone who may want to do such conversion, which was
+ * allowed in earlier algorithms.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL
+ };
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow. There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... etc.). Remember that sequencs
+ * for *legal* UTF-8 will be 4 or fewer bytes total.
+ */
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+/* --------------------------------------------------------------------- */
+
+/* The interface converts a whole buffer to avoid function-call overhead.
+ * Constants have been gathered. Loops & conditionals have been removed as
+ * much as possible for efficiency, in favor of drop-through switches.
+ * (See "Note A" at the bottom of the file for equivalent code.)
+ * If your compiler supports it, the "isLegalUTF8" call can be turned
+ * into an inline function.
+ */
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF8(
+ const UTF16 **sourceStart, const UTF16 *sourceEnd,
+ UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16 *source = *sourceStart;
+ UTF8 *target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
+ /* If the 16 bits following the high surrogate are in the source buffer... */
+ if (source < sourceEnd) {
+ UTF32 ch2 = *source;
+ /* If it's a low surrogate, convert to UTF32. */
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else { /* We don't have the 16 bits following the high surrogate. */
+ --source; /* return to the high surrogate */
+ result = sourceExhausted;
+ break;
+ }
+ } else if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /* Figure out how many bytes the result will require */
+ if (ch < (UTF32)0x80) {
+ bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) {
+ bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) {
+ bytesToWrite = 3;
+ } else if (ch < (UTF32)0x110000) {
+ bytesToWrite = 4;
+ } else {
+ bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ target -= bytesToWrite;
+ result = targetExhausted;
+ break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 3:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 2:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 1:
+ *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static Boolean isLegalUTF8(const UTF8 *source, int length) {
+ UTF8 a;
+ const UTF8 *srcptr = source + length;
+ switch (length) {
+ default:
+ return false;
+ /* Everything else falls through when "true"... */
+ case 4:
+ if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 3:
+ if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 2:
+ if ((a = (*--srcptr)) > 0xBF) return false;
+
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0:
+ if (a < 0xA0) return false;
+ break;
+ case 0xED:
+ if (a > 0x9F) return false;
+ break;
+ case 0xF0:
+ if (a < 0x90) return false;
+ break;
+ case 0xF4:
+ if (a > 0x8F) return false;
+ break;
+ default:
+ if (a < 0x80) return false;
+ }
+
+ case 1:
+ if (*source >= 0x80 && *source < 0xC2) return false;
+ }
+ if (*source > 0xF4) return false;
+ return true;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Exported function to return whether a UTF-8 sequence is legal or not.
+ * This is not used here; it's just exported.
+ */
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
+ int length = trailingBytesForUTF8[*source] + 1;
+ if (source + length > sourceEnd) {
+ return false;
+ }
+ return isLegalUTF8(source, length);
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF16(
+ const UTF8 **sourceStart, const UTF8 *sourceEnd,
+ UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8 *source = *sourceStart;
+ UTF16 *target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted;
+ break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead + 1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5:
+ ch += *source++;
+ ch <<= 6; /* remember, illegal UTF-8 */
+ case 4:
+ ch += *source++;
+ ch <<= 6; /* remember, illegal UTF-8 */
+ case 3:
+ ch += *source++;
+ ch <<= 6;
+ case 2:
+ ch += *source++;
+ ch <<= 6;
+ case 1:
+ ch += *source++;
+ ch <<= 6;
+ case 0:
+ ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead + 1); /* Back up source pointer! */
+ result = targetExhausted;
+ break;
+ }
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead + 1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = (UTF16)ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_UTF16) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ source -= (extraBytesToRead + 1); /* return to the start */
+ break; /* Bail out; shouldn't continue */
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ source -= (extraBytesToRead + 1); /* Back up source pointer! */
+ result = targetExhausted;
+ break;
+ }
+ ch -= halfBase;
+ *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
+ *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF32toUTF8(
+ const UTF32 **sourceStart, const UTF32 *sourceEnd,
+ UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF32 *source = *sourceStart;
+ UTF8 *target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ ch = *source++;
+ if (flags == strictConversion) {
+ /* UTF-16 surrogate values are illegal in UTF-32 */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ }
+ /*
+ * Figure out how many bytes the result will require. Turn any
+ * illegally large UTF32 things (> Plane 17) into replacement chars.
+ */
+ if (ch < (UTF32)0x80) {
+ bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) {
+ bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) {
+ bytesToWrite = 3;
+ } else if (ch <= UNI_MAX_LEGAL_UTF32) {
+ bytesToWrite = 4;
+ } else {
+ bytesToWrite = 3;
+ ch = UNI_REPLACEMENT_CHAR;
+ result = sourceIllegal;
+ }
+
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ --source; /* Back up source pointer! */
+ target -= bytesToWrite;
+ result = targetExhausted;
+ break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 3:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 2:
+ *--target = (UTF8)((ch | byteMark) & byteMask);
+ ch >>= 6;
+ case 1:
+ *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF32(
+ const UTF8 **sourceStart, const UTF8 *sourceEnd,
+ UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8 *source = *sourceStart;
+ UTF32 *target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted;
+ break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead + 1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 5:
+ ch += *source++;
+ ch <<= 6;
+ case 4:
+ ch += *source++;
+ ch <<= 6;
+ case 3:
+ ch += *source++;
+ ch <<= 6;
+ case 2:
+ ch += *source++;
+ ch <<= 6;
+ case 1:
+ ch += *source++;
+ ch <<= 6;
+ case 0:
+ ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead + 1); /* Back up the source pointer! */
+ result = targetExhausted;
+ break;
+ }
+ if (ch <= UNI_MAX_LEGAL_UTF32) {
+ /*
+ * UTF-16 surrogate values are illegal in UTF-32, and anything
+ * over Plane 17 (> 0x10FFFF) is illegal.
+ */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
+ if (flags == strictConversion) {
+ source -= (extraBytesToRead + 1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ *target++ = ch;
+ }
+ } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
+ result = sourceIllegal;
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* ---------------------------------------------------------------------
+
+ Note A.
+ The fall-through switches in UTF-8 reading code save a
+ temp variable, some decrements & conditionals. The switches
+ are equivalent to the following loop:
+ {
+ int tmpBytesToRead = extraBytesToRead+1;
+ do {
+ ch += *source++;
+ --tmpBytesToRead;
+ if (tmpBytesToRead) ch <<= 6;
+ } while (tmpBytesToRead > 0);
+ }
+ In UTF-8 writing code, the switches on "bytesToWrite" are
+ similarly unrolled loops.
+
+ --------------------------------------------------------------------- */
diff --git a/engines/wintermute/ConvertUTF.h b/engines/wintermute/ConvertUTF.h
new file mode 100644
index 0000000000..03a8bb2bae
--- /dev/null
+++ b/engines/wintermute/ConvertUTF.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2001-2004 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Header file.
+
+ Several funtions are included here, forming a complete set of
+ conversions between the three formats. UTF-7 is not included
+ here, but is handled in a separate source file.
+
+ Each of these routines takes pointers to input buffers and output
+ buffers. The input buffers are const.
+
+ Each routine converts the text between *sourceStart and sourceEnd,
+ putting the result into the buffer between *targetStart and
+ targetEnd. Note: the end pointers are *after* the last item: e.g.
+ *(sourceEnd - 1) is the last item.
+
+ The return result indicates whether the conversion was successful,
+ and if not, whether the problem was in the source or target buffers.
+ (Only the first encountered problem is indicated.)
+
+ After the conversion, *sourceStart and *targetStart are both
+ updated to point to the end of last text successfully converted in
+ the respective buffers.
+
+ Input parameters:
+ sourceStart - pointer to a pointer to the source buffer.
+ The contents of this are modified on return so that
+ it points at the next thing to be converted.
+ targetStart - similarly, pointer to pointer to the target buffer.
+ sourceEnd, targetEnd - respectively pointers to the ends of the
+ two buffers, for overflow checking only.
+
+ These conversion functions take a ConversionFlags argument. When this
+ flag is set to strict, both irregular sequences and isolated surrogates
+ will cause an error. When the flag is set to lenient, both irregular
+ sequences and isolated surrogates are converted.
+
+ Whether the flag is strict or lenient, all illegal sequences will cause
+ an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
+ or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
+ must check for illegal sequences.
+
+ When the flag is set to lenient, characters over 0x10FFFF are converted
+ to the replacement character; otherwise (when the flag is set to strict)
+ they constitute an error.
+
+ Output parameters:
+ The value "sourceIllegal" is returned from some routines if the input
+ sequence is malformed. When "sourceIllegal" is returned, the source
+ value will point to the illegal value that caused the problem. E.g.,
+ in UTF-8 when a sequence is malformed, it points to the start of the
+ malformed sequence.
+
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Fixes & updates, Sept 2001.
+
+------------------------------------------------------------------------ */
+
+/* ---------------------------------------------------------------------
+ The following 4 definitions are compiler-specific.
+ The C standard does not guarantee that wchar_t has at least
+ 16 bits, so wchar_t is no less portable than unsigned short!
+ All should be unsigned values to avoid sign extension during
+ bit mask & shift operations.
+------------------------------------------------------------------------ */
+
+typedef unsigned long UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+typedef unsigned char Boolean; /* 0 or 1 */
+
+/* Some fundamental constants */
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
+
+typedef enum {
+ conversionOK, /* conversion successful */
+ sourceExhausted, /* partial character in source, but hit end */
+ targetExhausted, /* insuff. room in target for conversion */
+ sourceIllegal /* source sequence is illegal/malformed */
+} ConversionResult;
+
+typedef enum {
+ strictConversion = 0,
+ lenientConversion
+} ConversionFlags;
+
+/* This is for C++ and does no harm in C */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ ConversionResult ConvertUTF8toUTF16(
+ const UTF8 **sourceStart, const UTF8 *sourceEnd,
+ UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags);
+
+ ConversionResult ConvertUTF16toUTF8(
+ const UTF16 **sourceStart, const UTF16 *sourceEnd,
+ UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags);
+
+ ConversionResult ConvertUTF8toUTF32(
+ const UTF8 **sourceStart, const UTF8 *sourceEnd,
+ UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags);
+
+ ConversionResult ConvertUTF32toUTF8(
+ const UTF32 **sourceStart, const UTF32 *sourceEnd,
+ UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags);
+
+ ConversionResult ConvertUTF16toUTF32(
+ const UTF16 **sourceStart, const UTF16 *sourceEnd,
+ UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags);
+
+ ConversionResult ConvertUTF32toUTF16(
+ const UTF32 **sourceStart, const UTF32 *sourceEnd,
+ UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags);
+
+ Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* --------------------------------------------------------------------- */
diff --git a/engines/wintermute/StringUtil.cpp b/engines/wintermute/StringUtil.cpp
new file mode 100644
index 0000000000..6a42857615
--- /dev/null
+++ b/engines/wintermute/StringUtil.cpp
@@ -0,0 +1,320 @@
+/*
+This file is part of WME Lite.
+http://dead-code.org/redir.php?target=wmelite
+
+Copyright (c) 2011 Jan Nedoma
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "dcgf.h"
+#include <algorithm>
+#include <string>
+#include <sstream>
+#include "common/tokenizer.h"
+#include "StringUtil.h"
+#include "ConvertUTF.h"
+
+namespace WinterMute {
+
+//////////////////////////////////////////////////////////////////////////
+void StringUtil::ToLowerCase(AnsiString &str) {
+ std::transform(str.begin(), str.end(), str.begin(), ::tolower);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void StringUtil::ToLowerCase(WideString &str) {
+ std::transform(str.begin(), str.end(), str.begin(), ::towlower);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void StringUtil::ToUpperCase(AnsiString &str) {
+ std::transform(str.begin(), str.end(), str.begin(), ::toupper);
+}
+
+//////////////////////////////////////////////////////////////////////////
+void StringUtil::ToUpperCase(WideString &str) {
+ std::transform(str.begin(), str.end(), str.begin(), ::towupper);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool StringUtil::CompareNoCase(const AnsiString &str1, const AnsiString &str2) {
+ AnsiString str1lc = str1;
+ AnsiString str2lc = str2;
+
+ ToLowerCase(str1lc);
+ ToLowerCase(str2lc);
+
+ return (str1lc == str2lc);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool StringUtil::CompareNoCase(const WideString &str1, const WideString &str2) {
+ WideString str1lc = str1;
+ WideString str2lc = str2;
+
+ ToLowerCase(str1lc);
+ ToLowerCase(str2lc);
+
+ return (str1lc == str2lc);
+}
+
+//////////////////////////////////////////////////////////////////////////
+WideString StringUtil::Utf8ToWide(const Utf8String &Utf8Str) {
+ size_t WideSize = Utf8Str.length();
+
+ if (sizeof(wchar_t) == 2) {
+ wchar_t *WideStringNative = new wchar_t[WideSize + 1];
+
+ const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(Utf8Str.c_str());
+ const UTF8 *SourceEnd = SourceStart + WideSize;
+
+ UTF16 *TargetStart = reinterpret_cast<UTF16 *>(WideStringNative);
+ UTF16 *TargetEnd = TargetStart + WideSize + 1;
+
+ ConversionResult res = ConvertUTF8toUTF16(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion);
+ if (res != conversionOK) {
+ delete [] WideStringNative;
+ return L"";
+ }
+ *TargetStart = 0;
+ WideString ResultString(WideStringNative);
+ delete [] WideStringNative;
+
+ return ResultString;
+ } else if (sizeof(wchar_t) == 4) {
+ wchar_t *WideStringNative = new wchar_t[WideSize + 1];
+
+ const UTF8 *SourceStart = reinterpret_cast<const UTF8 *>(Utf8Str.c_str());
+ const UTF8 *SourceEnd = SourceStart + WideSize;
+
+ UTF32 *TargetStart = reinterpret_cast<UTF32 *>(WideStringNative);
+ UTF32 *TargetEnd = TargetStart + WideSize;
+
+ ConversionResult res = ConvertUTF8toUTF32(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion);
+ if (res != conversionOK) {
+ delete [] WideStringNative;
+ return L"";
+ }
+ *TargetStart = 0;
+ WideString ResultString(WideStringNative);
+ delete [] WideStringNative;
+
+ return ResultString;
+ } else {
+ return L"";
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+Utf8String StringUtil::WideToUtf8(const WideString &WideStr) {
+ size_t WideSize = WideStr.length();
+
+ if (sizeof(wchar_t) == 2) {
+ size_t Utf8Size = 3 * WideSize + 1;
+ char *Utf8StringNative = new char[Utf8Size];
+
+ const UTF16 *SourceStart = reinterpret_cast<const UTF16 *>(WideStr.c_str());
+ const UTF16 *SourceEnd = SourceStart + WideSize;
+
+ UTF8 *TargetStart = reinterpret_cast<UTF8 *>(Utf8StringNative);
+ UTF8 *TargetEnd = TargetStart + Utf8Size;
+
+ ConversionResult res = ConvertUTF16toUTF8(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion);
+ if (res != conversionOK) {
+ delete [] Utf8StringNative;
+ return (Utf8String)"";
+ }
+ *TargetStart = 0;
+ Utf8String ResultString(Utf8StringNative);
+ delete [] Utf8StringNative;
+ return ResultString;
+ } else if (sizeof(wchar_t) == 4) {
+ size_t Utf8Size = 4 * WideSize + 1;
+ char *Utf8StringNative = new char[Utf8Size];
+
+ const UTF32 *SourceStart = reinterpret_cast<const UTF32 *>(WideStr.c_str());
+ const UTF32 *SourceEnd = SourceStart + WideSize;
+
+ UTF8 *TargetStart = reinterpret_cast<UTF8 *>(Utf8StringNative);
+ UTF8 *TargetEnd = TargetStart + Utf8Size;
+
+ ConversionResult res = ConvertUTF32toUTF8(&SourceStart, SourceEnd, &TargetStart, TargetEnd, strictConversion);
+ if (res != conversionOK) {
+ delete [] Utf8StringNative;
+ return (Utf8String)"";
+ }
+ *TargetStart = 0;
+ Utf8String ResultString(Utf8StringNative);
+ delete [] Utf8StringNative;
+ return ResultString;
+ } else {
+ return (Utf8String)"";
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+WideString StringUtil::AnsiToWide(const AnsiString &str) {
+ // using default os locale!
+ setlocale(LC_CTYPE, "");
+ size_t WideSize = mbstowcs(NULL, str.c_str(), 0) + 1;
+ wchar_t *wstr = new wchar_t[WideSize];
+ mbstowcs(wstr, str.c_str(), WideSize);
+ WideString ResultString(wstr);
+ delete [] wstr;
+ return ResultString;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString StringUtil::WideToAnsi(const WideString &wstr) {
+ // using default os locale!
+ setlocale(LC_CTYPE, "");
+ size_t WideSize = wcstombs(NULL, wstr.c_str(), 0) + 1;
+ char *str = new char[WideSize];
+ wcstombs(str, wstr.c_str(), WideSize);
+ AnsiString ResultString(str);
+ delete [] str;
+ return ResultString;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool StringUtil::StartsWith(const AnsiString &str, const AnsiString &pattern, bool ignoreCase) {
+ size_t strLength = str.length();
+ size_t patternLength = pattern.length();
+
+ if (strLength < patternLength || patternLength == 0)
+ return false;
+
+ AnsiString startPart = str.substr(0, patternLength);
+
+ if (ignoreCase) return CompareNoCase(startPart, pattern);
+ else return (startPart == pattern);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool StringUtil::EndsWith(const AnsiString &str, const AnsiString &pattern, bool ignoreCase) {
+ size_t strLength = str.length();
+ size_t patternLength = pattern.length();
+
+ if (strLength < patternLength || patternLength == 0)
+ return false;
+
+ AnsiString endPart = str.substr(strLength - patternLength, patternLength);
+
+ if (ignoreCase) return CompareNoCase(endPart, pattern);
+ else return (endPart == pattern);
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool StringUtil::IsUtf8BOM(const byte *Buffer, uint32 BufferSize) {
+ if (BufferSize > 3 && Buffer[0] == 0xEF && Buffer[1] == 0xBB && Buffer[2] == 0xBF) return true;
+ else return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString StringUtil::Replace(const AnsiString &str, const AnsiString &from, const AnsiString &to) {
+ if (from.empty() || from == to) return str;
+
+ AnsiString result = str;
+ size_t pos = 0;
+
+ while (true) {
+ pos = result.find(from, pos);
+ if (pos == result.npos) break;
+
+ result.replace(pos, from.size(), to);
+ pos += to.size();
+ }
+
+ return result;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString StringUtil::Trim(const AnsiString &str, bool fromLeft, bool fromRight, const AnsiString &chars) {
+ AnsiString trimmedStr = str;
+
+ if (fromRight)
+ trimmedStr.erase(trimmedStr.find_last_not_of(chars) + 1);
+ if (fromLeft)
+ trimmedStr.erase(0, trimmedStr.find_first_not_of(chars));
+
+ return trimmedStr;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int StringUtil::IndexOf(const WideString &str, const WideString &toFind, size_t startFrom) {
+ size_t pos = str.find(toFind, startFrom);
+ if (pos == str.npos) return -1;
+ else return pos;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int StringUtil::LastIndexOf(const WideString &str, const WideString &toFind, size_t startFrom) {
+ size_t pos = str.rfind(toFind, startFrom);
+ if (pos == str.npos) return -1;
+ else return pos;
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString StringUtil::ToString(size_t val) {
+ std::ostringstream str;
+ str << val;
+ return str.str();
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString StringUtil::ToString(int val) {
+ std::ostringstream str;
+ str << val;
+ return str.str();
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString StringUtil::ToString(float val) {
+ std::ostringstream str;
+ str << val;
+ return str.str();
+}
+
+//////////////////////////////////////////////////////////////////////////
+AnsiString StringUtil::ToString(double val) {
+ std::ostringstream str;
+ str << val;
+ return str.str();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void StringUtil::Split(const AnsiString &list, const AnsiString &delimiters, AnsiStringArray &result, bool keepEmptyItems) {
+ result.clear();
+//TODO: Verify this, wrt keepEmptyItems.
+ Common::StringTokenizer tokenizer(list.c_str(), delimiters.c_str());
+ //typedef boost::char_separator<char> separator_t;
+ //typedef boost::tokenizer<separator_t, AnsiString::const_iterator, AnsiString> tokenizer_t;
+
+ //separator_t del(delimiters.c_str(), "", keepEmptyItems ? boost::keep_empty_tokens : boost::drop_empty_tokens);
+ //tokenizer_t tokens(list, del);
+ while (!tokenizer.empty()) {
+ std::string copy(tokenizer.nextToken().c_str());
+ result.push_back(copy);
+ }
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/StringUtil.h b/engines/wintermute/StringUtil.h
new file mode 100644
index 0000000000..20758ed0cd
--- /dev/null
+++ b/engines/wintermute/StringUtil.h
@@ -0,0 +1,67 @@
+/*
+This file is part of WME Lite.
+http://dead-code.org/redir.php?target=wmelite
+
+Copyright (c) 2011 Jan Nedoma
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef __WmeStringUtil_H__
+#define __WmeStringUtil_H__
+
+#include "PlatformSDL.h"
+
+namespace WinterMute {
+
+class StringUtil {
+public:
+ static void ToLowerCase(AnsiString &str);
+ static void ToLowerCase(WideString &str);
+ static void ToUpperCase(AnsiString &str);
+ static void ToUpperCase(WideString &str);
+ static bool CompareNoCase(const AnsiString &str1, const AnsiString &str2);
+ static bool CompareNoCase(const WideString &str1, const WideString &str2);
+ static WideString Utf8ToWide(const Utf8String &Utf8Str);
+ static Utf8String WideToUtf8(const WideString &WideStr);
+ static WideString AnsiToWide(const AnsiString &str);
+ static AnsiString WideToAnsi(const WideString &str);
+
+ static bool StartsWith(const AnsiString &str, const AnsiString &pattern, bool ignoreCase = false);
+ static bool EndsWith(const AnsiString &str, const AnsiString &pattern, bool ignoreCase = false);
+
+ static bool IsUtf8BOM(const byte *buffer, uint32 bufferSize);
+
+ static AnsiString Replace(const AnsiString &str, const AnsiString &from, const AnsiString &to);
+ static AnsiString Trim(const AnsiString &str, bool fromLeft = true, bool fromRight = true, const AnsiString &chars = " \n\r\t");
+
+ static int IndexOf(const WideString &str, const WideString &toFind, size_t startFrom);
+ static int LastIndexOf(const WideString &str, const WideString &toFind, size_t startFrom);
+
+ static AnsiString ToString(size_t val);
+ static AnsiString ToString(int val);
+ static AnsiString ToString(float val);
+ static AnsiString ToString(double val);
+
+ static void Split(const AnsiString &list, const AnsiString &delimiters, AnsiStringArray &result, bool keepEmptyItems = false);
+};
+
+} // end of namespace WinterMute
+
+#endif // __WmeStringUtil_H__
diff --git a/engines/wintermute/SysClass.h b/engines/wintermute/SysClass.h
index 21d4acd594..ede3d41c22 100644
--- a/engines/wintermute/SysClass.h
+++ b/engines/wintermute/SysClass.h
@@ -23,8 +23,8 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
-#ifndef __WmeSysClass_H__
-#define __WmeSysClass_H__
+#ifndef WINTERMUTE_SYSCLASS_H
+#define WINTERMUTE_SYSCLASS_H
#include "persistent.h"
#include <set>
diff --git a/engines/wintermute/dcscript.h b/engines/wintermute/dcscript.h
new file mode 100644
index 0000000000..98e4f74c53
--- /dev/null
+++ b/engines/wintermute/dcscript.h
@@ -0,0 +1,148 @@
+/* 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_DCSCRIPT_H
+#define WINTERMUTE_DCSCRIPT_H
+
+namespace WinterMute {
+
+#define SCRIPT_MAGIC 0xDEC0ADDE
+#define SCRIPT_VERSION 0x0102
+
+// value types
+typedef enum {
+ VAL_NULL,
+ VAL_STRING,
+ VAL_INT,
+ VAL_BOOL,
+ VAL_FLOAT,
+ VAL_OBJECT,
+ VAL_NATIVE,
+ VAL_VARIABLE_REF
+} TValType;
+
+
+// script states
+typedef enum {
+ SCRIPT_RUNNING, SCRIPT_WAITING, SCRIPT_SLEEPING, SCRIPT_FINISHED, SCRIPT_PERSISTENT, SCRIPT_ERROR, SCRIPT_PAUSED, SCRIPT_WAITING_SCRIPT, SCRIPT_THREAD_FINISHED
+} TScriptState;
+
+// opcodes
+typedef enum {
+ II_DEF_VAR = 0,
+ II_DEF_GLOB_VAR,
+ II_RET,
+ II_RET_EVENT,
+ II_CALL,
+ II_CALL_BY_EXP,
+ II_EXTERNAL_CALL,
+ II_SCOPE,
+ II_CORRECT_STACK,
+ II_CREATE_OBJECT,
+ II_POP_EMPTY,
+ II_PUSH_VAR,
+ II_PUSH_VAR_REF,
+ II_POP_VAR,
+ II_PUSH_VAR_THIS, // push current this on stack
+ II_PUSH_INT,
+ II_PUSH_BOOL,
+ II_PUSH_FLOAT,
+ II_PUSH_STRING,
+ II_PUSH_NULL,
+ II_PUSH_THIS_FROM_STACK,
+ II_PUSH_THIS,
+ II_POP_THIS,
+ II_PUSH_BY_EXP,
+ II_POP_BY_EXP,
+ II_JMP,
+ II_JMP_FALSE,
+ II_ADD,
+ II_SUB,
+ II_MUL,
+ II_DIV,
+ II_MODULO,
+ II_NOT,
+ II_AND,
+ II_OR,
+ II_CMP_EQ,
+ II_CMP_NE,
+ II_CMP_L,
+ II_CMP_G,
+ II_CMP_LE,
+ II_CMP_GE,
+ II_CMP_STRICT_EQ,
+ II_CMP_STRICT_NE,
+ II_DBG_LINE,
+ II_POP_REG1,
+ II_PUSH_REG1,
+ II_DEF_CONST_VAR
+} TInstruction;
+
+// external data types
+typedef enum {
+ TYPE_VOID = 0,
+ TYPE_BOOL,
+ TYPE_LONG,
+ TYPE_BYTE,
+ TYPE_STRING,
+ TYPE_FLOAT,
+ TYPE_DOUBLE,
+ TYPE_MEMBUFFER
+} TExternalType;
+
+
+// call types
+typedef enum {
+ CALL_STDCALL = 0,
+ CALL_CDECL,
+ CALL_THISCALL
+} TCallType;
+
+// element types
+typedef enum {
+ ELEMENT_STRING = 0
+} TElementType;
+
+
+
+// compiler interface
+typedef byte *(WINAPI DLL_LOAD_FILE)(void *Data, char *Filename, uint32 *Size);
+typedef void (WINAPI DLL_CLOSE_FILE)(void *Data, byte *Buffer);
+typedef void (WINAPI DLL_ADD_ERROR)(void *Data, int Line, char *Text);
+typedef void (WINAPI DLL_PARSE_ELEMENT)(void *Data, int Line, int Type, void *ElementData);
+
+typedef struct {
+ DLL_LOAD_FILE *Dll_LoadFile;
+ DLL_CLOSE_FILE *Dll_CloseFile;
+ DLL_ADD_ERROR *Dll_AddError;
+ DLL_PARSE_ELEMENT *Dll_ParseElement;
+} CALLBACKS;
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/module.mk b/engines/wintermute/module.mk
index 2eda176c75..45708e2b84 100644
--- a/engines/wintermute/module.mk
+++ b/engines/wintermute/module.mk
@@ -1,8 +1,17 @@
MODULE := engines/wintermute
MODULE_OBJS := \
+ scriptables/ScScript.o \
+ scriptables/ScStack.o \
+ scriptables/ScValue.o \
+ scriptables/SXArray.o \
+ scriptables/SXDate.o \
+ scriptables/SXMath.o \
+ scriptables/SXMemBuffer.o \
BBase.o \
+ BNamedObject.o \
BParser.o \
+ BScriptable.o \
detection.o \
SysClass.o \
SysInstance.o \
diff --git a/engines/wintermute/persistent.h b/engines/wintermute/persistent.h
index 2e1c9f3214..5d0b7ee5fb 100644
--- a/engines/wintermute/persistent.h
+++ b/engines/wintermute/persistent.h
@@ -1,26 +1,30 @@
-/*
- This file is part of WME Lite.
- http://dead-code.org/redir.php?target=wmelite
-
- Copyright (c) 2011 Jan Nedoma
+/* 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.
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
+ * 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.
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
+ * 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
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
*/
#ifndef WINTERMUTE_PERSISTENT_H
diff --git a/engines/wintermute/scriptables/SXArray.cpp b/engines/wintermute/scriptables/SXArray.cpp
new file mode 100644
index 0000000000..4a67cdfb5a
--- /dev/null
+++ b/engines/wintermute/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/scriptables/ScValue.h"
+#include "engines/wintermute/scriptables/ScStack.h"
+#include "engines/wintermute/SysInstance.h"
+#include "engines/wintermute/scriptables/SXArray.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXArray, false)
+
+//////////////////////////////////////////////////////////////////////////
+CSXArray::CSXArray(CBGame *inGame, CScStack *Stack): CBScriptable(inGame) {
+ m_Length = 0;
+ m_Values = new CScValue(Game);
+
+ int NumParams = Stack->Pop()->GetInt(0);
+
+ if (NumParams == 1) m_Length = Stack->Pop()->GetInt(0);
+ else if (NumParams > 1) {
+ m_Length = NumParams;
+ char ParamName[20];
+ for (int i = 0; i < NumParams; i++) {
+ sprintf(ParamName, "%d", i);
+ m_Values->SetProp(ParamName, Stack->Pop());
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+CSXArray::CSXArray(CBGame *inGame): CBScriptable(inGame) {
+ m_Length = 0;
+ m_Values = new CScValue(Game);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXArray::~CSXArray() {
+ delete m_Values;
+ m_Values = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *CSXArray::ScToString() {
+ static char Dummy[32768];
+ strcpy(Dummy, "");
+ char PropName[20];
+ for (int i = 0; i < m_Length; i++) {
+ sprintf(PropName, "%d", i);
+ CScValue *val = m_Values->GetProp(PropName);
+ if (val) {
+ if (strlen(Dummy) + strlen(val->GetString()) < 32768) {
+ strcat(Dummy, val->GetString());
+ }
+ }
+
+ if (i < m_Length - 1 && strlen(Dummy) + 1 < 32768) strcat(Dummy, ",");
+ }
+ return Dummy;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXArray::ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, char *Name) {
+ //////////////////////////////////////////////////////////////////////////
+ // Push
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "Push") == 0) {
+ int NumParams = Stack->Pop()->GetInt(0);
+ char ParamName[20];
+
+ for (int i = 0; i < NumParams; i++) {
+ m_Length++;
+ sprintf(ParamName, "%d", m_Length - 1);
+ m_Values->SetProp(ParamName, Stack->Pop(), true);
+ }
+ Stack->PushInt(m_Length);
+
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Pop
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "Pop") == 0) {
+
+ Stack->CorrectParams(0);
+
+ if (m_Length > 0) {
+ char ParamName[20];
+ sprintf(ParamName, "%d", m_Length - 1);
+ Stack->Push(m_Values->GetProp(ParamName));
+ m_Values->DeleteProp(ParamName);
+ m_Length--;
+ } else Stack->PushNULL();
+
+ return S_OK;
+ }
+
+ else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CSXArray::ScGetProperty(char *Name) {
+ m_ScValue->SetNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "Type") == 0) {
+ m_ScValue->SetString("array");
+ return m_ScValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Length
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "Length") == 0) {
+ m_ScValue->SetInt(m_Length);
+ return m_ScValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // [number]
+ //////////////////////////////////////////////////////////////////////////
+ else {
+ char ParamName[20];
+ if (ValidNumber(Name, ParamName)) {
+ return m_Values->GetProp(ParamName);
+ } else return m_ScValue;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXArray::ScSetProperty(char *Name, CScValue *Value) {
+ //////////////////////////////////////////////////////////////////////////
+ // Length
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "Length") == 0) {
+ int OrigLength = m_Length;
+ m_Length = std::max(Value->GetInt(0), 0);
+
+ char PropName[20];
+ if (m_Length < OrigLength) {
+ for (int i = m_Length; i < OrigLength; i++) {
+ sprintf(PropName, "%d", i);
+ m_Values->DeleteProp(PropName);
+ }
+ }
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // [number]
+ //////////////////////////////////////////////////////////////////////////
+ else {
+ char ParamName[20];
+ if (ValidNumber(Name, ParamName)) {
+ int Index = atoi(ParamName);
+ if (Index >= m_Length) m_Length = Index + 1;
+ return m_Values->SetProp(ParamName, Value);
+ } else return E_FAIL;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXArray::Persist(CBPersistMgr *PersistMgr) {
+ CBScriptable::Persist(PersistMgr);
+
+ PersistMgr->Transfer(TMEMBER(m_Length));
+ PersistMgr->Transfer(TMEMBER(m_Values));
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXArray::ValidNumber(const char *OrigStr, char *OutStr) {
+ bool IsNumber = true;
+ for (int i = 0; i < strlen(OrigStr); i++) {
+ if (!isdigit(OrigStr[i])) {
+ 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];
+ m_Length++;
+ sprintf(ParamName, "%d", m_Length - 1);
+ m_Values->SetProp(ParamName, Val, true);
+ return S_OK;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/scriptables/SXArray.h b/engines/wintermute/scriptables/SXArray.h
new file mode 100644
index 0000000000..5283db2efb
--- /dev/null
+++ b/engines/wintermute/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/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(char *Name);
+ HRESULT ScSetProperty(char *Name, CScValue *Value);
+ HRESULT ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, char *Name);
+ char *ScToString();
+ int m_Length;
+ CScValue *m_Values;
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/scriptables/SXDate.cpp b/engines/wintermute/scriptables/SXDate.cpp
new file mode 100644
index 0000000000..39b50da528
--- /dev/null
+++ b/engines/wintermute/scriptables/SXDate.cpp
@@ -0,0 +1,264 @@
+/*
+This file is part of WME Lite.
+http://dead-code.org/redir.php?target=wmelite
+
+Copyright (c) 2011 Jan Nedoma
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "engines/wintermute/scriptables/ScStack.h"
+#include "engines/wintermute/scriptables/ScValue.h"
+#include "engines/wintermute/scriptables/SXDate.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXDate, false)
+
+//////////////////////////////////////////////////////////////////////////
+CSXDate::CSXDate(CBGame *inGame, CScStack *Stack): CBScriptable(inGame) {
+ Stack->CorrectParams(6);
+
+ memset(&m_tm, 0, sizeof(m_tm));
+
+ CScValue *valYear = Stack->Pop();
+ m_tm.tm_year = valYear->GetInt() - 1900;
+ m_tm.tm_mon = Stack->Pop()->GetInt() - 1;
+ m_tm.tm_mday = Stack->Pop()->GetInt();
+ m_tm.tm_hour = Stack->Pop()->GetInt();
+ m_tm.tm_min = Stack->Pop()->GetInt();
+ m_tm.tm_sec = Stack->Pop()->GetInt();
+
+ if (valYear->IsNULL()) {
+ time_t TimeNow;
+ time(&TimeNow);
+ memcpy(&m_tm, localtime(&TimeNow), sizeof(m_tm));
+ }
+
+ mktime(&m_tm);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXDate::~CSXDate() {
+
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *CSXDate::ScToString() {
+ return asctime(&m_tm);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXDate::ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, char *Name) {
+ //////////////////////////////////////////////////////////////////////////
+ // GetYear
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "GetYear") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(m_tm.tm_year + 1900);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetMonth
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetMonth") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(m_tm.tm_mon + 1);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetDate
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetDate") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(m_tm.tm_mday);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetHours
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetHours") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(m_tm.tm_hour);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetMinutes
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetMinutes") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(m_tm.tm_min);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetSeconds
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetSeconds") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(m_tm.tm_sec);
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // GetWeekday
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "GetWeekday") == 0) {
+ Stack->CorrectParams(0);
+ Stack->PushInt(m_tm.tm_wday);
+ return S_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetYear
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetYear") == 0) {
+ Stack->CorrectParams(1);
+ m_tm.tm_year = Stack->Pop()->GetInt() - 1900;
+ mktime(&m_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetMonth
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetMonth") == 0) {
+ Stack->CorrectParams(1);
+ m_tm.tm_mon = Stack->Pop()->GetInt() - 1;
+ mktime(&m_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetDate
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetDate") == 0) {
+ Stack->CorrectParams(1);
+ m_tm.tm_mday = Stack->Pop()->GetInt();
+ mktime(&m_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetHours
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetHours") == 0) {
+ Stack->CorrectParams(1);
+ m_tm.tm_hour = Stack->Pop()->GetInt();
+ mktime(&m_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetMinutes
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetMinutes") == 0) {
+ Stack->CorrectParams(1);
+ m_tm.tm_min = Stack->Pop()->GetInt();
+ mktime(&m_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SetSeconds
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetSeconds") == 0) {
+ Stack->CorrectParams(1);
+ m_tm.tm_sec = Stack->Pop()->GetInt();
+ mktime(&m_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetCurrentTime
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetCurrentTime") == 0) {
+ Stack->CorrectParams(0);
+ time_t TimeNow;
+ time(&TimeNow);
+ memcpy(&m_tm, localtime(&TimeNow), sizeof(m_tm));
+ mktime(&m_tm);
+ Stack->PushNULL();
+ return S_OK;
+ }
+
+ else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CSXDate::ScGetProperty(char *Name) {
+ m_ScValue->SetNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "Type") == 0) {
+ m_ScValue->SetString("date");
+ return m_ScValue;
+ }
+
+ else return m_ScValue;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXDate::ScSetProperty(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 (PersistMgr->m_Saving)
+ PersistMgr->PutBytes((byte *)&m_tm, sizeof(m_tm));
+ else
+ PersistMgr->GetBytes((byte *)&m_tm, sizeof(m_tm));
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CSXDate::ScCompare(CBScriptable *Value) {
+ time_t time1 = mktime(&m_tm);
+ time_t time2 = mktime(&((CSXDate *)Value)->m_tm);
+
+ if (time1 < time2) return -1;
+ else if (time1 > time2) return 1;
+ else return 0;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/scriptables/SXDate.h b/engines/wintermute/scriptables/SXDate.h
new file mode 100644
index 0000000000..fe9191368d
--- /dev/null
+++ b/engines/wintermute/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/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(char *Name);
+ HRESULT ScSetProperty(char *Name, CScValue *Value);
+ HRESULT ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, char *Name);
+ char *ScToString();
+ char *m_String;
+ struct tm m_tm;
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/scriptables/SXMath.cpp b/engines/wintermute/scriptables/SXMath.cpp
new file mode 100644
index 0000000000..48e722a02c
--- /dev/null
+++ b/engines/wintermute/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/scriptables/SXMath.h"
+#include "engines/wintermute/scriptables/ScStack.h"
+#include "engines/wintermute/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, 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(char *Name) {
+ m_ScValue->SetNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "Type") == 0) {
+ m_ScValue->SetString("math");
+ return m_ScValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // PI
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "PI") == 0) {
+ m_ScValue->SetFloat(PI);
+ return m_ScValue;
+ }
+
+ else return m_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/scriptables/SXMath.h b/engines/wintermute/scriptables/SXMath.h
new file mode 100644
index 0000000000..a8c34e954e
--- /dev/null
+++ b/engines/wintermute/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/BScriptable.h"
+
+namespace WinterMute {
+
+class CSXMath : public CBScriptable {
+public:
+ DECLARE_PERSISTENT(CSXMath, CBScriptable)
+ CSXMath(CBGame *inGame);
+ virtual ~CSXMath();
+ virtual CScValue *ScGetProperty(char *Name);
+ virtual HRESULT ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, char *Name);
+
+private:
+ double DegreeToRadian(double Value);
+ double RadianToDegree(double Value);
+
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/scriptables/SXMemBuffer.cpp b/engines/wintermute/scriptables/SXMemBuffer.cpp
new file mode 100644
index 0000000000..38468757ea
--- /dev/null
+++ b/engines/wintermute/scriptables/SXMemBuffer.cpp
@@ -0,0 +1,475 @@
+/* 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/BScriptable.h"
+#include "engines/wintermute/scriptables/ScStack.h"
+#include "engines/wintermute/scriptables/ScScript.h"
+#include "engines/wintermute/scriptables/ScValue.h"
+#include "engines/wintermute/scriptables/SXMemBuffer.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CSXMemBuffer, false)
+
+//////////////////////////////////////////////////////////////////////////
+CSXMemBuffer::CSXMemBuffer(CBGame *inGame, CScStack *Stack): CBScriptable(inGame) {
+ Stack->CorrectParams(1);
+ m_Buffer = NULL;
+ m_Size = 0;
+
+ int NewSize = Stack->Pop()->GetInt();
+ Resize(std::max(0, NewSize));
+}
+
+//////////////////////////////////////////////////////////////////////////
+CSXMemBuffer::CSXMemBuffer(CBGame *inGame, void *Buffer): CBScriptable(inGame) {
+ m_Size = NULL;
+ m_Buffer = Buffer;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CSXMemBuffer::~CSXMemBuffer() {
+ Cleanup();
+}
+
+//////////////////////////////////////////////////////////////////////////
+void *CSXMemBuffer::ScToMemBuffer() {
+ return m_Buffer;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CSXMemBuffer::Cleanup() {
+ if (m_Size) free(m_Buffer);
+ m_Buffer = NULL;
+ m_Size = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXMemBuffer::Resize(int NewSize) {
+ int OldSize = m_Size;
+
+ if (m_Size == 0) {
+ m_Buffer = malloc(NewSize);
+ if (m_Buffer) m_Size = NewSize;
+ } else {
+ void *NewBuf = realloc(m_Buffer, NewSize);
+ if (!NewBuf) {
+ if (NewSize == 0) {
+ m_Buffer = NewBuf;
+ m_Size = NewSize;
+ } else return E_FAIL;
+ } else {
+ m_Buffer = NewBuf;
+ m_Size = NewSize;
+ }
+ }
+
+ if (m_Buffer && m_Size > OldSize) {
+ memset((byte *)m_Buffer + OldSize, 0, m_Size - OldSize);
+ }
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CSXMemBuffer::CheckBounds(CScScript *Script, int Start, int Length) {
+ if (m_Buffer == NULL) {
+ Script->RuntimeError("Cannot use Set/Get methods on an uninitialized memory buffer");
+ return false;
+ }
+ if (m_Size == 0) return true;
+
+ if (Start < 0 || Length == 0 || Start + Length > m_Size) {
+ Script->RuntimeError("Set/Get method call is out of bounds");
+ return false;
+ } else return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+char *CSXMemBuffer::ScToString() {
+ return "[membuffer object]";
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXMemBuffer::ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, char *Name) {
+ //////////////////////////////////////////////////////////////////////////
+ // SetSize
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "SetSize") == 0) {
+ Stack->CorrectParams(1);
+ int NewSize = Stack->Pop()->GetInt();
+ NewSize = std::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 *)m_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 *)m_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 *)m_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 *)m_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 *)m_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 *)m_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 < m_Size) {
+ for (int i = Start; i < m_Size; i++) {
+ if (((char *)m_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 *)m_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 *)m_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 *)m_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 *)m_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 *)m_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 *)m_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 *)m_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 *)m_Buffer + Start) = Val;
+ Stack->PushBool(true);
+ }
+ return S_OK;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SetString
+ //////////////////////////////////////////////////////////////////////////
+ else if (strcmp(Name, "SetString") == 0) {
+ Stack->CorrectParams(2);
+ int Start = Stack->Pop()->GetInt();
+ char *Val = Stack->Pop()->GetString();
+
+ if (!CheckBounds(Script, Start, strlen(Val) + 1)) Stack->PushBool(false);
+ else {
+ memcpy((byte *)m_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 *)m_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 (m_Buffer && m_Size) {
+ FILE *f = fopen("c:\\!!buffer.bin", "wb");
+ fwrite(m_Buffer, m_Size, 1, f);
+ fclose(f);
+ }
+ Stack->PushNULL();
+ return S_OK;
+ }
+
+ else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CSXMemBuffer::ScGetProperty(char *Name) {
+ m_ScValue->SetNULL();
+
+ //////////////////////////////////////////////////////////////////////////
+ // Type (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "Type") == 0) {
+ m_ScValue->SetString("membuffer");
+ return m_ScValue;
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Size (RO)
+ //////////////////////////////////////////////////////////////////////////
+ if (strcmp(Name, "Size") == 0) {
+ m_ScValue->SetInt(m_Size);
+ return m_ScValue;
+ }
+
+ else return CBScriptable::ScGetProperty(Name);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXMemBuffer::ScSetProperty(char *Name, CScValue *Value) {
+ /*
+ //////////////////////////////////////////////////////////////////////////
+ // Length
+ //////////////////////////////////////////////////////////////////////////
+ if(strcmp(Name, "Length")==0){
+ int OrigLength = m_Length;
+ m_Length = max(Value->GetInt(0), 0);
+
+ char PropName[20];
+ if(m_Length < OrigLength){
+ for(int i=m_Length; i<OrigLength; i++){
+ sprintf(PropName, "%d", i);
+ m_Values->DeleteProp(PropName);
+ }
+ }
+ return S_OK;
+ }
+ else*/ return CBScriptable::ScSetProperty(Name, Value);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CSXMemBuffer::Persist(CBPersistMgr *PersistMgr) {
+
+ CBScriptable::Persist(PersistMgr);
+
+ PersistMgr->Transfer(TMEMBER(m_Size));
+
+ if (PersistMgr->m_Saving) {
+ if (m_Size > 0) PersistMgr->PutBytes((byte *)m_Buffer, m_Size);
+ } else {
+ if (m_Size > 0) {
+ m_Buffer = malloc(m_Size);
+ PersistMgr->GetBytes((byte *)m_Buffer, m_Size);
+ } else m_Buffer = NULL;
+ }
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CSXMemBuffer::ScCompare(CBScriptable *Val) {
+ if (m_Buffer == Val->ScToMemBuffer()) return 0;
+ else return 1;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/scriptables/SXMemBuffer.h b/engines/wintermute/scriptables/SXMemBuffer.h
new file mode 100644
index 0000000000..e44761b723
--- /dev/null
+++ b/engines/wintermute/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/BScriptable.h"
+
+namespace WinterMute {
+
+class CSXMemBuffer : public CBScriptable {
+public:
+ virtual int ScCompare(CBScriptable *Val);
+ DECLARE_PERSISTENT(CSXMemBuffer, CBScriptable)
+ CScValue *ScGetProperty(char *Name);
+ HRESULT ScSetProperty(char *Name, CScValue *Value);
+ HRESULT ScCallMethod(CScScript *Script, CScStack *Stack, CScStack *ThisStack, char *Name);
+ char *ScToString();
+ CSXMemBuffer(CBGame *inGame, CScStack *Stack);
+ CSXMemBuffer(CBGame *inGame, void *Buffer);
+ virtual ~CSXMemBuffer();
+ virtual void *ScToMemBuffer();
+ int m_Size;
+private:
+ HRESULT Resize(int NewSize);
+ void *m_Buffer;
+ void Cleanup();
+ bool CheckBounds(CScScript *Script, int Start, int Length);
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/scriptables/ScEngine.cpp b/engines/wintermute/scriptables/ScEngine.cpp
new file mode 100644
index 0000000000..ce3ac5a94f
--- /dev/null
+++ b/engines/wintermute/scriptables/ScEngine.cpp
@@ -0,0 +1,842 @@
+/* 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 "dcgf.h"
+#include "ScEngine.h"
+#include "StringUtil.h"
+#include "ScValue.h"
+#include "ScScript.h"
+#include "ScStack.h"
+#include "SXMath.h"
+#include "BRegistry.h"
+#include "BGame.h"
+#include "BSound.h"
+#include "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);
+
+ m_CompilerDLL = ::LoadLibrary(CompilerPath);
+ if (m_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);
+ m_CompilerDLL = ::LoadLibrary(CompilerPath);
+
+ delete [] ExeDir;
+ }
+ if (m_CompilerDLL != NULL) {
+ // bind compiler's functionality
+ ExtCompileBuffer = (DLL_COMPILE_BUFFER) ::GetProcAddress(m_CompilerDLL, "CompileBuffer");
+ ExtCompileFile = (DLL_COMPILE_FILE) ::GetProcAddress(m_CompilerDLL, "CompileFile");
+ ExtReleaseBuffer = (DLL_RELEASE_BUFFER) ::GetProcAddress(m_CompilerDLL, "ReleaseBuffer");
+ ExtSetCallbacks = (DLL_SET_CALLBACKS) ::GetProcAddress(m_CompilerDLL, "SetCallbacks");
+ ExtDefineFunction = (DLL_DEFINE_FUNCTION)::GetProcAddress(m_CompilerDLL, "DefineFunction");
+ ExtDefineVariable = (DLL_DEFINE_VARIABLE)::GetProcAddress(m_CompilerDLL, "DefineVariable");
+
+ if (!ExtCompileBuffer || !ExtCompileFile || !ExtReleaseBuffer || !ExtSetCallbacks || !ExtDefineFunction || !ExtDefineVariable) {
+ m_CompilerAvailable = false;
+ ::FreeLibrary(m_CompilerDLL);
+ m_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);
+ */
+
+ m_CompilerAvailable = true;
+ }
+ } else m_CompilerAvailable = false;
+#else
+ m_CompilerAvailable = false;
+ m_CompilerDLL = NULL;
+#endif
+
+
+ if (m_CompilerAvailable) Game->LOG(0, " Script compiler bound successfuly");
+ else Game->LOG(0, " Script compiler is NOT available");
+
+ m_Globals = new CScValue(Game);
+
+
+ // register 'Game' as global variable
+ if (!m_Globals->PropExists("Game")) {
+ CScValue val(Game);
+ val.SetNative(Game, true);
+ m_Globals->SetProp("Game", &val);
+ }
+
+ // register 'Math' as global variable
+ if (!m_Globals->PropExists("Math")) {
+ CScValue val(Game);
+ val.SetNative(Game->m_MathClass, true);
+ m_Globals->SetProp("Math", &val);
+ }
+
+ // prepare script cache
+ for (int i = 0; i < MAX_CACHED_SCRIPTS; i++) m_CachedScripts[i] = NULL;
+
+ m_CurrentScript = NULL;
+
+ m_FileToCompile = NULL;
+
+ m_CompileErrorCallback = NULL;
+ m_CompileErrorCallbackData = NULL;
+
+ m_ParseElementCallback = NULL;
+ m_ParseElementCallbackData = NULL;
+
+ m_IsProfiling = false;
+ m_ProfilingStartTime = 0;
+
+ //EnableProfiling();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScEngine::~CScEngine() {
+ Game->LOG(0, "Shutting down scripting engine");
+ SaveBreakpoints();
+
+ DisableProfiling();
+#ifdef __WIN32__
+ if (m_CompilerAvailable && m_CompilerDLL) ::FreeLibrary(m_CompilerDLL);
+#endif
+ Cleanup();
+
+ for (int i = 0; i < m_Breakpoints.GetSize(); i++) {
+ SAFE_DELETE(m_Breakpoints[i]);
+ }
+ m_Breakpoints.RemoveAll();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::Cleanup() {
+ for (int i = 0; i < m_Scripts.GetSize(); i++) {
+ if (!m_Scripts[i]->m_Thread && m_Scripts[i]->m_Owner) m_Scripts[i]->m_Owner->RemoveScript(m_Scripts[i]);
+ delete m_Scripts[i];
+ m_Scripts.RemoveAt(i);
+ i--;
+ }
+
+ m_Scripts.RemoveAll();
+
+
+ SAFE_DELETE(m_Globals);
+
+ EmptyScriptCache();
+
+ m_CurrentScript = NULL; // ref only
+
+ SAFE_DELETE_ARRAY(m_FileToCompile);
+
+ m_CompileErrorCallback = NULL;
+ m_CompileErrorCallbackData = NULL;
+
+ m_ParseElementCallback = NULL;
+ m_ParseElementCallbackData = NULL;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+byte *WINAPI CScEngine::LoadFile(void *Data, char *Filename, uint32 *Size) {
+ CBGame *Game = (CBGame *)Data;
+ return Game->m_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->m_ScEngine && Game->m_ScEngine->m_FileToCompile)
+ Game->LOG(0, "Compiling script '%s'...", Game->m_ScEngine->m_FileToCompile);
+ Game->LOG(0, " Error@line %d: %s", Line, Text);
+
+
+ // redirect to an engine's own callback
+ if (Game->m_ScEngine && Game->m_ScEngine->m_CompileErrorCallback) {
+ Game->m_ScEngine->m_CompileErrorCallback(Line, Text, Game->m_ScEngine->m_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->m_ScEngine && Game->m_ScEngine->m_ParseElementCallback) {
+ Game->m_ScEngine->m_ParseElementCallback(Line, Type, ElementData, Game->m_ScEngine->m_CompileErrorCallbackData);
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScScript *CScEngine::RunScript(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->m_Globals->SetProp("self", &val);
+ script->m_Globals->SetProp("this", &val);
+
+ m_Scripts.Add(script);
+ Game->GetDebugMgr()->OnScriptInit(script);
+
+ return script;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+byte *CScEngine::GetCompiledScript(char *Filename, uint32 *OutSize, bool IgnoreCache) {
+ int i;
+
+ // is script in cache?
+ if (!IgnoreCache) {
+ for (i = 0; i < MAX_CACHED_SCRIPTS; i++) {
+ if (m_CachedScripts[i] && scumm_stricmp(m_CachedScripts[i]->m_Filename, Filename) == 0) {
+ m_CachedScripts[i]->m_Timestamp = CBPlatform::GetTime();
+ *OutSize = m_CachedScripts[i]->m_Size;
+ return m_CachedScripts[i]->m_Buffer;
+ }
+ }
+ }
+
+ // nope, load it
+ byte *CompBuffer;
+ uint32 CompSize;
+ bool CompiledNow = false;
+
+ uint32 Size;
+
+ byte *Buffer = Game->m_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 (!m_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();
+
+ SetFileToCompile(Filename);
+ CompBuffer = ExtCompileFile(Filename, &CompSize);
+ 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 (m_CachedScripts[i] == NULL) {
+ index = i;
+ break;
+ } else if (m_CachedScripts[i]->m_Timestamp <= MinTime) {
+ MinTime = m_CachedScripts[i]->m_Timestamp;
+ index = i;
+ }
+ }
+
+ if (m_CachedScripts[index] != NULL) delete m_CachedScripts[index];
+ m_CachedScripts[index] = CachedScript;
+
+ ret = CachedScript->m_Buffer;
+ *OutSize = CachedScript->m_Size;
+ }
+
+
+ // cleanup
+ delete [] Buffer;
+ if (CompiledNow) ExtReleaseBuffer(CompBuffer);
+
+ return ret;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::Tick() {
+ int i;
+
+ if (m_Scripts.GetSize() == 0) return S_OK;
+
+
+ // resolve waiting scripts
+ for (i = 0; i < m_Scripts.GetSize(); i++) {
+
+ switch (m_Scripts[i]->m_State) {
+ case SCRIPT_WAITING: {
+ /*
+ bool obj_found=false;
+ for(int j=0; j<Game->m_RegObjects.GetSize(); j++)
+ {
+ if(Game->m_RegObjects[j] == m_Scripts[i]->m_WaitObject)
+ {
+ if(Game->m_RegObjects[j]->IsReady()) m_Scripts[i]->Run();
+ obj_found = true;
+ break;
+ }
+ }
+ if(!obj_found) m_Scripts[i]->Finish(); // m_WaitObject no longer exists
+ */
+ if (Game->ValidObject(m_Scripts[i]->m_WaitObject)) {
+ if (m_Scripts[i]->m_WaitObject->IsReady()) m_Scripts[i]->Run();
+ } else m_Scripts[i]->Finish();
+ break;
+ }
+
+ case SCRIPT_SLEEPING: {
+ if (m_Scripts[i]->m_WaitFrozen) {
+ if (m_Scripts[i]->m_WaitTime <= CBPlatform::GetTime()) m_Scripts[i]->Run();
+ } else {
+ if (m_Scripts[i]->m_WaitTime <= Game->m_Timer) m_Scripts[i]->Run();
+ }
+ break;
+ }
+
+ case SCRIPT_WAITING_SCRIPT: {
+ if (!IsValidScript(m_Scripts[i]->m_WaitScript) || m_Scripts[i]->m_WaitScript->m_State == SCRIPT_ERROR) {
+ // fake return value
+ m_Scripts[i]->m_Stack->PushNULL();
+ m_Scripts[i]->m_WaitScript = NULL;
+ m_Scripts[i]->Run();
+ } else {
+ if (m_Scripts[i]->m_WaitScript->m_State == SCRIPT_THREAD_FINISHED) {
+ // copy return value
+ m_Scripts[i]->m_Stack->Push(m_Scripts[i]->m_WaitScript->m_Stack->Pop());
+ m_Scripts[i]->Run();
+ m_Scripts[i]->m_WaitScript->Finish();
+ m_Scripts[i]->m_WaitScript = NULL;
+ }
+ }
+ break;
+ }
+ } // switch
+ } // for each script
+
+
+ // execute scripts
+ for (i = 0; i < m_Scripts.GetSize(); i++) {
+
+ // skip paused scripts
+ if (m_Scripts[i]->m_State == SCRIPT_PAUSED) continue;
+
+ // time sliced script
+ if (m_Scripts[i]->m_TimeSlice > 0) {
+ uint32 StartTime = CBPlatform::GetTime();
+ while (m_Scripts[i]->m_State == SCRIPT_RUNNING && CBPlatform::GetTime() - StartTime < m_Scripts[i]->m_TimeSlice) {
+ m_CurrentScript = m_Scripts[i];
+ m_Scripts[i]->ExecuteInstruction();
+ }
+ if (m_IsProfiling && m_Scripts[i]->m_Filename) AddScriptTime(m_Scripts[i]->m_Filename, CBPlatform::GetTime() - StartTime);
+ }
+
+ // normal script
+ else {
+ uint32 StartTime;
+ bool isProfiling = m_IsProfiling;
+ if (isProfiling) StartTime = CBPlatform::GetTime();
+
+ while (m_Scripts[i]->m_State == SCRIPT_RUNNING) {
+ m_CurrentScript = m_Scripts[i];
+ m_Scripts[i]->ExecuteInstruction();
+ }
+ if (isProfiling && m_Scripts[i]->m_Filename) AddScriptTime(m_Scripts[i]->m_Filename, CBPlatform::GetTime() - StartTime);
+ }
+ m_CurrentScript = NULL;
+ }
+
+ RemoveFinishedScripts();
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::TickUnbreakable() {
+ // execute unbreakable scripts
+ for (int i = 0; i < m_Scripts.GetSize(); i++) {
+ if (!m_Scripts[i]->m_Unbreakable) continue;
+
+ while (m_Scripts[i]->m_State == SCRIPT_RUNNING) {
+ m_CurrentScript = m_Scripts[i];
+ m_Scripts[i]->ExecuteInstruction();
+ }
+ m_Scripts[i]->Finish();
+ m_CurrentScript = NULL;
+ }
+ RemoveFinishedScripts();
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::RemoveFinishedScripts() {
+ // remove finished scripts
+ for (int i = 0; i < m_Scripts.GetSize(); i++) {
+ if (m_Scripts[i]->m_State == SCRIPT_FINISHED || m_Scripts[i]->m_State == SCRIPT_ERROR) {
+ if (!m_Scripts[i]->m_Thread && m_Scripts[i]->m_Owner) m_Scripts[i]->m_Owner->RemoveScript(m_Scripts[i]);
+ Game->GetDebugMgr()->OnScriptShutdown(m_Scripts[i]);
+ delete m_Scripts[i];
+ m_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 < m_Scripts.GetSize(); i++) {
+ if (m_Scripts[i]->m_State == SCRIPT_FINISHED) continue;
+ switch (m_Scripts[i]->m_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 (m_CachedScripts[i]) {
+ SAFE_DELETE(m_CachedScripts[i]);
+ }
+ }
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::ResetObject(CBObject *Object) {
+ // terminate all scripts waiting for this object
+ for (int i = 0; i < m_Scripts.GetSize(); i++) {
+ if (m_Scripts[i]->m_State == SCRIPT_WAITING && m_Scripts[i]->m_WaitObject == Object) {
+ if (!Game->m_CompatKillMethodThreads) ResetScript(m_Scripts[i]);
+
+ bool IsThread = m_Scripts[i]->m_MethodThread || m_Scripts[i]->m_Thread;
+ m_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 < m_Scripts.GetSize(); i++) {
+ if (m_Scripts[i]->m_State == SCRIPT_WAITING_SCRIPT && m_Scripts[i]->m_WaitScript == Script) {
+ m_Scripts[i]->Finish();
+ }
+ }
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::Persist(CBPersistMgr *PersistMgr) {
+ if (!PersistMgr->m_Saving) Cleanup();
+
+ PersistMgr->Transfer(TMEMBER(Game));
+ PersistMgr->Transfer(TMEMBER(m_CurrentScript));
+ PersistMgr->Transfer(TMEMBER(m_FileToCompile));
+ PersistMgr->Transfer(TMEMBER(m_Globals));
+ m_Scripts.Persist(PersistMgr);
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::EditorCleanup() {
+ for (int i = 0; i < m_Scripts.GetSize(); i++) {
+ if (m_Scripts[i]->m_Owner == NULL && (m_Scripts[i]->m_State == SCRIPT_FINISHED || m_Scripts[i]->m_State == SCRIPT_ERROR)) {
+ delete m_Scripts[i];
+ m_Scripts.RemoveAt(i);
+ i--;
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::PauseAll() {
+ for (int i = 0; i < m_Scripts.GetSize(); i++) {
+ if (m_Scripts[i] != m_CurrentScript) m_Scripts[i]->Pause();
+ }
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::ResumeAll() {
+ for (int i = 0; i < m_Scripts.GetSize(); i++)
+ m_Scripts[i]->Resume();
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::SetFileToCompile(char *Filename) {
+ SAFE_DELETE_ARRAY(m_FileToCompile);
+ m_FileToCompile = new char[strlen(Filename) + 1];
+ if (m_FileToCompile) {
+ strcpy(m_FileToCompile, Filename);
+ return S_OK;
+ } else return E_FAIL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::SetCompileErrorCallback(COMPILE_ERROR_CALLBACK Callback, void *Data) {
+ m_CompileErrorCallback = Callback;
+ m_CompileErrorCallbackData = Data;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::SetParseElementCallback(PARSE_ELEMENT_CALLBACK Callback, void *Data) {
+ m_ParseElementCallback = Callback;
+ m_ParseElementCallbackData = Data;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScEngine::IsValidScript(CScScript *Script) {
+ for (int i = 0; i < m_Scripts.GetSize(); i++) {
+ if (m_Scripts[i] == Script) return true;
+ }
+ return false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::ClearGlobals(bool IncludingNatives) {
+ m_Globals->CleanProps(IncludingNatives);
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::DbgSendScripts(IWmeDebugClient *Client) {
+ // send global variables
+ m_Globals->DbgSendVariables(Client, WME_DBGVAR_GLOBAL, NULL, 0);
+
+ // process normal scripts first
+ for (int i = 0; i < m_Scripts.GetSize(); i++) {
+ if (m_Scripts[i]->m_Thread || m_Scripts[i]->m_MethodThread) continue;
+ m_Scripts[i]->DbgSendScript(Client);
+ }
+
+ // and threads later
+ for (int i = 0; i < m_Scripts.GetSize(); i++) {
+ if (m_Scripts[i]->m_Thread || m_Scripts[i]->m_MethodThread)
+ m_Scripts[i]->DbgSendScript(Client);
+ }
+
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::AddBreakpoint(char *ScriptFilename, int Line) {
+ if (!Game->GetDebugMgr()->m_Enabled) return S_OK;
+
+ CScBreakpoint *Bp = NULL;
+ for (int i = 0; i < m_Breakpoints.GetSize(); i++) {
+ if (scumm_stricmp(m_Breakpoints[i]->m_Filename, ScriptFilename) == 0) {
+ Bp = m_Breakpoints[i];
+ break;
+ }
+ }
+ if (Bp == NULL) {
+ Bp = new CScBreakpoint(ScriptFilename);
+ m_Breakpoints.Add(Bp);
+ }
+ bool Found = false;
+ for (int i = 0; i < Bp->m_Lines.GetSize(); i++) {
+ if (Bp->m_Lines[i] == Line) return S_OK;
+ }
+ Bp->m_Lines.Add(Line);
+
+ // refresh changes
+ RefreshScriptBreakpoints();
+
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::RemoveBreakpoint(char *ScriptFilename, int Line) {
+ if (!Game->GetDebugMgr()->m_Enabled) return S_OK;
+
+ for (int i = 0; i < m_Breakpoints.GetSize(); i++) {
+ if (scumm_stricmp(m_Breakpoints[i]->m_Filename, ScriptFilename) == 0) {
+ for (int j = 0; j < m_Breakpoints[i]->m_Lines.GetSize(); j++) {
+ if (m_Breakpoints[i]->m_Lines[j] == Line) {
+ m_Breakpoints[i]->m_Lines.RemoveAt(j);
+ if (m_Breakpoints[i]->m_Lines.GetSize() == 0) {
+ SAFE_DELETE(m_Breakpoints[i]);
+ m_Breakpoints.RemoveAt(i);
+ }
+ // refresh changes
+ RefreshScriptBreakpoints();
+
+ return S_OK;
+ }
+ }
+ break;
+ }
+ }
+ return E_FAIL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::RefreshScriptBreakpoints() {
+ if (!Game->GetDebugMgr()->m_Enabled) return S_OK;
+
+ for (int i = 0; i < m_Scripts.GetSize(); i++) {
+ RefreshScriptBreakpoints(m_Scripts[i]);
+ }
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::RefreshScriptBreakpoints(CScScript *Script) {
+ if (!Game->GetDebugMgr()->m_Enabled) return S_OK;
+
+ if (!Script || !Script->m_Filename) return E_FAIL;
+
+ for (int i = 0; i < m_Breakpoints.GetSize(); i++) {
+ if (scumm_stricmp(m_Breakpoints[i]->m_Filename, Script->m_Filename) == 0) {
+ Script->m_Breakpoints.Copy(m_Breakpoints[i]->m_Lines);
+ return S_OK;
+ }
+ }
+ if (Script->m_Breakpoints.GetSize() > 0) Script->m_Breakpoints.RemoveAll();
+
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::SaveBreakpoints() {
+ if (!Game->GetDebugMgr()->m_Enabled) return S_OK;
+
+
+ char Text[512];
+ char Key[100];
+
+ int Count = 0;
+ for (int i = 0; i < m_Breakpoints.GetSize(); i++) {
+ for (int j = 0; j < m_Breakpoints[i]->m_Lines.GetSize(); j++) {
+ Count++;
+ sprintf(Key, "Breakpoint%d", Count);
+ sprintf(Text, "%s:%d", m_Breakpoints[i]->m_Filename, m_Breakpoints[i]->m_Lines[j]);
+
+ Game->m_Registry->WriteString("Debug", Key, Text);
+ }
+ }
+ Game->m_Registry->WriteInt("Debug", "NumBreakpoints", Count);
+
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScEngine::LoadBreakpoints() {
+ if (!Game->GetDebugMgr()->m_Enabled) return S_OK;
+
+ char Key[100];
+
+ int Count = Game->m_Registry->ReadInt("Debug", "NumBreakpoints", 0);
+ for (int i = 1; i <= Count; i++) {
+ uint32 BufSize = 512;
+ sprintf(Key, "Breakpoint%d", i);
+ AnsiString breakpoint = Game->m_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));
+ SAFE_DELETE_ARRAY(Path);
+ SAFE_DELETE_ARRAY(Line);
+ }
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::AddScriptTime(const char *Filename, uint32 Time) {
+ if (!m_IsProfiling) return;
+
+ AnsiString fileName = Filename;
+ StringUtil::ToLowerCase(fileName);
+ m_ScriptTimes[fileName] += Time;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::EnableProfiling() {
+ if (m_IsProfiling) return;
+
+ // destroy old data, if any
+ m_ScriptTimes.clear();
+
+ m_ProfilingStartTime = CBPlatform::GetTime();
+ m_IsProfiling = true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::DisableProfiling() {
+ if (!m_IsProfiling) return;
+
+ DumpStats();
+ m_IsProfiling = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScEngine::DumpStats() {
+ uint32 totalTime = CBPlatform::GetTime() - m_ProfilingStartTime;
+
+ typedef std::vector <std::pair<uint32, std::string> > TimeVector;
+ TimeVector times;
+
+ ScriptTimes::iterator it;
+ for (it = m_ScriptTimes.begin(); it != m_ScriptTimes.end(); it++) {
+ times.push_back(std::pair<uint32, std::string> (it->second, it->first));
+ }
+ 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/scriptables/ScEngine.h b/engines/wintermute/scriptables/ScEngine.h
new file mode 100644
index 0000000000..54e590bdfe
--- /dev/null
+++ b/engines/wintermute/scriptables/ScEngine.h
@@ -0,0 +1,178 @@
+/* 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/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)(char *Name);
+typedef int (*DLL_DEFINE_VARIABLE)(char *Name);
+
+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(char *Filename, byte *Buffer, uint32 Size) {
+ m_Timestamp = CBPlatform::GetTime();
+ m_Buffer = new byte[Size];
+ if (m_Buffer) memcpy(m_Buffer, Buffer, Size);
+ m_Size = Size;
+ m_Filename = new char[strlen(Filename) + 1];
+ if (m_Filename) strcpy(m_Filename, Filename);
+ };
+
+ ~CScCachedScript() {
+ if (m_Buffer) delete [] m_Buffer;
+ if (m_Filename) delete [] m_Filename;
+ };
+
+ uint32 m_Timestamp;
+ byte *m_Buffer;
+ uint32 m_Size;
+ char *m_Filename;
+ };
+
+ class CScBreakpoint {
+ public:
+ CScBreakpoint(const char *Filename) {
+ m_Filename = NULL;
+ CBUtils::SetString(&m_Filename, Filename);
+ }
+
+ ~CScBreakpoint() {
+ if (m_Filename) delete [] m_Filename;
+ m_Lines.RemoveAll();
+ }
+
+ char *m_Filename;
+ CBArray<int, int> m_Lines;
+ };
+
+
+
+
+public:
+ HRESULT DbgSendScripts(IWmeDebugClient *Client);
+
+ CBArray<CScBreakpoint *, CScBreakpoint *> m_Breakpoints;
+ HRESULT AddBreakpoint(char *ScriptFilename, int Line);
+ HRESULT RemoveBreakpoint(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 m_CompileErrorCallback;
+ void *m_CompileErrorCallbackData;
+
+ PARSE_ELEMENT_CALLBACK m_ParseElementCallback;
+ void *m_ParseElementCallbackData;
+
+ HRESULT SetFileToCompile(char *Filename);
+ char *m_FileToCompile;
+ CScScript *m_CurrentScript;
+ HRESULT ResumeAll();
+ HRESULT PauseAll();
+ void EditorCleanup();
+ HRESULT ResetObject(CBObject *Object);
+ HRESULT ResetScript(CScScript *Script);
+ HRESULT EmptyScriptCache();
+ byte *GetCompiledScript(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 *m_Globals;
+ CScScript *RunScript(char *Filename, CBScriptHolder *Owner = NULL);
+ bool m_CompilerAvailable;
+ HINSTANCE m_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 *> m_Scripts;
+
+ void EnableProfiling();
+ void DisableProfiling();
+ bool IsProfiling() {
+ return m_IsProfiling;
+ }
+
+ void AddScriptTime(const char *Filename, uint32 Time);
+ void DumpStats();
+
+private:
+
+ CScCachedScript *m_CachedScripts[MAX_CACHED_SCRIPTS];
+ bool m_IsProfiling;
+ uint32 m_ProfilingStartTime;
+
+ typedef std::map<std::string, uint32> ScriptTimes;
+ ScriptTimes m_ScriptTimes;
+
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/scriptables/ScScript.cpp b/engines/wintermute/scriptables/ScScript.cpp
new file mode 100644
index 0000000000..e954fe9d9f
--- /dev/null
+++ b/engines/wintermute/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/scriptables/ScValue.h"
+#include "engines/wintermute/scriptables/ScScript.h"
+#include "engines/wintermute/BGame.h"
+#include "engines/wintermute/scriptables/ScEngine.h"
+#include "engines/wintermute/scriptables/ScStack.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CScScript, false)
+
+//////////////////////////////////////////////////////////////////////////
+CScScript::CScScript(CBGame *inGame, CScEngine *Engine): CBBase(inGame) {
+ m_Buffer = NULL;
+ m_BufferSize = m_IP = 0;
+ m_Filename = NULL;
+ m_CurrentLine = 0;
+
+ m_Symbols = NULL;
+ m_NumSymbols = 0;
+
+ m_Engine = Engine;
+
+ m_Globals = NULL;
+
+ m_ScopeStack = NULL;
+ m_CallStack = NULL;
+ m_ThisStack = NULL;
+ m_Stack = NULL;
+
+ m_Operand = NULL;
+ m_Reg1 = NULL;
+
+ m_Functions = NULL;
+ m_NumFunctions = 0;
+
+ m_Methods = NULL;
+ m_NumMethods = 0;
+
+ m_Events = NULL;
+ m_NumEvents = 0;
+
+ m_Externals = NULL;
+ m_NumExternals = 0;
+
+ m_State = SCRIPT_FINISHED;
+ m_OrigState = SCRIPT_FINISHED;
+
+ m_WaitObject = NULL;
+ m_WaitTime = 0;
+ m_WaitFrozen = false;
+ m_WaitScript = NULL;
+
+ m_TimeSlice = 0;
+
+ m_Thread = false;
+ m_MethodThread = false;
+ m_ThreadEvent = NULL;
+
+ m_Freezable = true;
+ m_Owner = NULL;
+
+ m_Unbreakable = false;
+ m_ParentScript = NULL;
+
+ m_TracingMode = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScScript::~CScScript() {
+ Cleanup();
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::InitScript() {
+ TScriptHeader *Header = (TScriptHeader *)m_Buffer;
+ if (Header->magic != SCRIPT_MAGIC) {
+ Game->LOG(0, "File '%s' is not a valid compiled script", m_Filename);
+ Cleanup();
+ return E_FAIL;
+ }
+
+ if (Header->version > SCRIPT_VERSION) {
+ Game->LOG(0, "Script '%s' has a wrong version %d.%d (expected %d.%d)", m_Filename, Header->version / 256, Header->version % 256, SCRIPT_VERSION / 256, SCRIPT_VERSION % 256);
+ Cleanup();
+ return E_FAIL;
+ }
+
+ InitTables();
+
+ // init stacks
+ m_ScopeStack = new CScStack(Game);
+ m_CallStack = new CScStack(Game);
+ m_ThisStack = new CScStack(Game);
+ m_Stack = new CScStack(Game);
+
+ m_Operand = new CScValue(Game);
+ m_Reg1 = new CScValue(Game);
+
+
+ // skip to the beginning
+ m_IP = Header->code_start;
+ m_CurrentLine = 0;
+
+ // init breakpoints
+ m_Engine->RefreshScriptBreakpoints(this);
+
+
+ // ready to rumble...
+ m_State = SCRIPT_RUNNING;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::InitTables() {
+ uint32 OrigIP = m_IP;
+
+ TScriptHeader *Header = (TScriptHeader *)m_Buffer;
+
+ uint32 i;
+
+ // load symbol table
+ m_IP = Header->symbol_table;
+
+ m_NumSymbols = GetDWORD();
+ m_Symbols = new char*[m_NumSymbols];
+ for (i = 0; i < m_NumSymbols; i++) {
+ uint32 index = GetDWORD();
+ m_Symbols[index] = GetString();
+ }
+
+ // load functions table
+ m_IP = Header->func_table;
+
+ m_NumFunctions = GetDWORD();
+ m_Functions = new TFunctionPos[m_NumFunctions];
+ for (i = 0; i < m_NumFunctions; i++) {
+ m_Functions[i].pos = GetDWORD();
+ m_Functions[i].name = GetString();
+ }
+
+
+ // load events table
+ m_IP = Header->event_table;
+
+ m_NumEvents = GetDWORD();
+ m_Events = new TEventPos[m_NumEvents];
+ for (i = 0; i < m_NumEvents; i++) {
+ m_Events[i].pos = GetDWORD();
+ m_Events[i].name = GetString();
+ }
+
+
+ // load externals
+ if (Header->version >= 0x0101) {
+ m_IP = Header->externals_table;
+
+ m_NumExternals = GetDWORD();
+ m_Externals = new TExternalFunction[m_NumExternals];
+ for (i = 0; i < m_NumExternals; i++) {
+ m_Externals[i].dll_name = GetString();
+ m_Externals[i].name = GetString();
+ m_Externals[i].call_type = (TCallType)GetDWORD();
+ m_Externals[i].returns = (TExternalType)GetDWORD();
+ m_Externals[i].num_params = GetDWORD();
+ if (m_Externals[i].num_params > 0) {
+ m_Externals[i].params = new TExternalType[m_Externals[i].num_params];
+ for (int j = 0; j < m_Externals[i].num_params; j++) {
+ m_Externals[i].params[j] = (TExternalType)GetDWORD();
+ }
+ }
+ }
+ }
+
+ // load method table
+ m_IP = Header->method_table;
+
+ m_NumMethods = GetDWORD();
+ m_Methods = new TMethodPos[m_NumMethods];
+ for (i = 0; i < m_NumMethods; i++) {
+ m_Methods[i].pos = GetDWORD();
+ m_Methods[i].name = GetString();
+ }
+
+
+ m_IP = OrigIP;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Create(char *Filename, byte *Buffer, uint32 Size, CBScriptHolder *Owner) {
+ Cleanup();
+
+ m_Thread = false;
+ m_MethodThread = false;
+
+ delete[] m_ThreadEvent;
+ m_ThreadEvent = NULL;
+
+ m_Filename = new char[strlen(Filename) + 1];
+ if (m_Filename) strcpy(m_Filename, Filename);
+
+ m_Buffer = new byte [Size];
+ if (!m_Buffer) return E_FAIL;
+
+ memcpy(m_Buffer, Buffer, Size);
+
+ m_BufferSize = Size;
+
+ HRESULT res = InitScript();
+ if (FAILED(res)) return res;
+
+ // establish global variables table
+ m_Globals = new CScValue(Game);
+
+ m_Owner = Owner;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::CreateThread(CScScript *Original, uint32 InitIP, const char *EventName) {
+ Cleanup();
+
+ m_Thread = true;
+ m_MethodThread = false;
+ m_ThreadEvent = new char[strlen(EventName) + 1];
+ if (m_ThreadEvent) strcpy(m_ThreadEvent, EventName);
+
+ // copy filename
+ m_Filename = new char[strlen(Original->m_Filename) + 1];
+ if (m_Filename) strcpy(m_Filename, Original->m_Filename);
+
+ // copy buffer
+ m_Buffer = new byte [Original->m_BufferSize];
+ if (!m_Buffer) return E_FAIL;
+
+ memcpy(m_Buffer, Original->m_Buffer, Original->m_BufferSize);
+ m_BufferSize = Original->m_BufferSize;
+
+ // initialize
+ HRESULT res = InitScript();
+ if (FAILED(res)) return res;
+
+ // copy globals
+ m_Globals = Original->m_Globals;
+
+ // skip to the beginning of the event
+ m_IP = InitIP;
+
+ m_TimeSlice = Original->m_TimeSlice;
+ m_Freezable = Original->m_Freezable;
+ m_Owner = Original->m_Owner;
+
+ m_Engine = Original->m_Engine;
+ m_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();
+
+ m_Thread = true;
+ m_MethodThread = true;
+ m_ThreadEvent = new char[strlen(MethodName) + 1];
+ if (m_ThreadEvent) strcpy(m_ThreadEvent, MethodName);
+
+ // copy filename
+ m_Filename = new char[strlen(Original->m_Filename) + 1];
+ if (m_Filename) strcpy(m_Filename, Original->m_Filename);
+
+ // copy buffer
+ m_Buffer = new byte [Original->m_BufferSize];
+ if (!m_Buffer) return E_FAIL;
+
+ memcpy(m_Buffer, Original->m_Buffer, Original->m_BufferSize);
+ m_BufferSize = Original->m_BufferSize;
+
+ // initialize
+ HRESULT res = InitScript();
+ if (FAILED(res)) return res;
+
+ // copy globals
+ m_Globals = Original->m_Globals;
+
+ // skip to the beginning of the event
+ m_IP = IP;
+
+ m_TimeSlice = Original->m_TimeSlice;
+ m_Freezable = Original->m_Freezable;
+ m_Owner = Original->m_Owner;
+
+ m_Engine = Original->m_Engine;
+ m_ParentScript = Original;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScScript::Cleanup() {
+ if (m_Buffer) delete [] m_Buffer;
+ m_Buffer = NULL;
+
+ if (m_Filename) delete [] m_Filename;
+ m_Filename = NULL;
+
+ if (m_Symbols) delete [] m_Symbols;
+ m_Symbols = NULL;
+ m_NumSymbols = 0;
+
+ if (m_Globals && !m_Thread) delete m_Globals;
+ m_Globals = NULL;
+
+ if (m_ScopeStack) delete m_ScopeStack;
+ m_ScopeStack = NULL;
+
+ if (m_CallStack) delete m_CallStack;
+ m_CallStack = NULL;
+
+ if (m_ThisStack) delete m_ThisStack;
+ m_ThisStack = NULL;
+
+ if (m_Stack) delete m_Stack;
+ m_Stack = NULL;
+
+ if (m_Functions) delete [] m_Functions;
+ m_Functions = NULL;
+ m_NumFunctions = 0;
+
+ if (m_Methods) delete [] m_Methods;
+ m_Methods = NULL;
+ m_NumMethods = 0;
+
+ if (m_Events) delete [] m_Events;
+ m_Events = NULL;
+ m_NumEvents = 0;
+
+
+ if (m_Externals) {
+ for (int i = 0; i < m_NumExternals; i++) {
+ if (m_Externals[i].num_params > 0) delete [] m_Externals[i].params;
+ }
+ delete [] m_Externals;
+ }
+ m_Externals = NULL;
+ m_NumExternals = 0;
+
+ delete m_Operand;
+ delete m_Reg1;
+ m_Operand = NULL;
+ m_Reg1 = NULL;
+
+ delete[] m_ThreadEvent;
+ m_ThreadEvent = NULL;
+
+ m_State = SCRIPT_FINISHED;
+
+ m_WaitObject = NULL;
+ m_WaitTime = 0;
+ m_WaitFrozen = false;
+ m_WaitScript = NULL;
+
+ m_ParentScript = NULL; // ref only
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CScScript::GetDWORD() {
+ uint32 ret = *(uint32 *)(m_Buffer + m_IP);
+ m_IP += sizeof(uint32);
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double CScScript::GetFloat() {
+ double ret = *(double *)(m_Buffer + m_IP);
+ m_IP += sizeof(double);
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *CScScript::GetString() {
+ char *ret = (char *)(m_Buffer + m_IP);
+ while (*(char *)(m_Buffer + m_IP) != '\0') m_IP++;
+ m_IP++; // string terminator
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::ExecuteInstruction() {
+ HRESULT ret = S_OK;
+
+ uint32 dw;
+ char *str;
+
+ //CScValue* op = new CScValue(Game);
+ m_Operand->Cleanup();
+
+ CScValue *op1;
+ CScValue *op2;
+
+ uint32 inst = GetDWORD();
+ switch (inst) {
+
+ case II_DEF_VAR:
+ m_Operand->SetNULL();
+ dw = GetDWORD();
+ if (m_ScopeStack->m_SP < 0) {
+ m_Globals->SetProp(m_Symbols[dw], m_Operand);
+ if (Game->GetDebugMgr()->m_Enabled)
+ Game->GetDebugMgr()->OnVariableInit(WME_DBGVAR_SCRIPT, this, NULL, m_Globals->GetProp(m_Symbols[dw]), m_Symbols[dw]);
+ } else {
+ m_ScopeStack->GetTop()->SetProp(m_Symbols[dw], m_Operand);
+ if (Game->GetDebugMgr()->m_Enabled)
+ Game->GetDebugMgr()->OnVariableInit(WME_DBGVAR_SCOPE, this, m_ScopeStack->GetTop(), m_ScopeStack->GetTop()->GetProp(m_Symbols[dw]), m_Symbols[dw]);
+ }
+
+ break;
+
+ case II_DEF_GLOB_VAR:
+ case II_DEF_CONST_VAR: {
+ dw = GetDWORD();
+ char *Temp = m_Symbols[dw]; // TODO delete
+ // only create global var if it doesn't exist
+ if (!m_Engine->m_Globals->PropExists(m_Symbols[dw])) {
+ m_Operand->SetNULL();
+ m_Engine->m_Globals->SetProp(m_Symbols[dw], m_Operand, false, inst == II_DEF_CONST_VAR);
+
+ if (Game->GetDebugMgr()->m_Enabled)
+ Game->GetDebugMgr()->OnVariableInit(WME_DBGVAR_GLOBAL, this, NULL, m_Engine->m_Globals->GetProp(m_Symbols[dw]), m_Symbols[dw]);
+ }
+ break;
+ }
+
+ case II_RET:
+ if (m_ScopeStack->m_SP >= 0 && m_CallStack->m_SP >= 0) {
+ Game->GetDebugMgr()->OnScriptShutdownScope(this, m_ScopeStack->GetTop());
+
+ m_ScopeStack->Pop();
+ m_IP = (uint32)m_CallStack->Pop()->GetInt();
+
+ if (m_ScopeStack->m_SP < 0) Game->GetDebugMgr()->OnScriptChangeScope(this, NULL);
+ else Game->GetDebugMgr()->OnScriptChangeScope(this, m_ScopeStack->GetTop());
+ } else {
+ if (m_Thread) {
+ m_State = SCRIPT_THREAD_FINISHED;
+ } else {
+ if (m_NumEvents == 0 && m_NumMethods == 0) m_State = SCRIPT_FINISHED;
+ else m_State = SCRIPT_PERSISTENT;
+ }
+ }
+
+ break;
+
+ case II_RET_EVENT:
+ m_State = SCRIPT_FINISHED;
+ break;
+
+
+ case II_CALL:
+ dw = GetDWORD();
+
+ m_Operand->SetInt(m_IP);
+ m_CallStack->Push(m_Operand);
+
+ m_IP = dw;
+
+ break;
+
+ case II_CALL_BY_EXP: {
+ // push var
+ // push string
+ str = m_Stack->Pop()->GetString();
+ char *MethodName = new char[strlen(str) + 1];
+ strcpy(MethodName, str);
+
+ CScValue *var = m_Stack->Pop();
+ if (var->m_Type == VAL_VARIABLE_REF) var = var->m_ValRef;
+
+ HRESULT res = E_FAIL;
+ bool TriedNative = false;
+
+ // we are already calling this method, try native
+ if (m_Thread && m_MethodThread && strcmp(MethodName, m_ThreadEvent) == 0 && var->m_Type == VAL_NATIVE && m_Owner == var->GetNative()) {
+ TriedNative = true;
+ res = var->m_ValNative->ScCallMethod(this, m_Stack, m_ThisStack, MethodName);
+ }
+
+ if (FAILED(res)) {
+ if (var->IsNative() && var->GetNative()->CanHandleMethod(MethodName)) {
+ if (!m_Unbreakable) {
+ m_WaitScript = var->GetNative()->InvokeMethodThread(MethodName);
+ if (!m_WaitScript) {
+ m_Stack->CorrectParams(0);
+ RuntimeError("Error invoking method '%s'.", MethodName);
+ m_Stack->PushNULL();
+ } else {
+ m_State = SCRIPT_WAITING_SCRIPT;
+ m_WaitScript->CopyParameters(m_Stack);
+ }
+ } else {
+ // can call methods in unbreakable mode
+ m_Stack->CorrectParams(0);
+ RuntimeError("Cannot call method '%s'. Ignored.", MethodName);
+ m_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(m_Stack, m_ThisStack, f);
+ }
+ else{
+ // not an internal nor external, try for native function
+ Game->ExternalCall(this, m_Stack, m_ThisStack, val->GetString());
+ }
+ }
+ else{
+ m_Operand->SetInt(m_IP);
+ m_CallStack->Push(m_Operand);
+ m_IP = dw;
+ }
+ }
+ */
+ else {
+ res = E_FAIL;
+ if (var->m_Type == VAL_NATIVE && !TriedNative) res = var->m_ValNative->ScCallMethod(this, m_Stack, m_ThisStack, MethodName);
+
+ if (FAILED(res)) {
+ m_Stack->CorrectParams(0);
+ RuntimeError("Call to undefined method '%s'. Ignored.", MethodName);
+ m_Stack->PushNULL();
+ }
+ }
+ }
+ delete [] MethodName;
+ }
+ break;
+
+ case II_EXTERNAL_CALL: {
+ uint32 SymbolIndex = GetDWORD();
+
+ TExternalFunction *f = GetExternal(m_Symbols[SymbolIndex]);
+ if (f) {
+ ExternalCall(m_Stack, m_ThisStack, f);
+ } else Game->ExternalCall(this, m_Stack, m_ThisStack, m_Symbols[SymbolIndex]);
+
+ break;
+ }
+ case II_SCOPE:
+ m_Operand->SetNULL();
+ m_ScopeStack->Push(m_Operand);
+
+ if (m_ScopeStack->m_SP < 0) Game->GetDebugMgr()->OnScriptChangeScope(this, NULL);
+ else Game->GetDebugMgr()->OnScriptChangeScope(this, m_ScopeStack->GetTop());
+
+ break;
+
+ case II_CORRECT_STACK:
+ dw = GetDWORD(); // params expected
+ m_Stack->CorrectParams(dw);
+ break;
+
+ case II_CREATE_OBJECT:
+ m_Operand->SetObject();
+ m_Stack->Push(m_Operand);
+ break;
+
+ case II_POP_EMPTY:
+ m_Stack->Pop();
+ break;
+
+ case II_PUSH_VAR: {
+ CScValue *var = GetVar(m_Symbols[GetDWORD()]);
+ if (false && /*var->m_Type==VAL_OBJECT ||*/ var->m_Type == VAL_NATIVE) {
+ m_Operand->SetReference(var);
+ m_Stack->Push(m_Operand);
+ } else m_Stack->Push(var);
+ break;
+ }
+
+ case II_PUSH_VAR_REF: {
+ CScValue *var = GetVar(m_Symbols[GetDWORD()]);
+ m_Operand->SetReference(var);
+ m_Stack->Push(m_Operand);
+ break;
+ }
+
+ case II_POP_VAR: {
+ char *VarName = m_Symbols[GetDWORD()];
+ CScValue *var = GetVar(VarName);
+ if (var) {
+ CScValue *val = m_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->m_ValRef;
+ if (val->m_Type == VAL_NATIVE) var->SetValue(val);
+ else {
+ var->Copy(val);
+ }
+ }
+
+ if (Game->GetDebugMgr()->m_Enabled)
+ Game->GetDebugMgr()->OnVariableChangeValue(var, val);
+ }
+
+ break;
+ }
+
+ case II_PUSH_VAR_THIS:
+ m_Stack->Push(m_ThisStack->GetTop());
+ break;
+
+ case II_PUSH_INT:
+ m_Stack->PushInt((int)GetDWORD());
+ break;
+
+ case II_PUSH_FLOAT:
+ m_Stack->PushFloat(GetFloat());
+ break;
+
+
+ case II_PUSH_BOOL:
+ m_Stack->PushBool(GetDWORD() != 0);
+
+ break;
+
+ case II_PUSH_STRING:
+ m_Stack->PushString(GetString());
+ break;
+
+ case II_PUSH_NULL:
+ m_Stack->PushNULL();
+ break;
+
+ case II_PUSH_THIS_FROM_STACK:
+ m_Operand->SetReference(m_Stack->GetTop());
+ m_ThisStack->Push(m_Operand);
+ break;
+
+ case II_PUSH_THIS:
+ m_Operand->SetReference(GetVar(m_Symbols[GetDWORD()]));
+ m_ThisStack->Push(m_Operand);
+ break;
+
+ case II_POP_THIS:
+ m_ThisStack->Pop();
+ break;
+
+ case II_PUSH_BY_EXP: {
+ str = m_Stack->Pop()->GetString();
+ CScValue *val = m_Stack->Pop()->GetProp(str);
+ if (val) m_Stack->Push(val);
+ else m_Stack->PushNULL();
+
+ break;
+ }
+
+ case II_POP_BY_EXP: {
+ str = m_Stack->Pop()->GetString();
+ CScValue *var = m_Stack->Pop();
+ CScValue *val = m_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()->m_Enabled)
+ Game->GetDebugMgr()->OnVariableChangeValue(var, NULL);
+
+ break;
+ }
+
+ case II_PUSH_REG1:
+ m_Stack->Push(m_Reg1);
+ break;
+
+ case II_POP_REG1:
+ m_Reg1->Copy(m_Stack->Pop());
+ break;
+
+ case II_JMP:
+ m_IP = GetDWORD();
+ break;
+
+ case II_JMP_FALSE: {
+ dw = GetDWORD();
+ //if(!m_Stack->Pop()->GetBool()) m_IP = dw;
+ CScValue *Val = m_Stack->Pop();
+ if (!Val) {
+ RuntimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?");
+ } else {
+ if (!Val->GetBool()) m_IP = dw;
+ }
+ break;
+ }
+
+ case II_ADD:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ if (op1->IsNULL() || op2->IsNULL()) m_Operand->SetNULL();
+ else if (op1->GetType() == VAL_STRING || op2->GetType() == VAL_STRING) {
+ str = new char [strlen(op1->GetString()) + strlen(op2->GetString()) + 1];
+ strcpy(str, op1->GetString());
+ strcat(str, op2->GetString());
+ m_Operand->SetString(str);
+ delete [] str;
+ } else if (op1->GetType() == VAL_INT && op2->GetType() == VAL_INT)
+ m_Operand->SetInt(op1->GetInt() + op2->GetInt());
+ else m_Operand->SetFloat(op1->GetFloat() + op2->GetFloat());
+
+ m_Stack->Push(m_Operand);
+
+ break;
+
+ case II_SUB:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ if (op1->IsNULL() || op2->IsNULL()) m_Operand->SetNULL();
+ else if (op1->GetType() == VAL_INT && op2->GetType() == VAL_INT)
+ m_Operand->SetInt(op1->GetInt() - op2->GetInt());
+ else m_Operand->SetFloat(op1->GetFloat() - op2->GetFloat());
+
+ m_Stack->Push(m_Operand);
+
+ break;
+
+ case II_MUL:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ if (op1->IsNULL() || op2->IsNULL()) m_Operand->SetNULL();
+ else if (op1->GetType() == VAL_INT && op2->GetType() == VAL_INT)
+ m_Operand->SetInt(op1->GetInt() * op2->GetInt());
+ else m_Operand->SetFloat(op1->GetFloat() * op2->GetFloat());
+
+ m_Stack->Push(m_Operand);
+
+ break;
+
+ case II_DIV:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ if (op2->GetFloat() == 0.0f) RuntimeError("Division by zero.");
+
+ if (op1->IsNULL() || op2->IsNULL() || op2->GetFloat() == 0.0f) m_Operand->SetNULL();
+ else m_Operand->SetFloat(op1->GetFloat() / op2->GetFloat());
+
+ m_Stack->Push(m_Operand);
+
+ break;
+
+ case II_MODULO:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ if (op2->GetInt() == 0) RuntimeError("Division by zero.");
+
+ if (op1->IsNULL() || op2->IsNULL() || op2->GetInt() == 0) m_Operand->SetNULL();
+ else m_Operand->SetInt(op1->GetInt() % op2->GetInt());
+
+ m_Stack->Push(m_Operand);
+
+ break;
+
+ case II_NOT:
+ op1 = m_Stack->Pop();
+ //if(op1->IsNULL()) m_Operand->SetNULL();
+ if (op1->IsNULL()) m_Operand->SetBool(true);
+ else m_Operand->SetBool(!op1->GetBool());
+ m_Stack->Push(m_Operand);
+
+ break;
+
+ case II_AND:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+ if (op1 == NULL || op2 == NULL) {
+ RuntimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?");
+ m_Operand->SetBool(false);
+ } else {
+ m_Operand->SetBool(op1->GetBool() && op2->GetBool());
+ }
+ m_Stack->Push(m_Operand);
+ break;
+
+ case II_OR:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+ if (op1 == NULL || op2 == NULL) {
+ RuntimeError("Script corruption detected. Did you use '=' instead of '==' for comparison?");
+ m_Operand->SetBool(false);
+ } else {
+ m_Operand->SetBool(op1->GetBool() || op2->GetBool());
+ }
+ m_Stack->Push(m_Operand);
+ break;
+
+ case II_CMP_EQ:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ /*
+ if((op1->IsNULL() && !op2->IsNULL()) || (!op1->IsNULL() && op2->IsNULL())) m_Operand->SetBool(false);
+ else if(op1->IsNative() && op2->IsNative()){
+ m_Operand->SetBool(op1->GetNative() == op2->GetNative());
+ }
+ else if(op1->GetType()==VAL_STRING || op2->GetType()==VAL_STRING){
+ m_Operand->SetBool(scumm_stricmp(op1->GetString(), op2->GetString())==0);
+ }
+ else if(op1->GetType()==VAL_FLOAT && op2->GetType()==VAL_FLOAT){
+ m_Operand->SetBool(op1->GetFloat() == op2->GetFloat());
+ }
+ else{
+ m_Operand->SetBool(op1->GetInt() == op2->GetInt());
+ }
+ */
+
+ m_Operand->SetBool(CScValue::Compare(op1, op2) == 0);
+ m_Stack->Push(m_Operand);
+ break;
+
+ case II_CMP_NE:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ /*
+ if((op1->IsNULL() && !op2->IsNULL()) || (!op1->IsNULL() && op2->IsNULL())) m_Operand->SetBool(true);
+ else if(op1->IsNative() && op2->IsNative()){
+ m_Operand->SetBool(op1->GetNative() != op2->GetNative());
+ }
+ else if(op1->GetType()==VAL_STRING || op2->GetType()==VAL_STRING){
+ m_Operand->SetBool(scumm_stricmp(op1->GetString(), op2->GetString())!=0);
+ }
+ else if(op1->GetType()==VAL_FLOAT && op2->GetType()==VAL_FLOAT){
+ m_Operand->SetBool(op1->GetFloat() != op2->GetFloat());
+ }
+ else{
+ m_Operand->SetBool(op1->GetInt() != op2->GetInt());
+ }
+ */
+
+ m_Operand->SetBool(CScValue::Compare(op1, op2) != 0);
+ m_Stack->Push(m_Operand);
+ break;
+
+ case II_CMP_L:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ /*
+ if(op1->GetType()==VAL_FLOAT && op2->GetType()==VAL_FLOAT){
+ m_Operand->SetBool(op1->GetFloat() < op2->GetFloat());
+ }
+ else m_Operand->SetBool(op1->GetInt() < op2->GetInt());
+ */
+
+ m_Operand->SetBool(CScValue::Compare(op1, op2) < 0);
+ m_Stack->Push(m_Operand);
+ break;
+
+ case II_CMP_G:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ /*
+ if(op1->GetType()==VAL_FLOAT && op2->GetType()==VAL_FLOAT){
+ m_Operand->SetBool(op1->GetFloat() > op2->GetFloat());
+ }
+ else m_Operand->SetBool(op1->GetInt() > op2->GetInt());
+ */
+
+ m_Operand->SetBool(CScValue::Compare(op1, op2) > 0);
+ m_Stack->Push(m_Operand);
+ break;
+
+ case II_CMP_LE:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ /*
+ if(op1->GetType()==VAL_FLOAT && op2->GetType()==VAL_FLOAT){
+ m_Operand->SetBool(op1->GetFloat() <= op2->GetFloat());
+ }
+ else m_Operand->SetBool(op1->GetInt() <= op2->GetInt());
+ */
+
+ m_Operand->SetBool(CScValue::Compare(op1, op2) <= 0);
+ m_Stack->Push(m_Operand);
+ break;
+
+ case II_CMP_GE:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ /*
+ if(op1->GetType()==VAL_FLOAT && op2->GetType()==VAL_FLOAT){
+ m_Operand->SetBool(op1->GetFloat() >= op2->GetFloat());
+ }
+ else m_Operand->SetBool(op1->GetInt() >= op2->GetInt());
+ */
+
+ m_Operand->SetBool(CScValue::Compare(op1, op2) >= 0);
+ m_Stack->Push(m_Operand);
+ break;
+
+ case II_CMP_STRICT_EQ:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ //m_Operand->SetBool(op1->GetType()==op2->GetType() && op1->GetFloat()==op2->GetFloat());
+ m_Operand->SetBool(CScValue::CompareStrict(op1, op2) == 0);
+ m_Stack->Push(m_Operand);
+
+ break;
+
+ case II_CMP_STRICT_NE:
+ op2 = m_Stack->Pop();
+ op1 = m_Stack->Pop();
+
+ //m_Operand->SetBool(op1->GetType()!=op2->GetType() || op1->GetFloat()!=op2->GetFloat());
+ m_Operand->SetBool(CScValue::CompareStrict(op1, op2) != 0);
+ m_Stack->Push(m_Operand);
+ break;
+
+ case II_DBG_LINE: {
+ int NewLine = GetDWORD();
+ if (NewLine != m_CurrentLine) {
+ m_CurrentLine = NewLine;
+ if (Game->GetDebugMgr()->m_Enabled) {
+ Game->GetDebugMgr()->OnScriptChangeLine(this, m_CurrentLine);
+ for (int i = 0; i < m_Breakpoints.GetSize(); i++) {
+ if (m_Breakpoints[i] == m_CurrentLine) {
+ Game->GetDebugMgr()->OnScriptHitBreakpoint(this);
+ Sleep(0);
+ break;
+ }
+ }
+ if (m_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, m_Filename, m_CurrentLine, m_IP - sizeof(uint32));
+ m_State = SCRIPT_FINISHED;
+ ret = E_FAIL;
+ } // switch(instruction)
+
+ //delete op;
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CScScript::GetFuncPos(const char *Name) {
+ for (int i = 0; i < m_NumFunctions; i++) {
+ if (strcmp(Name, m_Functions[i].name) == 0) return m_Functions[i].pos;
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CScScript::GetMethodPos(const char *Name) {
+ for (int i = 0; i < m_NumMethods; i++) {
+ if (strcmp(Name, m_Methods[i].name) == 0) return m_Methods[i].pos;
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScScript::GetVar(char *Name) {
+ CScValue *ret = NULL;
+
+ // scope locals
+ if (m_ScopeStack->m_SP >= 0) {
+ if (m_ScopeStack->GetTop()->PropExists(Name)) ret = m_ScopeStack->GetTop()->GetProp(Name);
+ }
+
+ // script globals
+ if (ret == NULL) {
+ if (m_Globals->PropExists(Name)) ret = m_Globals->GetProp(Name);
+ }
+
+ // engine globals
+ if (ret == NULL) {
+ if (m_Engine->m_Globals->PropExists(Name)) ret = m_Engine->m_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, m_Filename, m_CurrentLine);
+ CScValue *Val = new CScValue(Game);
+ CScValue *Scope = m_ScopeStack->GetTop();
+ if (Scope) {
+ Scope->SetProp(Name, Val);
+ ret = m_ScopeStack->GetTop()->GetProp(Name);
+ } else {
+ m_Globals->SetProp(Name, Val);
+ ret = m_Globals->GetProp(Name);
+ }
+ delete Val;
+ }
+
+ return ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::WaitFor(CBObject *Object) {
+ if (m_Unbreakable) {
+ RuntimeError("Script cannot be interrupted.");
+ return S_OK;
+ }
+
+ m_State = SCRIPT_WAITING;
+ m_WaitObject = Object;
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::WaitForExclusive(CBObject *Object) {
+ m_Engine->ResetObject(Object);
+ return WaitFor(Object);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Sleep(uint32 Duration) {
+ if (m_Unbreakable) {
+ RuntimeError("Script cannot be interrupted.");
+ return S_OK;
+ }
+
+ m_State = SCRIPT_SLEEPING;
+ if (Game->m_State == GAME_FROZEN) {
+ m_WaitTime = CBPlatform::GetTime() + Duration;
+ m_WaitFrozen = true;
+ } else {
+ m_WaitTime = Game->m_Timer + Duration;
+ m_WaitFrozen = false;
+ }
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Finish(bool IncludingThreads) {
+ if (m_State != SCRIPT_FINISHED && IncludingThreads) {
+ m_State = SCRIPT_FINISHED;
+ FinishThreads();
+ } else m_State = SCRIPT_FINISHED;
+
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Run() {
+ m_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", m_Filename, m_CurrentLine);
+ Game->LOG(0, " %s", buff);
+
+ if (!Game->m_SuppressScriptErrors)
+ Game->QuickMessage("Script runtime error. View log for details.");
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Persist(CBPersistMgr *PersistMgr) {
+
+ PersistMgr->Transfer(TMEMBER(Game));
+
+ // buffer
+ if (PersistMgr->m_Saving) {
+ if (m_State != SCRIPT_PERSISTENT && m_State != SCRIPT_FINISHED && m_State != SCRIPT_THREAD_FINISHED) {
+ PersistMgr->Transfer(TMEMBER(m_BufferSize));
+ PersistMgr->PutBytes(m_Buffer, m_BufferSize);
+ } else {
+ // don't save idle/finished scripts
+ int bufferSize = 0;
+ PersistMgr->Transfer(TMEMBER(bufferSize));
+ }
+ } else {
+ PersistMgr->Transfer(TMEMBER(m_BufferSize));
+ if (m_BufferSize > 0) {
+ m_Buffer = new byte[m_BufferSize];
+ PersistMgr->GetBytes(m_Buffer, m_BufferSize);
+ InitTables();
+ } else m_Buffer = NULL;
+ }
+
+ PersistMgr->Transfer(TMEMBER(m_CallStack));
+ PersistMgr->Transfer(TMEMBER(m_CurrentLine));
+ PersistMgr->Transfer(TMEMBER(m_Engine));
+ PersistMgr->Transfer(TMEMBER(m_Filename));
+ PersistMgr->Transfer(TMEMBER(m_Freezable));
+ PersistMgr->Transfer(TMEMBER(m_Globals));
+ PersistMgr->Transfer(TMEMBER(m_IP));
+ PersistMgr->Transfer(TMEMBER(m_ScopeStack));
+ PersistMgr->Transfer(TMEMBER(m_Stack));
+ PersistMgr->Transfer(TMEMBER_INT(m_State));
+ PersistMgr->Transfer(TMEMBER(m_Operand));
+ PersistMgr->Transfer(TMEMBER_INT(m_OrigState));
+ PersistMgr->Transfer(TMEMBER(m_Owner));
+ PersistMgr->Transfer(TMEMBER(m_Reg1));
+ PersistMgr->Transfer(TMEMBER(m_Thread));
+ PersistMgr->Transfer(TMEMBER(m_ThreadEvent));
+ PersistMgr->Transfer(TMEMBER(m_ThisStack));
+ PersistMgr->Transfer(TMEMBER(m_TimeSlice));
+ PersistMgr->Transfer(TMEMBER(m_WaitObject));
+ PersistMgr->Transfer(TMEMBER(m_WaitScript));
+ PersistMgr->Transfer(TMEMBER(m_WaitTime));
+ PersistMgr->Transfer(TMEMBER(m_WaitFrozen));
+
+ PersistMgr->Transfer(TMEMBER(m_MethodThread));
+ PersistMgr->Transfer(TMEMBER(m_MethodThread));
+ PersistMgr->Transfer(TMEMBER(m_Unbreakable));
+ PersistMgr->Transfer(TMEMBER(m_ParentScript));
+
+ if (!PersistMgr->m_Saving) m_TracingMode = false;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScScript *CScScript::InvokeEventHandler(const char *EventName, bool Unbreakable) {
+ //if(m_State!=SCRIPT_PERSISTENT) return NULL;
+
+ uint32 pos = GetEventPos(EventName);
+ if (!pos) return NULL;
+
+ CScScript *thread = new CScScript(Game, m_Engine);
+ if (thread) {
+ HRESULT ret = thread->CreateThread(this, pos, EventName);
+ if (SUCCEEDED(ret)) {
+ thread->m_Unbreakable = Unbreakable;
+ m_Engine->m_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 = m_NumEvents - 1; i >= 0; i--) {
+ if (scumm_stricmp(Name, m_Events[i].name) == 0) return m_Events[i].pos;
+ }
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::CanHandleEvent(char *EventName) {
+ return GetEventPos(EventName) != 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::CanHandleMethod(char *MethodName) {
+ return GetMethodPos(MethodName) != 0;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Pause() {
+ if (m_State == SCRIPT_PAUSED) {
+ Game->LOG(0, "Attempting to pause a paused script ('%s', line %d)", m_Filename, m_CurrentLine);
+ return E_FAIL;
+ }
+
+ if (!m_Freezable || m_State == SCRIPT_PERSISTENT) return S_OK;
+
+ m_OrigState = m_State;
+ m_State = SCRIPT_PAUSED;
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::Resume() {
+ if (m_State != SCRIPT_PAUSED) return S_OK;
+
+ m_State = m_OrigState;
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScScript::TExternalFunction *CScScript::GetExternal(char *Name) {
+ for (int i = 0; i < m_NumExternals; i++) {
+ if (strcmp(Name, m_Externals[i].name) == 0) return &m_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->num_params);
+ CBDynBuffer *Buffer = new CBDynBuffer(Game, 20 * sizeof(uint32));
+
+ for (i = 0; i < Function->num_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->m_Buffer, Buffer->GetSize(), (uint32)pFunc, &StackCorrupted);
+ break;
+ default:
+ ret = Call_stdcall(Buffer->m_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--) {
+ m_Stack->Push(Stack->GetAt(i));
+ }
+ m_Stack->PushInt(NumParams);
+
+ for (i = 0; i < NumParams; i++) Stack->Pop();
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::FinishThreads() {
+ for (int i = 0; i < m_Engine->m_Scripts.GetSize(); i++) {
+ CScScript *Scr = m_Engine->m_Scripts[i];
+ if (Scr->m_Thread && Scr->m_State != SCRIPT_FINISHED && Scr->m_Owner == m_Owner && scumm_stricmp(Scr->m_Filename, m_Filename) == 0)
+ Scr->Finish(true);
+ }
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// IWmeDebugScript interface implementation
+int CScScript::DbgGetLine() {
+ return m_CurrentLine;
+}
+
+//////////////////////////////////////////////////////////////////////////
+const char *CScScript::DbgGetFilename() {
+ return m_Filename;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::DbgSendScript(IWmeDebugClient *Client) {
+ if (m_MethodThread) Client->OnScriptMethodThreadInit(this, m_ParentScript, m_ThreadEvent);
+ else if (m_Thread) Client->OnScriptEventThreadInit(this, m_ParentScript, m_ThreadEvent);
+ else Client->OnScriptInit(this);
+
+ return DbgSendVariables(Client);
+ return S_OK;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScScript::DbgSendVariables(IWmeDebugClient *Client) {
+ // send script globals
+ m_Globals->DbgSendVariables(Client, WME_DBGVAR_SCRIPT, this, 0);
+
+ // send scope variables
+ if (m_ScopeStack->m_SP >= 0) {
+ for (int i = 0; i <= m_ScopeStack->m_SP; i++) {
+ CScValue *Scope = m_ScopeStack->GetAt(i);
+ //Scope->DbgSendVariables(Client, WME_DBGVAR_SCOPE, this, (unsigned int)Scope);
+ }
+ }
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+TScriptState CScScript::DbgGetState() {
+ return m_State;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int CScScript::DbgGetNumBreakpoints() {
+ return m_Breakpoints.GetSize();
+}
+
+//////////////////////////////////////////////////////////////////////////
+int CScScript::DbgGetBreakpoint(int Index) {
+ if (Index >= 0 && Index < m_Breakpoints.GetSize()) return m_Breakpoints[Index];
+ else return -1;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::DbgSetTracingMode(bool IsTracing) {
+ m_TracingMode = IsTracing;
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScScript::DbgGetTracingMode() {
+ return m_TracingMode;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScScript::AfterLoad() {
+ if (m_Buffer == NULL) {
+ byte *buffer = m_Engine->GetCompiledScript(m_Filename, &m_BufferSize);
+ if (!buffer) {
+ Game->LOG(0, "Error reinitializing script '%s' after load. Script will be terminated.", m_Filename);
+ m_State = SCRIPT_ERROR;
+ return;
+ }
+
+ m_Buffer = new byte [m_BufferSize];
+ memcpy(m_Buffer, buffer, m_BufferSize);
+
+ InitTables();
+ }
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/scriptables/ScScript.h b/engines/wintermute/scriptables/ScScript.h
new file mode 100644
index 0000000000..6f467437da
--- /dev/null
+++ b/engines/wintermute/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/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> m_Breakpoints;
+ bool m_TracingMode;
+
+ CScScript *m_ParentScript;
+ bool m_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 *m_Operand;
+ CScValue *m_Reg1;
+ bool m_Freezable;
+ HRESULT Resume();
+ HRESULT Pause();
+ bool CanHandleEvent(char *EventName);
+ bool CanHandleMethod(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 m_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 m_WaitTime;
+ bool m_WaitFrozen;
+ CBObject *m_WaitObject;
+ CScScript *m_WaitScript;
+ TScriptState m_State;
+ TScriptState m_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 num_params;
+ TExternalType *params;
+ } TExternalFunction;
+
+
+ CScStack *m_CallStack;
+ CScStack *m_ThisStack;
+ CScStack *m_ScopeStack;
+ CScStack *m_Stack;
+ CScValue *m_Globals;
+ CScEngine *m_Engine;
+ int m_CurrentLine;
+ HRESULT ExecuteInstruction();
+ char *GetString();
+ uint32 GetDWORD();
+ double GetFloat();
+ void Cleanup();
+ HRESULT Create(char *Filename, byte *Buffer, uint32 Size, CBScriptHolder *Owner);
+ uint32 m_IP;
+ uint32 m_BufferSize;
+ byte *m_Buffer;
+ CScScript(CBGame *inGame, CScEngine *Engine);
+ virtual ~CScScript();
+ char *m_Filename;
+ char **m_Symbols;
+ int m_NumSymbols;
+ TFunctionPos *m_Functions;
+ TMethodPos *m_Methods;
+ TEventPos *m_Events;
+ int m_NumExternals;
+ TExternalFunction *m_Externals;
+ int m_NumFunctions;
+ int m_NumMethods;
+ int m_NumEvents;
+ bool m_Thread;
+ bool m_MethodThread;
+ char *m_ThreadEvent;
+ CBScriptHolder *m_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/scriptables/ScStack.cpp b/engines/wintermute/scriptables/ScStack.cpp
new file mode 100644
index 0000000000..7975223516
--- /dev/null
+++ b/engines/wintermute/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/scriptables/ScStack.h"
+#include "engines/wintermute/scriptables/ScValue.h"
+#include "engines/wintermute/BGame.h"
+
+namespace WinterMute {
+
+IMPLEMENT_PERSISTENT(CScStack, false)
+
+//////////////////////////////////////////////////////////////////////////
+CScStack::CScStack(CBGame *inGame): CBBase(inGame) {
+ m_SP = -1;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScStack::~CScStack() {
+
+#if _DEBUG
+ //Game->LOG(0, "STAT: Stack size: %d, SP=%d", m_Values.GetSize(), m_SP);
+#endif
+
+ for (int i = 0; i < m_Values.GetSize(); i++) {
+ delete m_Values[i];
+ }
+ m_Values.RemoveAll();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScStack::Pop() {
+ if (m_SP < 0) {
+ Game->LOG(0, "Fatal: Stack underflow");
+ return NULL;
+ }
+
+ return m_Values[m_SP--];
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::Push(CScValue *Val) {
+ m_SP++;
+
+ if (m_SP < m_Values.GetSize()) {
+ m_Values[m_SP]->Cleanup();
+ m_Values[m_SP]->Copy(Val);
+ } else {
+ CScValue *val = new CScValue(Game);
+ val->Copy(Val);
+ m_Values.Add(val);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScStack::GetPushValue() {
+ m_SP++;
+
+ if (m_SP >= m_Values.GetSize()) {
+ CScValue *val = new CScValue(Game);
+ m_Values.Add(val);
+ }
+ m_Values[m_SP]->Cleanup();
+ return m_Values[m_SP];
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScStack::GetTop() {
+ if (m_SP < 0 || m_SP >= m_Values.GetSize()) return NULL;
+ else return m_Values[m_SP];
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScStack::GetAt(int Index) {
+ Index = m_SP - Index;
+ if (Index < 0 || Index >= m_Values.GetSize()) return NULL;
+ else return m_Values[Index];
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScStack::CorrectParams(uint32 expected_params) {
+ int num_params = Pop()->GetInt();
+
+ if (expected_params < num_params) { // too many params
+ while (expected_params < num_params) {
+ //Pop();
+ delete m_Values[m_SP - expected_params];
+ m_Values.RemoveAt(m_SP - expected_params);
+ num_params--;
+ m_SP--;
+ }
+ } else if (expected_params > num_params) { // need more params
+ while (expected_params > num_params) {
+ //Push(null_val);
+ CScValue *null_val = new CScValue(Game);
+ null_val->SetNULL();
+ m_Values.InsertAt(m_SP - num_params + 1, null_val);
+ num_params++;
+ m_SP++;
+
+ if (m_Values.GetSize() > m_SP + 1) {
+ delete m_Values[m_Values.GetSize() - 1];
+ m_Values.RemoveAt(m_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(m_SP));
+ m_Values.Persist(PersistMgr);
+
+ return S_OK;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/scriptables/ScStack.h b/engines/wintermute/scriptables/ScStack.h
new file mode 100644
index 0000000000..70d54cbbe4
--- /dev/null
+++ b/engines/wintermute/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/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 *> m_Values;
+ int m_SP;
+
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/scriptables/ScValue.cpp b/engines/wintermute/scriptables/ScValue.cpp
new file mode 100644
index 0000000000..387bf45b46
--- /dev/null
+++ b/engines/wintermute/scriptables/ScValue.cpp
@@ -0,0 +1,1025 @@
+/* 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/BDynBuffer.h"
+#include "engines/wintermute/BGame.h"
+#include "engines/wintermute/scriptables/ScValue.h"
+#include "engines/wintermute/scriptables/ScScript.h"
+#include "engines/wintermute/StringUtil.h"
+#include "engines/wintermute/BScriptable.h"
+
+namespace WinterMute {
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+IMPLEMENT_PERSISTENT(CScValue, false)
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::CScValue(CBGame *inGame): CBBase(inGame) {
+ m_Type = VAL_NULL;
+
+ m_ValBool = false;
+ m_ValInt = 0;
+ m_ValFloat = 0.0f;
+ m_ValNative = NULL;
+ m_ValString = NULL;
+ m_ValRef = NULL;
+ m_Persistent = false;
+ m_IsConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::CScValue(CBGame *inGame, bool Val): CBBase(inGame) {
+ m_Type = VAL_BOOL;
+ m_ValBool = Val;
+
+ m_ValInt = 0;
+ m_ValFloat = 0.0f;
+ m_ValNative = NULL;
+ m_ValString = NULL;
+ m_ValRef = NULL;
+ m_Persistent = false;
+ m_IsConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::CScValue(CBGame *inGame, int Val): CBBase(inGame) {
+ m_Type = VAL_INT;
+ m_ValInt = Val;
+
+ m_ValFloat = 0.0f;
+ m_ValBool = false;
+ m_ValNative = NULL;
+ m_ValString = NULL;
+ m_ValRef = NULL;
+ m_Persistent = false;
+ m_IsConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::CScValue(CBGame *inGame, double Val): CBBase(inGame) {
+ m_Type = VAL_FLOAT;
+ m_ValFloat = Val;
+
+ m_ValInt = 0;
+ m_ValBool = false;
+ m_ValNative = NULL;
+ m_ValString = NULL;
+ m_ValRef = NULL;
+ m_Persistent = false;
+ m_IsConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::CScValue(CBGame *inGame, const char *Val): CBBase(inGame) {
+ m_Type = VAL_STRING;
+ m_ValString = NULL;
+ SetStringVal(Val);
+
+ m_ValBool = false;
+ m_ValInt = 0;
+ m_ValFloat = 0.0f;
+ m_ValNative = NULL;
+ m_ValRef = NULL;
+ m_Persistent = false;
+ m_IsConstVar = false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::Cleanup(bool IgnoreNatives) {
+ DeleteProps();
+
+ if (m_ValString) delete [] m_ValString;
+
+ if (!IgnoreNatives) {
+ if (m_ValNative && !m_Persistent) {
+ m_ValNative->m_RefCount--;
+ if (m_ValNative->m_RefCount <= 0) {
+ delete m_ValNative;
+ m_ValNative = NULL;
+ }
+ }
+ }
+
+
+ m_Type = VAL_NULL;
+
+ m_ValBool = false;
+ m_ValInt = 0;
+ m_ValFloat = 0.0f;
+ m_ValNative = NULL;
+ m_ValString = NULL;
+ m_ValRef = NULL;
+ m_Persistent = false;
+ m_IsConstVar = false;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue::~CScValue() {
+ Cleanup();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CScValue *CScValue::GetProp(char *Name) {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->GetProp(Name);
+
+ if (m_Type == VAL_STRING && strcmp(Name, "Length") == 0) {
+ Game->m_ScValue->m_Type = VAL_INT;
+
+#if 0 // TODO: Remove FreeType-dependency
+ if (Game->m_TextEncoding == TEXT_ANSI) {
+#else
+ if (true) {
+#endif
+ Game->m_ScValue->SetInt(strlen(m_ValString));
+ } else {
+ WideString wstr = StringUtil::Utf8ToWide(m_ValString);
+ Game->m_ScValue->SetInt(wstr.length());
+ }
+
+ return Game->m_ScValue;
+ }
+
+ CScValue *ret = NULL;
+
+ if (m_Type == VAL_NATIVE && m_ValNative) ret = m_ValNative->ScGetProperty(Name);
+
+ if (ret == NULL) {
+ m_ValIter = m_ValObject.find(Name);
+ if (m_ValIter != m_ValObject.end()) ret = m_ValIter->second;
+ }
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScValue::DeleteProp(char *Name) {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->DeleteProp(Name);
+
+ m_ValIter = m_ValObject.find(Name);
+ if (m_ValIter != m_ValObject.end()) {
+ delete m_ValIter->second;
+ m_ValIter->second = NULL;
+ }
+
+ return S_OK;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScValue::SetProp(char *Name, CScValue *Val, bool CopyWhole, bool SetAsConst) {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->SetProp(Name, Val);
+
+ HRESULT ret = E_FAIL;
+ if (m_Type == VAL_NATIVE && m_ValNative) {
+ ret = m_ValNative->ScSetProperty(Name, Val);
+ }
+
+ if (FAILED(ret)) {
+ CScValue *val = NULL;
+
+ m_ValIter = m_ValObject.find(Name);
+ if (m_ValIter != m_ValObject.end()) {
+ val = m_ValIter->second;
+ }
+ if (!val) val = new CScValue(Game);
+ else val->Cleanup();
+
+ val->Copy(Val, CopyWhole);
+ val->m_IsConstVar = SetAsConst;
+ m_ValObject[Name] = val;
+
+ if (m_Type != VAL_NATIVE) m_Type = VAL_OBJECT;
+
+ /*
+ m_ValIter = m_ValObject.find(Name);
+ if (m_ValIter != m_ValObject.end()){
+ delete m_ValIter->second;
+ m_ValIter->second = NULL;
+ }
+ CScValue* val = new CScValue(Game);
+ val->Copy(Val, CopyWhole);
+ val->m_IsConstVar = SetAsConst;
+ m_ValObject[Name] = val;
+
+ if(m_Type!=VAL_NATIVE) m_Type = VAL_OBJECT;
+ */
+ }
+
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::PropExists(char *Name) {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->PropExists(Name);
+ m_ValIter = m_ValObject.find(Name);
+
+ return (m_ValIter != m_ValObject.end());
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::DeleteProps() {
+ m_ValIter = m_ValObject.begin();
+ while (m_ValIter != m_ValObject.end()) {
+ delete(CScValue *)m_ValIter->second;
+ m_ValIter++;
+ }
+ m_ValObject.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::CleanProps(bool IncludingNatives) {
+ m_ValIter = m_ValObject.begin();
+ while (m_ValIter != m_ValObject.end()) {
+ if (!m_ValIter->second->m_IsConstVar && (!m_ValIter->second->IsNative() || IncludingNatives)) m_ValIter->second->SetNULL();
+ m_ValIter++;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::IsNULL() {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->IsNULL();
+
+ return (m_Type == VAL_NULL);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::IsNative() {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->IsNative();
+
+ return (m_Type == VAL_NATIVE);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::IsString() {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->IsString();
+
+ return (m_Type == VAL_STRING);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::IsFloat() {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->IsFloat();
+
+ return (m_Type == VAL_FLOAT);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::IsInt() {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->IsInt();
+
+ return (m_Type == VAL_INT);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::IsBool() {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->IsBool();
+
+ return (m_Type == VAL_BOOL);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::IsObject() {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->IsObject();
+
+ return (m_Type == VAL_OBJECT);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+TValType CScValue::GetTypeTolerant() {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->GetType();
+
+ return m_Type;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::SetBool(bool Val) {
+ if (m_Type == VAL_VARIABLE_REF) {
+ m_ValRef->SetBool(Val);
+ return;
+ }
+
+ if (m_Type == VAL_NATIVE) {
+ m_ValNative->ScSetBool(Val);
+ return;
+ }
+
+ m_ValBool = Val;
+ m_Type = VAL_BOOL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::SetInt(int Val) {
+ if (m_Type == VAL_VARIABLE_REF) {
+ m_ValRef->SetInt(Val);
+ return;
+ }
+
+ if (m_Type == VAL_NATIVE) {
+ m_ValNative->ScSetInt(Val);
+ return;
+ }
+
+ m_ValInt = Val;
+ m_Type = VAL_INT;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::SetFloat(double Val) {
+ if (m_Type == VAL_VARIABLE_REF) {
+ m_ValRef->SetFloat(Val);
+ return;
+ }
+
+ if (m_Type == VAL_NATIVE) {
+ m_ValNative->ScSetFloat(Val);
+ return;
+ }
+
+ m_ValFloat = Val;
+ m_Type = VAL_FLOAT;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::SetString(const char *Val) {
+ if (m_Type == VAL_VARIABLE_REF) {
+ m_ValRef->SetString(Val);
+ return;
+ }
+
+ if (m_Type == VAL_NATIVE) {
+ m_ValNative->ScSetString(Val);
+ return;
+ }
+
+ SetStringVal(Val);
+ if (m_ValString) m_Type = VAL_STRING;
+ else m_Type = VAL_NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::SetStringVal(const char *Val) {
+ if (m_ValString) delete [] m_ValString;
+
+ if (Val == NULL) {
+ m_ValString = NULL;
+ return;
+ }
+
+ m_ValString = new char [strlen(Val) + 1];
+ if (m_ValString) {
+ strcpy(m_ValString, Val);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::SetNULL() {
+ if (m_Type == VAL_VARIABLE_REF) {
+ m_ValRef->SetNULL();
+ return;
+ }
+
+ if (m_ValNative && !m_Persistent) {
+ m_ValNative->m_RefCount--;
+ if (m_ValNative->m_RefCount <= 0) delete m_ValNative;
+ }
+ m_ValNative = NULL;
+ DeleteProps();
+
+ m_Type = VAL_NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::SetNative(CBScriptable *Val, bool Persistent) {
+ if (m_Type == VAL_VARIABLE_REF) {
+ m_ValRef->SetNative(Val, Persistent);
+ return;
+ }
+
+ if (Val == NULL) {
+ SetNULL();
+ } else {
+ if (m_ValNative && !m_Persistent) {
+ m_ValNative->m_RefCount--;
+ if (m_ValNative->m_RefCount <= 0) {
+ if (m_ValNative != Val) delete m_ValNative;
+ m_ValNative = NULL;
+ }
+ }
+
+ m_Type = VAL_NATIVE;
+ m_Persistent = Persistent;
+
+ m_ValNative = Val;
+ if (m_ValNative && !m_Persistent) m_ValNative->m_RefCount++;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::SetObject() {
+ if (m_Type == VAL_VARIABLE_REF) {
+ m_ValRef->SetObject();
+ return;
+ }
+
+ DeleteProps();
+ m_Type = VAL_OBJECT;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::SetReference(CScValue *Val) {
+ m_ValRef = Val;
+ m_Type = VAL_VARIABLE_REF;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::GetBool(bool Default) {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->GetBool();
+
+ switch (m_Type) {
+ case VAL_BOOL:
+ return m_ValBool;
+
+ case VAL_NATIVE:
+ return m_ValNative->ScToBool();
+
+ case VAL_INT:
+ return (m_ValInt != 0);
+
+ case VAL_FLOAT:
+ return (m_ValFloat != 0.0f);
+
+ case VAL_STRING:
+ return (scumm_stricmp(m_ValString, "1") == 0 || scumm_stricmp(m_ValString, "yes") == 0 || scumm_stricmp(m_ValString, "true") == 0);
+
+ default:
+ return Default;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CScValue::GetInt(int Default) {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->GetInt();
+
+ switch (m_Type) {
+ case VAL_BOOL:
+ return m_ValBool ? 1 : 0;
+
+ case VAL_NATIVE:
+ return m_ValNative->ScToInt();
+
+ case VAL_INT:
+ return m_ValInt;
+
+ case VAL_FLOAT:
+ return (int)m_ValFloat;
+
+ case VAL_STRING:
+ return atoi(m_ValString);
+
+ default:
+ return Default;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+double CScValue::GetFloat(double Default) {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->GetFloat();
+
+ switch (m_Type) {
+ case VAL_BOOL:
+ return m_ValBool ? 1.0f : 0.0f;
+
+ case VAL_NATIVE:
+ return m_ValNative->ScToFloat();
+
+ case VAL_INT:
+ return (double)m_ValInt;
+
+ case VAL_FLOAT:
+ return m_ValFloat;
+
+ case VAL_STRING:
+ return atof(m_ValString);
+
+ default:
+ return Default;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+void *CScValue::GetMemBuffer() {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->GetMemBuffer();
+
+ if (m_Type == VAL_NATIVE) return m_ValNative->ScToMemBuffer();
+ else return (void *)NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *CScValue::GetString() {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->GetString();
+
+ switch (m_Type) {
+ case VAL_OBJECT:
+ SetStringVal("[object]");
+ break;
+
+ case VAL_NULL:
+ SetStringVal("[null]");
+ break;
+
+ case VAL_NATIVE: {
+ char *StrVal = m_ValNative->ScToString();
+ SetStringVal(StrVal);
+ return StrVal;
+ break;
+ }
+
+ case VAL_BOOL:
+ SetStringVal(m_ValBool ? "yes" : "no");
+ break;
+
+ case VAL_INT: {
+ char dummy[50];
+ sprintf(dummy, "%d", m_ValInt);
+ SetStringVal(dummy);
+ break;
+ }
+
+ case VAL_FLOAT: {
+ char dummy[50];
+ sprintf(dummy, "%f", m_ValFloat);
+ SetStringVal(dummy);
+ break;
+ }
+
+ case VAL_STRING:
+ break;
+
+ default:
+ SetStringVal("");
+ }
+
+ return m_ValString;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+CBScriptable *CScValue::GetNative() {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->GetNative();
+
+ if (m_Type == VAL_NATIVE) return m_ValNative;
+ else return NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+TValType CScValue::GetType() {
+ return m_Type;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::Copy(CScValue *orig, bool CopyWhole) {
+ Game = orig->Game;
+
+ if (m_ValNative && !m_Persistent) {
+ m_ValNative->m_RefCount--;
+ if (m_ValNative->m_RefCount <= 0) {
+ if (m_ValNative != orig->m_ValNative) delete m_ValNative;
+ m_ValNative = NULL;
+ }
+ }
+
+ if (orig->m_Type == VAL_VARIABLE_REF && orig->m_ValRef && CopyWhole) orig = orig->m_ValRef;
+
+ Cleanup(true);
+
+ m_Type = orig->m_Type;
+ m_ValBool = orig->m_ValBool;
+ m_ValInt = orig->m_ValInt;
+ m_ValFloat = orig->m_ValFloat;
+ SetStringVal(orig->m_ValString);
+
+ m_ValRef = orig->m_ValRef;
+ m_Persistent = orig->m_Persistent;
+
+ m_ValNative = orig->m_ValNative;
+ if (m_ValNative && !m_Persistent) m_ValNative->m_RefCount++;
+//!!!! ref->native++
+
+ // copy properties
+ if (orig->m_Type == VAL_OBJECT && orig->m_ValObject.size() > 0) {
+ orig->m_ValIter = orig->m_ValObject.begin();
+ while (orig->m_ValIter != orig->m_ValObject.end()) {
+ m_ValObject[orig->m_ValIter->first] = new CScValue(Game);
+ m_ValObject[orig->m_ValIter->first]->Copy(orig->m_ValIter->second);
+ orig->m_ValIter++;
+ }
+ } else m_ValObject.clear();
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CScValue::SetValue(CScValue *Val) {
+ if (Val->m_Type == VAL_VARIABLE_REF) {
+ SetValue(Val->m_ValRef);
+ return;
+ }
+
+ // if being assigned a simple type, preserve native state
+ if (m_Type == VAL_NATIVE && (Val->m_Type == VAL_INT || Val->m_Type == VAL_STRING || Val->m_Type == VAL_BOOL)) {
+ switch (Val->m_Type) {
+ case VAL_INT:
+ m_ValNative->ScSetInt(Val->GetInt());
+ break;
+ case VAL_FLOAT:
+ m_ValNative->ScSetFloat(Val->GetFloat());
+ break;
+ case VAL_BOOL:
+ m_ValNative->ScSetBool(Val->GetBool());
+ break;
+ case VAL_STRING:
+ m_ValNative->ScSetString(Val->GetString());
+ break;
+ }
+ }
+ // otherwise just copy everything
+ else Copy(Val);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+HRESULT CScValue::Persist(CBPersistMgr *PersistMgr) {
+ PersistMgr->Transfer(TMEMBER(Game));
+
+ PersistMgr->Transfer(TMEMBER(m_Persistent));
+ PersistMgr->Transfer(TMEMBER(m_IsConstVar));
+ PersistMgr->Transfer(TMEMBER_INT(m_Type));
+ PersistMgr->Transfer(TMEMBER(m_ValBool));
+ PersistMgr->Transfer(TMEMBER(m_ValFloat));
+ PersistMgr->Transfer(TMEMBER(m_ValInt));
+ PersistMgr->Transfer(TMEMBER(m_ValNative));
+
+ int size;
+ char *str;
+ if (PersistMgr->m_Saving) {
+ size = m_ValObject.size();
+ PersistMgr->Transfer("", &size);
+ m_ValIter = m_ValObject.begin();
+ while (m_ValIter != m_ValObject.end()) {
+ str = (char *)m_ValIter->first.c_str();
+ PersistMgr->Transfer("", &str);
+ PersistMgr->Transfer("", &m_ValIter->second);
+
+ m_ValIter++;
+ }
+ } else {
+ CScValue *val;
+ PersistMgr->Transfer("", &size);
+ for (int i = 0; i < size; i++) {
+ PersistMgr->Transfer("", &str);
+ PersistMgr->Transfer("", &val);
+
+ m_ValObject[str] = val;
+ delete [] str;
+ }
+ }
+
+ PersistMgr->Transfer(TMEMBER(m_ValRef));
+ PersistMgr->Transfer(TMEMBER(m_ValString));
+
+ /*
+ FILE* f = fopen("c:\\val.log", "a+");
+ switch(m_Type)
+ {
+ case VAL_STRING:
+ fprintf(f, "str %s\n", m_ValString);
+ break;
+
+ case VAL_INT:
+ fprintf(f, "int %d\n", m_ValInt);
+ break;
+
+ case VAL_BOOL:
+ fprintf(f, "bool %d\n", m_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) {
+ m_ValIter = m_ValObject.begin();
+ while (m_ValIter != m_ValObject.end()) {
+ Buffer->PutTextIndent(Indent, "PROPERTY {\n");
+ Buffer->PutTextIndent(Indent + 2, "NAME=\"%s\"\n", (char *)m_ValIter->first.c_str());
+ Buffer->PutTextIndent(Indent + 2, "VALUE=\"%s\"\n", m_ValIter->second->GetString());
+ Buffer->PutTextIndent(Indent, "}\n\n");
+
+ m_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) {
+ m_ValIter = m_ValObject.begin();
+ while (m_ValIter != m_ValObject.end()) {
+ Client->OnVariableInit(Type, Script, ScopeID, m_ValIter->second, m_ValIter->first.c_str());
+ m_ValIter++;
+ }
+ return S_OK;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::SetProperty(const char *PropName, int Value) {
+ CScValue *Val = new CScValue(Game, Value);
+ bool Ret = SUCCEEDED(SetProp((char *)PropName, Val));
+ delete Val;
+ return Ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::SetProperty(const char *PropName, const char *Value) {
+ CScValue *Val = new CScValue(Game, (char *)Value);
+ bool Ret = SUCCEEDED(SetProp((char *)PropName, Val));
+ delete Val;
+ return Ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::SetProperty(const char *PropName, double Value) {
+ CScValue *Val = new CScValue(Game, Value);
+ bool Ret = SUCCEEDED(SetProp((char *)PropName, Val));
+ delete Val;
+ return Ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::SetProperty(const char *PropName, bool Value) {
+ CScValue *Val = new CScValue(Game, Value);
+ bool Ret = SUCCEEDED(SetProp((char *)PropName, Val));
+ delete Val;
+ return Ret;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::SetProperty(const char *PropName) {
+ CScValue *Val = new CScValue(Game);
+ bool Ret = SUCCEEDED(SetProp((char *)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((char *)Value);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::DbgSetVal() {
+ SetNULL();
+ return true;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int CScValue::DbgGetNumProperties() {
+ if (m_ValNative && m_ValNative->m_ScProp) return m_ValNative->m_ScProp->DbgGetNumProperties();
+ else return m_ValObject.size();
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::DbgGetProperty(int Index, const char **Name, IWmeDebugProp **Value) {
+ if (m_ValNative && m_ValNative->m_ScProp) return m_ValNative->m_ScProp->DbgGetProperty(Index, Name, Value);
+ else {
+ int Count = 0;
+ m_ValIter = m_ValObject.begin();
+ while (m_ValIter != m_ValObject.end()) {
+ if (Count == Index) {
+ *Name = m_ValIter->first.c_str();
+ *Value = m_ValIter->second;
+ return true;
+ }
+ m_ValIter++;
+ Count++;
+ }
+ return false;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CScValue::DbgGetDescription(char *Buf, int BufSize) {
+ if (m_Type == VAL_VARIABLE_REF) return m_ValRef->DbgGetDescription(Buf, BufSize);
+
+ if (m_Type == VAL_NATIVE) {
+ m_ValNative->ScDebuggerDesc(Buf, BufSize);
+ } else {
+ strncpy(Buf, GetString(), BufSize);
+ }
+ return true;
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/scriptables/ScValue.h b/engines/wintermute/scriptables/ScValue.h
new file mode 100644
index 0000000000..40abe635ce
--- /dev/null
+++ b/engines/wintermute/scriptables/ScValue.h
@@ -0,0 +1,140 @@
+/* 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/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 m_IsConstVar;
+ HRESULT SaveAsText(CBDynBuffer *Buffer, int Indent);
+ void SetValue(CScValue *Val);
+ bool m_Persistent;
+ bool PropExists(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);
+ char *GetString();
+ void *GetMemBuffer();
+ CBScriptable *GetNative();
+ HRESULT DeleteProp(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(char *Name, CScValue *Val, bool CopyWhole = false, bool SetAsConst = false);
+ CScValue *GetProp(char *Name);
+ CBScriptable *m_ValNative;
+ CScValue *m_ValRef;
+ bool m_ValBool;
+ int m_ValInt;
+ double m_ValFloat;
+ char *m_ValString;
+ TValType m_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();
+ std::map<std::string, CScValue *> m_ValObject;
+ std::map<std::string, CScValue *>::iterator m_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/utils.cpp b/engines/wintermute/utils.cpp
new file mode 100644
index 0000000000..dd649d8afd
--- /dev/null
+++ b/engines/wintermute/utils.cpp
@@ -0,0 +1,340 @@
+/*
+This file is part of WME Lite.
+http://dead-code.org/redir.php?target=wmelite
+
+Copyright (c) 2011 Jan Nedoma
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include "dcgf.h"
+#include "utils.h"
+#include "PlatformSDL.h"
+#include "wintypes.h"
+#include "PathUtil.h"
+#include "BGame.h"
+#include "common/str.h"
+#include "common/textconsole.h"
+
+namespace WinterMute {
+
+//////////////////////////////////////////////////////////////////////
+static inline unsigned Sqr(int x) {
+ return (x * x);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////
+void CBUtils::Clip(int *DestX, int *DestY, RECT *SrcRect, RECT *DestRect) {
+ // If it's partly off the right side of the screen
+ if (*DestX + (SrcRect->right - SrcRect->left) > DestRect->right)
+ SrcRect->right -= *DestX + (SrcRect->right - SrcRect->left) - DestRect->right;
+
+ if (SrcRect->right < 0) SrcRect->right = 0;
+
+ // Partly off the left side of the screen
+ if (*DestX < DestRect->left) {
+ SrcRect->left += DestRect->left - *DestX;
+ *DestX = DestRect->left;
+ }
+
+ // Partly off the top of the screen
+ if (*DestY < DestRect->top) {
+ SrcRect->top += DestRect->top - *DestY;
+ *DestY = DestRect->top;
+ }
+
+ // If it's partly off the bottom side of the screen
+ if (*DestY + (SrcRect->bottom - SrcRect->top) > DestRect->bottom)
+ SrcRect->bottom -= ((SrcRect->bottom - SrcRect->top) + *DestY) - DestRect->bottom;
+
+ if (SrcRect->bottom < 0) SrcRect->bottom = 0;
+
+ return;
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+// Swap - swaps two integers
+//////////////////////////////////////////////////////////////////////////////////
+void CBUtils::Swap(int *a, int *b) {
+ int Temp = *a;
+ *a = *b;
+ *b = Temp;
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBUtils::StrBeginsI(const char *String, const char *Fragment) {
+ return (scumm_strnicmp(String, Fragment, strlen(Fragment)) == 0);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+float CBUtils::NormalizeAngle(float Angle) {
+ while (Angle > 360) Angle -= 360;
+ while (Angle < 0) Angle += 360;
+
+ return Angle;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+void CBUtils::CreatePath(const char *Path, bool PathOnly) {
+ AnsiString path;
+
+ if (!PathOnly) path = PathUtil::GetDirectoryName(Path);
+ else path = Path;
+
+// try {
+ warning("CBUtils::CreatePath - not implemented: %s", Path);
+// boost::filesystem::create_directories(path);
+// } catch (...) {
+ return;
+// }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+void CBUtils::DebugMessage(HWND hWnd, const char *Text) {
+ //MessageBox(hWnd, Text, "WME", MB_OK|MB_ICONINFORMATION);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *CBUtils::SetString(char **String, const char *Value) {
+ delete[] *String;
+ *String = new char[strlen(Value) + 1];
+ if (*String) strcpy(*String, Value);
+ return *String;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int CBUtils::StrNumEntries(const char *Str, const char Delim) {
+ int NumEntries = 1;
+ for (int i = 0; i < strlen(Str); i++) {
+ if (Str[i] == Delim) NumEntries++;
+ }
+ return NumEntries;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+char *CBUtils::StrEntry(int Entry, const char *Str, const char Delim) {
+ int NumEntries = 0;
+
+ const char *Start = NULL;
+ int Len = 0;
+
+ for (int i = 0; i <= strlen(Str); i++) {
+ if (NumEntries == Entry) {
+ if (!Start) Start = Str + i;
+ else Len++;
+ }
+ if (Str[i] == Delim || Str[i] == '\0') {
+ NumEntries++;
+ if (Start) {
+ char *Ret = new char[Len + 1];
+ memset(Ret, 0, Len + 1);
+ strncpy(Ret, Start, Len);
+ return Ret;
+ }
+ }
+ }
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////////////////////
+int CBUtils::RandomInt(int From, int To) {
+ if (To < From) {
+ int i = To;
+ To = From;
+ From = i;
+ }
+ return (rand() % (To - From + 1)) + From;
+}
+
+//////////////////////////////////////////////////////////////////////////
+float CBUtils::RandomFloat(float From, float To) {
+ float RandNum = (float)rand() / (float)RAND_MAX;
+ return From + (To - From) * RandNum;
+}
+
+//////////////////////////////////////////////////////////////////////////
+float CBUtils::RandomAngle(float From, float To) {
+ while (To < From) {
+ To += 360;
+ }
+ return NormalizeAngle(RandomFloat(From, To));
+}
+
+//////////////////////////////////////////////////////////////////////////
+bool CBUtils::MatchesPattern(const char *Pattern, const char *String) {
+ char stringc, patternc;
+
+ for (;; ++String) {
+ stringc = toupper(*String);
+ patternc = toupper(*Pattern++);
+
+ switch (patternc) {
+ case 0:
+ return (stringc == 0);
+
+ case '?':
+ if (stringc == 0) return false;
+ break;
+
+ case '*':
+ if (!*Pattern) return true;
+
+ if (*Pattern == '.') {
+ char *dot;
+ if (Pattern[1] == '*' && Pattern[2] == 0) return true;
+ dot = (char *)strchr(String, '.');
+ if (Pattern[1] == 0) return (dot == NULL || dot[1] == 0);
+ if (dot != NULL) {
+ String = dot;
+ if (strpbrk(Pattern, "*?[") == NULL && strchr(String + 1, '.') == NULL)
+ return(scumm_stricmp(Pattern + 1, String + 1) == 0);
+ }
+ }
+
+ while (*String)
+ if (CBUtils::MatchesPattern(Pattern, String++))
+ return true;
+ return false;
+
+ default:
+ if (patternc != stringc)
+ if (patternc == '.' && stringc == 0)
+ return(CBUtils::MatchesPattern(Pattern, String));
+ else
+ return false;
+ break;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+char *CBUtils::GetPath(char *Filename) {
+ AnsiString path = PathUtil::GetDirectoryName(Filename);
+ //path = boost::filesystem::system_complete(path).string();
+ warning("CBUtils::GetPath: (%s), not implemented", Filename);
+ return Filename;
+ char *ret = new char[path.length() + 1];
+ strcpy(ret, path.c_str());
+
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+char *CBUtils::GetFilename(char *Filename) {
+ AnsiString path = PathUtil::GetFileName(Filename);
+ char *ret = new char[path.length() + 1];
+ strcpy(ret, path.c_str());
+ return ret;
+}
+
+//////////////////////////////////////////////////////////////////////////
+void CBUtils::RGBtoHSL(uint32 RGBColor, byte *OutH, byte *OutS, byte *OutL) {
+ float var_R = (D3DCOLGetR(RGBColor) / 255.0f);
+ float var_G = (D3DCOLGetG(RGBColor) / 255.0f);
+ float var_B = (D3DCOLGetB(RGBColor) / 255.0f);
+
+ //Min. value of RGB
+ float var_Min = std::min(var_R, var_G);
+ var_Min = std::min(var_Min, var_B);
+
+ //Max. value of RGB
+ float var_Max = std::max(var_R, var_G);
+ var_Max = std::max(var_Max, var_B);
+
+ //Delta RGB value
+ float del_Max = var_Max - var_Min;
+
+ float H, S, L;
+
+ L = (var_Max + var_Min) / 2.0f;
+
+ //This is a gray, no chroma...
+ if (del_Max == 0) {
+ H = 0;
+ S = 0;
+ }
+ //Chromatic data...
+ else {
+ if (L < 0.5f) S = del_Max / (var_Max + var_Min);
+ else S = del_Max / (2.0f - var_Max - var_Min);
+
+ float del_R = (((var_Max - var_R) / 6.0f) + (del_Max / 2.0f)) / del_Max;
+ float del_G = (((var_Max - var_G) / 6.0f) + (del_Max / 2.0f)) / del_Max;
+ float del_B = (((var_Max - var_B) / 6.0f) + (del_Max / 2.0f)) / del_Max;
+
+ if (var_R == var_Max) H = del_B - del_G;
+ else if (var_G == var_Max) H = (1.0f / 3.0f) + del_R - del_B;
+ else if (var_B == var_Max) H = (2.0f / 3.0f) + del_G - del_R;
+
+ if (H < 0) H += 1;
+ if (H > 1) H -= 1;
+ }
+
+ *OutH = H * 255;
+ *OutS = S * 255;
+ *OutL = L * 255;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+uint32 CBUtils::HSLtoRGB(byte InH, byte InS, byte InL) {
+ float H = InH / 255.0f;
+ float S = InS / 255.0f;
+ float L = InL / 255.0f;
+
+ byte R, G, B;
+
+
+ if (S == 0) {
+ R = L * 255;
+ G = L * 255;
+ B = L * 255;
+ } else {
+ float var_1, var_2;
+
+ if (L < 0.5) var_2 = L * (1.0 + S);
+ else var_2 = (L + S) - (S * L);
+
+ var_1 = 2.0f * L - var_2;
+
+ R = 255 * Hue2RGB(var_1, var_2, H + (1.0f / 3.0f));
+ G = 255 * Hue2RGB(var_1, var_2, H);
+ B = 255 * Hue2RGB(var_1, var_2, H - (1.0f / 3.0f));
+ }
+ return DRGBA(255, R, G, B);
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+float CBUtils::Hue2RGB(float v1, float v2, float vH) {
+ if (vH < 0.0f) vH += 1.0f;
+ if (vH > 1.0f) vH -= 1.0f;
+ if ((6.0f * vH) < 1.0f) return (v1 + (v2 - v1) * 6.0f * vH);
+ if ((2.0f * vH) < 1.0f) return (v2);
+ if ((3.0f * vH) < 2.0f) return (v1 + (v2 - v1) * ((2.0f / 3.0f) - vH) * 6.0f);
+ return (v1);
+}
+
+} // end of namespace WinterMute
diff --git a/engines/wintermute/utils.h b/engines/wintermute/utils.h
new file mode 100644
index 0000000000..56d9876c5c
--- /dev/null
+++ b/engines/wintermute/utils.h
@@ -0,0 +1,68 @@
+/*
+This file is part of WME Lite.
+http://dead-code.org/redir.php?target=wmelite
+
+Copyright (c) 2011 Jan Nedoma
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#ifndef __WmeUtils_H__
+#define __WmeUtils_H__
+
+#include "wintypes.h"
+
+namespace WinterMute {
+
+class CBGame;
+
+class CBUtils {
+public:
+ static void Clip(int *DestX, int *DestY, RECT *SrcRect, RECT *DestRect);
+ static void Swap(int *a, int *b);
+ static bool StrBeginsI(const char *String, const char *Fragment);
+ static float NormalizeAngle(float Angle);
+
+ static void CreatePath(const char *Path, bool PathOnly = false);
+
+ static void DebugMessage(HWND hWnd, const char *Text);
+ static char *SetString(char **String, const char *Value);
+
+ static int StrNumEntries(const char *Str, const char Delim = ',');
+ static char *StrEntry(int Entry, const char *Str, const char Delim = ',');
+
+ static int RandomInt(int From, int To);
+ static float RandomFloat(float From, float To);
+ static float RandomAngle(float From, float To);
+
+ static bool MatchesPattern(const char *pattern, const char *string);
+
+ static char *GetPath(char *Filename);
+ static char *GetFilename(char *Filename);
+
+ static void RGBtoHSL(uint32 RGBColor, byte *OutH, byte *OutS, byte *OutL);
+ static uint32 HSLtoRGB(byte H, byte S, byte L);
+
+private:
+ static float Hue2RGB(float v1, float v2, float vH);
+};
+
+} // end of namespace WinterMute
+
+#endif
diff --git a/engines/wintermute/wintypes.h b/engines/wintermute/wintypes.h
index 89c1ef6e53..50d6dd16be 100644
--- a/engines/wintermute/wintypes.h
+++ b/engines/wintermute/wintypes.h
@@ -1,27 +1,30 @@
+/* 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 part of WME Lite.
-http://dead-code.org/redir.php?target=wmelite
-
-Copyright (c) 2011 Jan Nedoma
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
+ * This file is based on WME Lite.
+ * http://dead-code.org/redir.php?target=wmelite
+ * Copyright (c) 2011 Jan Nedoma
+ */
#ifndef __WmeWintypes_H__
#define __WmeWintypes_H__
diff --git a/engines/wintermute/wme_debugger.h b/engines/wintermute/wme_debugger.h
new file mode 100644
index 0000000000..6774ab0768
--- /dev/null
+++ b/engines/wintermute/wme_debugger.h
@@ -0,0 +1,167 @@
+/* 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 WME_DEBUGGER_H
+#define WME_DEBUGGER_H
+
+#ifdef SetProp
+#undef SetProp
+#endif
+
+#include "engines/wintermute/dcscript.h"
+
+namespace WinterMute {
+
+class IWmeDebugClient;
+class IWmeDebugObject;
+
+//////////////////////////////////////////////////////////////////////////
+typedef enum {
+ WME_DBGPROP_NULL = 0,
+ WME_DBGPROP_INT = 1,
+ WME_DBGPROP_FLOAT = 2,
+ WME_DBGPROP_BOOL = 3,
+ WME_DBGPROP_STRING = 4,
+ WME_DBGPROP_OBJECT = 5,
+ WME_DBGPROP_NATIVE = 6,
+ WME_DBGPROP_UNKNOWN = 7
+}
+EWmeDebuggerPropType;
+
+//////////////////////////////////////////////////////////////////////////
+typedef enum {
+ WME_DBGVAR_GLOBAL = 0,
+ WME_DBGVAR_SCRIPT = 1,
+ WME_DBGVAR_SCOPE = 2
+}
+EWmeDebuggerVariableType;
+
+//////////////////////////////////////////////////////////////////////////
+class IWmeDebugScript {
+public:
+ virtual int DbgGetLine() = 0;
+ virtual const char *DbgGetFilename() = 0;
+ virtual TScriptState DbgGetState() = 0;
+
+ virtual int DbgGetNumBreakpoints() = 0;
+ virtual int DbgGetBreakpoint(int Index) = 0;
+
+ virtual bool DbgSetTracingMode(bool IsTracing) = 0;
+ virtual bool DbgGetTracingMode() = 0;
+};
+
+//////////////////////////////////////////////////////////////////////////
+class IWmeDebugProp {
+public:
+ virtual EWmeDebuggerPropType DbgGetType() = 0;
+
+ // getters
+ virtual int DbgGetValInt() = 0;
+ virtual double DbgGetValFloat() = 0;
+ virtual bool DbgGetValBool() = 0;
+ virtual const char *DbgGetValString() = 0;
+ virtual IWmeDebugObject *DbgGetValNative() = 0;
+
+ // setters
+ virtual bool DbgSetVal(int Value) = 0;
+ virtual bool DbgSetVal(double Value) = 0;
+ virtual bool DbgSetVal(bool Value) = 0;
+ virtual bool DbgSetVal(const char *Value) = 0;
+ virtual bool DbgSetVal() = 0;
+
+ // properties
+ virtual int DbgGetNumProperties() = 0;
+ virtual bool DbgGetProperty(int Index, const char **Name, IWmeDebugProp **Value) = 0;
+
+ virtual bool DbgGetDescription(char *Buf, int BufSize) = 0;
+};
+
+//////////////////////////////////////////////////////////////////////////
+class IWmeDebugObject {
+public:
+ virtual const char *DbgGetNativeClass() = 0;
+ virtual IWmeDebugProp *DbgGetProperty(const char *Name) = 0;
+};
+
+//////////////////////////////////////////////////////////////////////////
+class IWmeDebugClient {
+public:
+ virtual bool OnGameInit() = 0;
+ virtual bool OnGameShutdown() = 0;
+
+ virtual bool OnGameTick() = 0;
+
+ virtual bool OnLog(unsigned int ErrorCode, const char *Text) = 0;
+
+ virtual bool OnScriptInit(IWmeDebugScript *Script) = 0;
+ virtual bool OnScriptEventThreadInit(IWmeDebugScript *Script, IWmeDebugScript *ParentScript, const char *EventName) = 0;
+ virtual bool OnScriptMethodThreadInit(IWmeDebugScript *Script, IWmeDebugScript *ParentScript, const char *MethodName) = 0;
+ virtual bool OnScriptShutdown(IWmeDebugScript *Script) = 0;
+ virtual bool OnScriptChangeLine(IWmeDebugScript *Script, int Line) = 0;
+ virtual bool OnScriptChangeScope(IWmeDebugScript *Script, unsigned int ScopeID) = 0;
+ virtual bool OnScriptShutdownScope(IWmeDebugScript *Script, unsigned int ScopeID) = 0;
+
+ virtual bool OnVariableInit(EWmeDebuggerVariableType Type, IWmeDebugScript *Script, unsigned int ScopeID, IWmeDebugProp *Variable, const char *VariableName) = 0;
+ virtual bool OnVariableChangeValue(IWmeDebugProp *Variable, IWmeDebugProp *Value) = 0;
+
+ virtual bool OnScriptHitBreakpoint(IWmeDebugScript *Script, int Line) = 0;
+};
+
+//////////////////////////////////////////////////////////////////////////
+class IWmeDebugServer {
+public:
+ virtual bool AttachClient(IWmeDebugClient *Client) = 0;
+ virtual bool DetachClient(IWmeDebugClient *Client) = 0;
+
+ virtual bool QueryData(IWmeDebugClient *Client) = 0;
+
+ virtual int GetPropInt(const char *PropName) = 0;
+ virtual double GetPropFloat(const char *PropName) = 0;
+ virtual const char *GetPropString(const char *PropName) = 0;
+ virtual bool GetPropBool(const char *PropName) = 0;
+
+ virtual bool SetProp(const char *PropName, int PropValue) = 0;
+ virtual bool SetProp(const char *PropName, double PropValue) = 0;
+ virtual bool SetProp(const char *PropName, const char *PropValue) = 0;
+ virtual bool SetProp(const char *PropName, bool PropValue) = 0;
+
+ virtual bool ResolveFilename(const char *RelativeFilename, char *AbsFilenameBuf, int AbsBufSize) = 0;
+
+ virtual bool AddBreakpoint(const char *ScriptFilename, int Line) = 0;
+ virtual bool RemoveBreakpoint(const char *ScriptFilename, int Line) = 0;
+
+ virtual bool ContinueExecution() = 0;
+};
+
+
+typedef bool (*WMEDBG_INITIALIZE)(IWmeDebugServer *Server);
+typedef bool (*WMEDBG_SHUTDOWN)(IWmeDebugServer *Server);
+
+} // end of namespace WinterMute
+
+#endif // WME_DEBUGGER_H \ No newline at end of file