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