/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /* * This file is based on WME Lite. * http://dead-code.org/redir.php?target=wmelite * Copyright (c) 2011 Jan Nedoma */ #include "engines/wintermute/base/base_game.h" #include "engines/wintermute/base/scriptables/script_stack.h" #include "engines/wintermute/base/scriptables/script_value.h" #include "engines/wintermute/utils/utils.h" #include "engines/wintermute/base/scriptables/script_ext_string.h" #include "engines/wintermute/base/scriptables/script_ext_array.h" #include "engines/wintermute/utils/string_util.h" #include "common/tokenizer.h" namespace WinterMute { IMPLEMENT_PERSISTENT(SXString, false) BaseScriptable *makeSXString(BaseGame *inGame, ScStack *stack) { return new SXString(inGame, stack); } ////////////////////////////////////////////////////////////////////////// SXString::SXString(BaseGame *inGame, ScStack *stack): BaseScriptable(inGame) { _string = NULL; _capacity = 0; stack->correctParams(1); ScValue *val = stack->pop(); if (val->isInt()) { _capacity = MAX(0, val->getInt()); if (_capacity > 0) { _string = new char[_capacity]; memset(_string, 0, _capacity); } } else { setStringVal(val->getString()); } if (_capacity == 0) setStringVal(""); } ////////////////////////////////////////////////////////////////////////// SXString::~SXString() { if (_string) delete [] _string; } ////////////////////////////////////////////////////////////////////////// void SXString::setStringVal(const char *val) { int len = strlen(val); if (len >= _capacity) { _capacity = len + 1; delete[] _string; _string = NULL; _string = new char[_capacity]; memset(_string, 0, _capacity); } strcpy(_string, val); } ////////////////////////////////////////////////////////////////////////// const char *SXString::scToString() { if (_string) return _string; else return "[null string]"; } ////////////////////////////////////////////////////////////////////////// void SXString::scSetString(const char *val) { setStringVal(val); } ////////////////////////////////////////////////////////////////////////// bool SXString::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) { ////////////////////////////////////////////////////////////////////////// // Substring ////////////////////////////////////////////////////////////////////////// if (strcmp(name, "Substring") == 0) { stack->correctParams(2); int start = stack->pop()->getInt(); int end = stack->pop()->getInt(); if (end < start) BaseUtils::swap(&start, &end); //try { WideString str; if (_gameRef->_textEncoding == TEXT_UTF8) str = StringUtil::utf8ToWide(_string); else str = StringUtil::ansiToWide(_string); //WideString subStr = str.substr(start, end - start + 1); WideString subStr(str.c_str() + start, end - start + 1); if (_gameRef->_textEncoding == TEXT_UTF8) stack->pushString(StringUtil::wideToUtf8(subStr).c_str()); else stack->pushString(StringUtil::wideToAnsi(subStr).c_str()); // } catch (std::exception &) { // stack->pushNULL(); // } return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // Substr ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "Substr") == 0) { stack->correctParams(2); int start = stack->pop()->getInt(); ScValue *val = stack->pop(); int len = val->getInt(); if (!val->isNULL() && len <= 0) { stack->pushString(""); return STATUS_OK; } if (val->isNULL()) len = strlen(_string) - start; // try { WideString str; if (_gameRef->_textEncoding == TEXT_UTF8) str = StringUtil::utf8ToWide(_string); else str = StringUtil::ansiToWide(_string); // WideString subStr = str.substr(start, len); WideString subStr(str.c_str() + start, len); if (_gameRef->_textEncoding == TEXT_UTF8) stack->pushString(StringUtil::wideToUtf8(subStr).c_str()); else stack->pushString(StringUtil::wideToAnsi(subStr).c_str()); // } catch (std::exception &) { // stack->pushNULL(); // } return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // ToUpperCase ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "ToUpperCase") == 0) { stack->correctParams(0); WideString str; if (_gameRef->_textEncoding == TEXT_UTF8) str = StringUtil::utf8ToWide(_string); else str = StringUtil::ansiToWide(_string); str.toUppercase(); if (_gameRef->_textEncoding == TEXT_UTF8) stack->pushString(StringUtil::wideToUtf8(str).c_str()); else stack->pushString(StringUtil::wideToAnsi(str).c_str()); return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // ToLowerCase ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "ToLowerCase") == 0) { stack->correctParams(0); WideString str; if (_gameRef->_textEncoding == TEXT_UTF8) str = StringUtil::utf8ToWide(_string); else str = StringUtil::ansiToWide(_string); str.toLowercase(); if (_gameRef->_textEncoding == TEXT_UTF8) stack->pushString(StringUtil::wideToUtf8(str).c_str()); else stack->pushString(StringUtil::wideToAnsi(str).c_str()); return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // IndexOf ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "IndexOf") == 0) { stack->correctParams(2); const char *strToFind = stack->pop()->getString(); int index = stack->pop()->getInt(); WideString str; if (_gameRef->_textEncoding == TEXT_UTF8) str = StringUtil::utf8ToWide(_string); else str = StringUtil::ansiToWide(_string); WideString toFind; if (_gameRef->_textEncoding == TEXT_UTF8) toFind = StringUtil::utf8ToWide(strToFind); else toFind = StringUtil::ansiToWide(strToFind); int indexOf = StringUtil::indexOf(str, toFind, index); stack->pushInt(indexOf); return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// // Split ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "Split") == 0) { stack->correctParams(1); ScValue *val = stack->pop(); char separators[MAX_PATH_LENGTH] = ","; if (!val->isNULL()) strcpy(separators, val->getString()); SXArray *array = new SXArray(_gameRef); if (!array) { stack->pushNULL(); return STATUS_OK; } WideString str; if (_gameRef->_textEncoding == TEXT_UTF8) str = StringUtil::utf8ToWide(_string); else str = StringUtil::ansiToWide(_string); WideString delims; if (_gameRef->_textEncoding == TEXT_UTF8) delims = StringUtil::utf8ToWide(separators); else delims = StringUtil::ansiToWide(separators); Common::Array parts; Common::StringTokenizer tokenizer(str, delims); while (!tokenizer.empty()) { Common::String str2 = tokenizer.nextToken(); parts.push_back(str2); } // TODO: Clean this up /*do { pos = StringUtil::IndexOf(Common::String(str.c_str() + start), delims, start); //pos = str.find_first_of(delims, start); if (pos == start) { start = pos + 1; } else if (pos == str.size()) { parts.push_back(Common::String(str.c_str() + start)); break; } else { parts.push_back(Common::String(str.c_str() + start, pos - start)); start = pos + 1; } //start = str.find_first_not_of(delims, start); start = StringUtil::LastIndexOf(Common::String(str.c_str() + start), delims, start) + 1; } while (pos != str.size());*/ for (Common::Array::iterator it = parts.begin(); it != parts.end(); ++it) { WideString &part = (*it); if (_gameRef->_textEncoding == TEXT_UTF8) val = new ScValue(_gameRef, StringUtil::wideToUtf8(part).c_str()); else val = new ScValue(_gameRef, StringUtil::wideToAnsi(part).c_str()); array->push(val); delete val; val = NULL; } stack->pushNative(array, false); return STATUS_OK; } else return STATUS_FAILED; } ////////////////////////////////////////////////////////////////////////// ScValue *SXString::scGetProperty(const char *name) { _scValue->setNULL(); ////////////////////////////////////////////////////////////////////////// // Type (RO) ////////////////////////////////////////////////////////////////////////// if (strcmp(name, "Type") == 0) { _scValue->setString("string"); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Length (RO) ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "Length") == 0) { if (_gameRef->_textEncoding == TEXT_UTF8) { WideString wstr = StringUtil::utf8ToWide(_string); _scValue->setInt(wstr.size()); } else _scValue->setInt(strlen(_string)); return _scValue; } ////////////////////////////////////////////////////////////////////////// // Capacity ////////////////////////////////////////////////////////////////////////// else if (strcmp(name, "Capacity") == 0) { _scValue->setInt(_capacity); return _scValue; } else return _scValue; } ////////////////////////////////////////////////////////////////////////// bool SXString::scSetProperty(const char *name, ScValue *value) { ////////////////////////////////////////////////////////////////////////// // Capacity ////////////////////////////////////////////////////////////////////////// if (strcmp(name, "Capacity") == 0) { int32 newCap = (uint32)value->getInt(); if (newCap < (int32)(strlen(_string) + 1)) _gameRef->LOG(0, "Warning: cannot lower string capacity"); else if (newCap != _capacity) { char *newStr = new char[newCap]; if (newStr) { memset(newStr, 0, newCap); strcpy(newStr, _string); delete[] _string; _string = newStr; _capacity = newCap; } } return STATUS_OK; } else return STATUS_FAILED; } ////////////////////////////////////////////////////////////////////////// bool SXString::persist(BasePersistenceManager *persistMgr) { BaseScriptable::persist(persistMgr); persistMgr->transfer(TMEMBER(_capacity)); if (persistMgr->_saving) { if (_capacity > 0) persistMgr->putBytes((byte *)_string, _capacity); } else { if (_capacity > 0) { _string = new char[_capacity]; persistMgr->getBytes((byte *)_string, _capacity); } else _string = NULL; } return STATUS_OK; } ////////////////////////////////////////////////////////////////////////// int SXString::scCompare(BaseScriptable *val) { return strcmp(_string, ((SXString *)val)->_string); } } // end of namespace WinterMute