1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 /*
24  * This file is based on WME Lite.
25  * http://dead-code.org/redir.php?target=wmelite
26  * Copyright (c) 2011 Jan Nedoma
27  */
28 
29 #include "engines/wintermute/base/base_game.h"
30 #include "engines/wintermute/base/scriptables/script_stack.h"
31 #include "engines/wintermute/base/scriptables/script_value.h"
32 #include "engines/wintermute/utils/utils.h"
33 #include "engines/wintermute/base/scriptables/script_ext_string.h"
34 #include "engines/wintermute/base/scriptables/script_ext_array.h"
35 #include "engines/wintermute/utils/string_util.h"
36 #include "common/str.h"
37 #include "common/tokenizer.h"
38 
39 namespace Wintermute {
40 
IMPLEMENT_PERSISTENT(SXString,false)41 IMPLEMENT_PERSISTENT(SXString, false)
42 
43 BaseScriptable *makeSXString(BaseGame *inGame, ScStack *stack) {
44 	return new SXString(inGame, stack);
45 }
46 
47 //////////////////////////////////////////////////////////////////////////
SXString(BaseGame * inGame,ScStack * stack)48 SXString::SXString(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) {
49 	_string = nullptr;
50 	_capacity = 0;
51 
52 	stack->correctParams(1);
53 	ScValue *val = stack->pop();
54 
55 	if (val->isInt()) {
56 		_capacity = MAX(0, val->getInt());
57 		if (_capacity > 0) {
58 			_string = new char[_capacity];
59 			memset(_string, 0, _capacity);
60 		}
61 	} else {
62 		setStringVal(val->getString());
63 	}
64 
65 	if (_capacity == 0) {
66 		setStringVal("");
67 	}
68 }
69 
70 
71 //////////////////////////////////////////////////////////////////////////
~SXString()72 SXString::~SXString() {
73 	if (_string) {
74 		delete[] _string;
75 	}
76 }
77 
78 
79 //////////////////////////////////////////////////////////////////////////
setStringVal(const char * val)80 void SXString::setStringVal(const char *val) {
81 	int len = strlen(val);
82 	if (len >= _capacity) {
83 		_capacity = len + 1;
84 		delete[] _string;
85 		_string = nullptr;
86 		_string = new char[_capacity];
87 		memset(_string, 0, _capacity);
88 	}
89 	strcpy(_string, val);
90 }
91 
92 
93 //////////////////////////////////////////////////////////////////////////
scToString()94 const char *SXString::scToString() {
95 	if (_string) {
96 		return _string;
97 	} else {
98 		return "[null string]";
99 	}
100 }
101 
102 
103 //////////////////////////////////////////////////////////////////////////
scSetString(const char * val)104 void SXString::scSetString(const char *val) {
105 	setStringVal(val);
106 }
107 
108 
109 //////////////////////////////////////////////////////////////////////////
scCallMethod(ScScript * script,ScStack * stack,ScStack * thisStack,const char * name)110 bool SXString::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
111 	//////////////////////////////////////////////////////////////////////////
112 	// Substring
113 	//////////////////////////////////////////////////////////////////////////
114 	if (strcmp(name, "Substring") == 0) {
115 		stack->correctParams(2);
116 		int start = stack->pop()->getInt();
117 		int end   = stack->pop()->getInt();
118 
119 		if (end < start) {
120 			BaseUtils::swap(&start, &end);
121 		}
122 
123 		//try {
124 		WideString str;
125 		if (_gameRef->_textEncoding == TEXT_UTF8) {
126 			str = StringUtil::utf8ToWide(_string);
127 		} else {
128 			str = StringUtil::ansiToWide(_string);
129 		}
130 
131 		WideString subStr = str.substr(start, end - start + 1);
132 
133 		if (_gameRef->_textEncoding == TEXT_UTF8) {
134 			stack->pushString(StringUtil::wideToUtf8(subStr).c_str());
135 		} else {
136 			stack->pushString(StringUtil::wideToAnsi(subStr).c_str());
137 		}
138 		//  } catch (std::exception &) {
139 		//      stack->pushNULL();
140 		//  }
141 
142 		return STATUS_OK;
143 	}
144 
145 	//////////////////////////////////////////////////////////////////////////
146 	// Substr
147 	//////////////////////////////////////////////////////////////////////////
148 	else if (strcmp(name, "Substr") == 0) {
149 		stack->correctParams(2);
150 		int start = stack->pop()->getInt();
151 
152 		ScValue *val = stack->pop();
153 		int len = val->getInt();
154 
155 		if (!val->isNULL() && len <= 0) {
156 			stack->pushString("");
157 			return STATUS_OK;
158 		}
159 
160 		if (val->isNULL()) {
161 			len = strlen(_string) - start;
162 		}
163 
164 //		try {
165 		WideString str;
166 		if (_gameRef->_textEncoding == TEXT_UTF8) {
167 			str = StringUtil::utf8ToWide(_string);
168 		} else {
169 			str = StringUtil::ansiToWide(_string);
170 		}
171 
172 		WideString subStr = str.substr(start, len);
173 
174 		if (_gameRef->_textEncoding == TEXT_UTF8) {
175 			stack->pushString(StringUtil::wideToUtf8(subStr).c_str());
176 		} else {
177 			stack->pushString(StringUtil::wideToAnsi(subStr).c_str());
178 		}
179 //		} catch (std::exception &) {
180 //			stack->pushNULL();
181 //		}
182 
183 		return STATUS_OK;
184 	}
185 
186 	//////////////////////////////////////////////////////////////////////////
187 	// ToUpperCase
188 	//////////////////////////////////////////////////////////////////////////
189 	else if (strcmp(name, "ToUpperCase") == 0) {
190 		stack->correctParams(0);
191 
192 		WideString str;
193 		if (_gameRef->_textEncoding == TEXT_UTF8) {
194 			str = StringUtil::utf8ToWide(_string);
195 		} else {
196 			str = StringUtil::ansiToWide(_string);
197 		}
198 
199 		str.toUppercase();
200 
201 		if (_gameRef->_textEncoding == TEXT_UTF8) {
202 			stack->pushString(StringUtil::wideToUtf8(str).c_str());
203 		} else {
204 			stack->pushString(StringUtil::wideToAnsi(str).c_str());
205 		}
206 
207 		return STATUS_OK;
208 	}
209 
210 	//////////////////////////////////////////////////////////////////////////
211 	// ToLowerCase
212 	//////////////////////////////////////////////////////////////////////////
213 	else if (strcmp(name, "ToLowerCase") == 0) {
214 		stack->correctParams(0);
215 
216 		WideString str;
217 		if (_gameRef->_textEncoding == TEXT_UTF8) {
218 			str = StringUtil::utf8ToWide(_string);
219 		} else {
220 			str = StringUtil::ansiToWide(_string);
221 		}
222 
223 		str.toLowercase();
224 
225 		if (_gameRef->_textEncoding == TEXT_UTF8) {
226 			stack->pushString(StringUtil::wideToUtf8(str).c_str());
227 		} else {
228 			stack->pushString(StringUtil::wideToAnsi(str).c_str());
229 		}
230 
231 		return STATUS_OK;
232 	}
233 
234 	//////////////////////////////////////////////////////////////////////////
235 	// IndexOf
236 	//////////////////////////////////////////////////////////////////////////
237 	else if (strcmp(name, "IndexOf") == 0) {
238 		stack->correctParams(2);
239 
240 		const char *strToFind = stack->pop()->getString();
241 		int index = stack->pop()->getInt();
242 
243 		WideString str;
244 		if (_gameRef->_textEncoding == TEXT_UTF8) {
245 			str = StringUtil::utf8ToWide(_string);
246 		} else {
247 			str = StringUtil::ansiToWide(_string);
248 		}
249 
250 		WideString toFind;
251 		if (_gameRef->_textEncoding == TEXT_UTF8) {
252 			toFind = StringUtil::utf8ToWide(strToFind);
253 		} else {
254 			toFind = StringUtil::ansiToWide(strToFind);
255 		}
256 
257 		int indexOf = StringUtil::indexOf(str, toFind, index);
258 		stack->pushInt(indexOf);
259 
260 		return STATUS_OK;
261 	}
262 
263 #ifdef ENABLE_HEROCRAFT
264 	//////////////////////////////////////////////////////////////////////////
265 	// [HeroCraft] GetCharCode
266 	// Returns integer value of char at given position
267 	// Used at "Pole Chudes" only
268 	//////////////////////////////////////////////////////////////////////////
269 	else if (strcmp(name, "GetCharCode") == 0) {
270 		stack->correctParams(1);
271 
272 		int index = stack->pop()->getInt();
273 		int result = 0;
274 		if (strlen(_string) > (uint32)index) {
275 			result = _string[index];
276 		}
277 		stack->pushInt(result);
278 
279 		return STATUS_OK;
280 	}
281 #endif
282 
283 	//////////////////////////////////////////////////////////////////////////
284 	// Split
285 	//////////////////////////////////////////////////////////////////////////
286 	else if (strcmp(name, "Split") == 0) {
287 		stack->correctParams(1);
288 		ScValue *val = stack->pop();
289 		char separators[MAX_PATH_LENGTH] = ",";
290 		if (!val->isNULL()) {
291 			Common::strlcpy(separators, val->getString(), MAX_PATH_LENGTH);
292 		}
293 
294 		SXArray *array = new SXArray(_gameRef);
295 		if (!array) {
296 			stack->pushNULL();
297 			return STATUS_OK;
298 		}
299 
300 
301 		WideString str;
302 		if (_gameRef->_textEncoding == TEXT_UTF8) {
303 			str = StringUtil::utf8ToWide(_string);
304 		} else {
305 			str = StringUtil::ansiToWide(_string);
306 		}
307 
308 		WideString delims;
309 		if (_gameRef->_textEncoding == TEXT_UTF8) {
310 			delims = StringUtil::utf8ToWide(separators);
311 		} else {
312 			delims = StringUtil::ansiToWide(separators);
313 		}
314 
315 		Common::Array<WideString> parts;
316 
317 		uint32 start = 0;
318 		for(uint32 i = 0; i < str.size() + 1; i++) {
319 			// The [] operator doesn't allow access to the zero code terminator
320 			// (bug #6531)
321 			uint32 ch = (i == str.size()) ? '\0' : str[i];
322 			if (ch =='\0' || delims.contains(ch)) {
323 				if (i != start) {
324 					parts.push_back(str.substr(start, i - start));
325 				} else {
326 					parts.push_back(WideString());
327 				}
328 				start = i + 1;
329 			}
330 		}
331 
332 		for (Common::Array<WideString>::iterator it = parts.begin(); it != parts.end(); ++it) {
333 			WideString &part = (*it);
334 
335 			if (_gameRef->_textEncoding == TEXT_UTF8) {
336 				val = new ScValue(_gameRef,  StringUtil::wideToUtf8(part).c_str());
337 			} else {
338 				val = new ScValue(_gameRef,  StringUtil::wideToAnsi(part).c_str());
339 			}
340 
341 			array->push(val);
342 			delete val;
343 			val = nullptr;
344 		}
345 
346 		stack->pushNative(array, false);
347 		return STATUS_OK;
348 	} else {
349 		return STATUS_FAILED;
350 	}
351 }
352 
353 
354 //////////////////////////////////////////////////////////////////////////
scGetProperty(const Common::String & name)355 ScValue *SXString::scGetProperty(const Common::String &name) {
356 	_scValue->setNULL();
357 
358 	//////////////////////////////////////////////////////////////////////////
359 	// Type (RO)
360 	//////////////////////////////////////////////////////////////////////////
361 	if (name == "Type") {
362 		_scValue->setString("string");
363 		return _scValue;
364 	}
365 	//////////////////////////////////////////////////////////////////////////
366 	// Length (RO)
367 	//////////////////////////////////////////////////////////////////////////
368 	else if (name == "Length") {
369 		if (_gameRef->_textEncoding == TEXT_UTF8) {
370 			WideString wstr = StringUtil::utf8ToWide(_string);
371 			_scValue->setInt(wstr.size());
372 		} else {
373 			_scValue->setInt(strlen(_string));
374 		}
375 
376 		return _scValue;
377 	}
378 	//////////////////////////////////////////////////////////////////////////
379 	// Capacity
380 	//////////////////////////////////////////////////////////////////////////
381 	else if (name == "Capacity") {
382 		_scValue->setInt(_capacity);
383 		return _scValue;
384 	} else {
385 		return _scValue;
386 	}
387 }
388 
389 
390 //////////////////////////////////////////////////////////////////////////
scSetProperty(const char * name,ScValue * value)391 bool SXString::scSetProperty(const char *name, ScValue *value) {
392 	//////////////////////////////////////////////////////////////////////////
393 	// Capacity
394 	//////////////////////////////////////////////////////////////////////////
395 	if (strcmp(name, "Capacity") == 0) {
396 		int32 newCap = (uint32)value->getInt();
397 		if (newCap < (int32)(strlen(_string) + 1)) {
398 			_gameRef->LOG(0, "Warning: cannot lower string capacity");
399 		} else if (newCap != _capacity) {
400 			char *newStr = new char[newCap];
401 			if (newStr) {
402 				memset(newStr, 0, newCap);
403 				strcpy(newStr, _string);
404 				delete[] _string;
405 				_string = newStr;
406 				_capacity = newCap;
407 			}
408 		}
409 		return STATUS_OK;
410 	} else {
411 		return STATUS_FAILED;
412 	}
413 }
414 
415 
416 //////////////////////////////////////////////////////////////////////////
persist(BasePersistenceManager * persistMgr)417 bool SXString::persist(BasePersistenceManager *persistMgr) {
418 
419 	BaseScriptable::persist(persistMgr);
420 
421 	persistMgr->transferSint32(TMEMBER(_capacity));
422 
423 	if (persistMgr->getIsSaving()) {
424 		if (_capacity > 0) {
425 			persistMgr->putBytes((byte *)_string, _capacity);
426 		}
427 	} else {
428 		if (_capacity > 0) {
429 			_string = new char[_capacity];
430 			persistMgr->getBytes((byte *)_string, _capacity);
431 		} else {
432 			_string = nullptr;
433 		}
434 	}
435 
436 	return STATUS_OK;
437 }
438 
439 
440 //////////////////////////////////////////////////////////////////////////
scCompare(BaseScriptable * val)441 int SXString::scCompare(BaseScriptable *val) {
442 	return strcmp(_string, ((SXString *)val)->_string);
443 }
444 
445 } // End of namespace Wintermute
446