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_scriptable.h"
30 #include "engines/wintermute/base/scriptables/script_stack.h"
31 #include "engines/wintermute/base/scriptables/script.h"
32 #include "engines/wintermute/base/scriptables/script_value.h"
33 #include "engines/wintermute/base/scriptables/script_ext_mem_buffer.h"
34 #include "common/file.h"
35 
36 namespace Wintermute {
37 
IMPLEMENT_PERSISTENT(SXMemBuffer,false)38 IMPLEMENT_PERSISTENT(SXMemBuffer, false)
39 
40 BaseScriptable *makeSXMemBuffer(BaseGame *inGame, ScStack *stack) {
41 	return new SXMemBuffer(inGame, stack);
42 }
43 
44 //////////////////////////////////////////////////////////////////////////
SXMemBuffer(BaseGame * inGame,ScStack * stack)45 SXMemBuffer::SXMemBuffer(BaseGame *inGame, ScStack *stack) : BaseScriptable(inGame) {
46 	stack->correctParams(1);
47 	_buffer = nullptr;
48 	_size = 0;
49 
50 	int newSize = stack->pop()->getInt();
51 	resize(MAX(0, newSize));
52 }
53 
54 //////////////////////////////////////////////////////////////////////////
SXMemBuffer(BaseGame * inGame,void * buffer)55 SXMemBuffer::SXMemBuffer(BaseGame *inGame, void *buffer) : BaseScriptable(inGame) {
56 	_size = 0;
57 	_buffer = buffer;
58 }
59 
60 
61 //////////////////////////////////////////////////////////////////////////
~SXMemBuffer()62 SXMemBuffer::~SXMemBuffer() {
63 	cleanup();
64 }
65 
66 //////////////////////////////////////////////////////////////////////////
scToMemBuffer()67 void *SXMemBuffer::scToMemBuffer() {
68 	return _buffer;
69 }
70 
71 //////////////////////////////////////////////////////////////////////////
cleanup()72 void SXMemBuffer::cleanup() {
73 	if (_size) {
74 		free(_buffer);
75 	}
76 	_buffer = nullptr;
77 	_size = 0;
78 }
79 
80 //////////////////////////////////////////////////////////////////////////
resize(int newSize)81 bool SXMemBuffer::resize(int newSize) {
82 	int oldSize = _size;
83 
84 	if (_size == 0) {
85 		_buffer = malloc(newSize);
86 		if (_buffer) {
87 			_size = newSize;
88 		}
89 	} else {
90 		void *newBuf = realloc(_buffer, newSize);
91 		if (!newBuf) {
92 			if (newSize == 0) {
93 				_buffer = newBuf;
94 				_size = newSize;
95 			} else {
96 				return STATUS_FAILED;
97 			}
98 		} else {
99 			_buffer = newBuf;
100 			_size = newSize;
101 		}
102 	}
103 
104 	if (_buffer && _size > oldSize) {
105 		memset((byte *)_buffer + oldSize, 0, _size - oldSize);
106 	}
107 	return STATUS_OK;
108 }
109 
110 //////////////////////////////////////////////////////////////////////////
checkBounds(ScScript * script,int start,int length)111 bool SXMemBuffer::checkBounds(ScScript *script, int start, int length) {
112 	if (_buffer == nullptr) {
113 		script->runtimeError("Cannot use Set/Get methods on an uninitialized memory buffer");
114 		return false;
115 	}
116 	if (_size == 0) {
117 		return true;
118 	}
119 
120 	if (start < 0 || length == 0 || start + length > _size) {
121 		script->runtimeError("Set/Get method call is out of bounds");
122 		return false;
123 	} else {
124 		return true;
125 	}
126 }
127 
128 //////////////////////////////////////////////////////////////////////////
scToString()129 const char *SXMemBuffer::scToString() {
130 	return "[membuffer object]";
131 }
132 
133 
134 //////////////////////////////////////////////////////////////////////////
scCallMethod(ScScript * script,ScStack * stack,ScStack * thisStack,const char * name)135 bool SXMemBuffer::scCallMethod(ScScript *script, ScStack *stack, ScStack *thisStack, const char *name) {
136 	//////////////////////////////////////////////////////////////////////////
137 	// SetSize
138 	//////////////////////////////////////////////////////////////////////////
139 	if (strcmp(name, "SetSize") == 0) {
140 		stack->correctParams(1);
141 		int newSize = stack->pop()->getInt();
142 		newSize = MAX(0, newSize);
143 		if (DID_SUCCEED(resize(newSize))) {
144 			stack->pushBool(true);
145 		} else {
146 			stack->pushBool(false);
147 		}
148 
149 		return STATUS_OK;
150 	}
151 
152 	//////////////////////////////////////////////////////////////////////////
153 	// GetBool
154 	//////////////////////////////////////////////////////////////////////////
155 	else if (strcmp(name, "GetBool") == 0) {
156 		stack->correctParams(1);
157 		int start = stack->pop()->getInt();
158 		if (!checkBounds(script, start, sizeof(bool))) {
159 			stack->pushNULL();
160 		} else {
161 			stack->pushBool(*(bool *)((byte *)_buffer + start));
162 		}
163 
164 		return STATUS_OK;
165 	}
166 
167 	//////////////////////////////////////////////////////////////////////////
168 	// GetByte
169 	//////////////////////////////////////////////////////////////////////////
170 	else if (strcmp(name, "GetByte") == 0) {
171 		stack->correctParams(1);
172 		int start = stack->pop()->getInt();
173 		if (!checkBounds(script, start, sizeof(byte))) {
174 			stack->pushNULL();
175 		} else {
176 			stack->pushInt(*(byte *)((byte *)_buffer + start));
177 		}
178 
179 		return STATUS_OK;
180 	}
181 
182 	//////////////////////////////////////////////////////////////////////////
183 	// GetShort
184 	//////////////////////////////////////////////////////////////////////////
185 	else if (strcmp(name, "GetShort") == 0) {
186 		stack->correctParams(1);
187 		int start = stack->pop()->getInt();
188 		if (!checkBounds(script, start, sizeof(short))) {
189 			stack->pushNULL();
190 		} else {
191 			stack->pushInt(65536 + * (short *)((byte *)_buffer + start));
192 		}
193 
194 		return STATUS_OK;
195 	}
196 
197 	//////////////////////////////////////////////////////////////////////////
198 	// GetInt / GetLong
199 	//////////////////////////////////////////////////////////////////////////
200 	else if (strcmp(name, "GetInt") == 0 || strcmp(name, "GetLong") == 0) {
201 		stack->correctParams(1);
202 		int start = stack->pop()->getInt();
203 		if (!checkBounds(script, start, sizeof(int))) {
204 			stack->pushNULL();
205 		} else {
206 			stack->pushInt(*(int *)((byte *)_buffer + start));
207 		}
208 
209 		return STATUS_OK;
210 	}
211 
212 	//////////////////////////////////////////////////////////////////////////
213 	// GetFloat
214 	//////////////////////////////////////////////////////////////////////////
215 	else if (strcmp(name, "GetFloat") == 0) {
216 		stack->correctParams(1);
217 		int start = stack->pop()->getInt();
218 		if (!checkBounds(script, start, sizeof(float))) {
219 			stack->pushNULL();
220 		} else {
221 			stack->pushFloat(*(float *)((byte *)_buffer + start));
222 		}
223 
224 		return STATUS_OK;
225 	}
226 
227 	//////////////////////////////////////////////////////////////////////////
228 	// GetDouble
229 	//////////////////////////////////////////////////////////////////////////
230 	else if (strcmp(name, "GetDouble") == 0) {
231 		stack->correctParams(1);
232 		int start = stack->pop()->getInt();
233 		if (!checkBounds(script, start, sizeof(double))) {
234 			stack->pushNULL();
235 		} else {
236 			stack->pushFloat(*(double *)((byte *)_buffer + start));
237 		}
238 
239 		return STATUS_OK;
240 	}
241 
242 	//////////////////////////////////////////////////////////////////////////
243 	// GetString
244 	//////////////////////////////////////////////////////////////////////////
245 	else if (strcmp(name, "GetString") == 0) {
246 		stack->correctParams(2);
247 		int start = stack->pop()->getInt();
248 		int length = stack->pop()->getInt();
249 
250 		// find end of string
251 		if (length == 0 && start >= 0 && start < _size) {
252 			for (int i = start; i < _size; i++) {
253 				if (((char *)_buffer)[i] == '\0') {
254 					length = i - start;
255 					break;
256 				}
257 			}
258 		}
259 
260 		if (!checkBounds(script, start, length)) {
261 			stack->pushNULL();
262 		} else {
263 			char *str = new char[length + 1];
264 			Common::strlcpy(str, (const char *)_buffer + start, length + 1);
265 			stack->pushString(str);
266 			delete[] str;
267 		}
268 		return STATUS_OK;
269 	}
270 
271 	//////////////////////////////////////////////////////////////////////////
272 	// GetPointer
273 	//////////////////////////////////////////////////////////////////////////
274 	else if (strcmp(name, "GetPointer") == 0) {
275 		stack->correctParams(1);
276 		int start = stack->pop()->getInt();
277 		if (!checkBounds(script, start, sizeof(void *))) {
278 			stack->pushNULL();
279 		} else {
280 			void *pointer = *(void **)((byte *)_buffer + start);
281 			SXMemBuffer *buf = new SXMemBuffer(_gameRef,  pointer);
282 			stack->pushNative(buf, false);
283 		}
284 		return STATUS_OK;
285 	}
286 
287 	//////////////////////////////////////////////////////////////////////////
288 	// SetBool
289 	//////////////////////////////////////////////////////////////////////////
290 	else if (strcmp(name, "SetBool") == 0) {
291 		stack->correctParams(2);
292 		int start = stack->pop()->getInt();
293 		bool val = stack->pop()->getBool();
294 
295 		if (!checkBounds(script, start, sizeof(bool))) {
296 			stack->pushBool(false);
297 		} else {
298 			*(bool *)((byte *)_buffer + start) = val;
299 			stack->pushBool(true);
300 		}
301 		return STATUS_OK;
302 	}
303 
304 	//////////////////////////////////////////////////////////////////////////
305 	// SetByte
306 	//////////////////////////////////////////////////////////////////////////
307 	else if (strcmp(name, "SetByte") == 0) {
308 		stack->correctParams(2);
309 		int start = stack->pop()->getInt();
310 		byte val = (byte)stack->pop()->getInt();
311 
312 		if (!checkBounds(script, start, sizeof(byte))) {
313 			stack->pushBool(false);
314 		} else {
315 			*(byte *)((byte *)_buffer + start) = val;
316 			stack->pushBool(true);
317 		}
318 		return STATUS_OK;
319 	}
320 
321 	//////////////////////////////////////////////////////////////////////////
322 	// SetShort
323 	//////////////////////////////////////////////////////////////////////////
324 	else if (strcmp(name, "SetShort") == 0) {
325 		stack->correctParams(2);
326 		int start = stack->pop()->getInt();
327 		short val = (short)stack->pop()->getInt();
328 
329 		if (!checkBounds(script, start, sizeof(short))) {
330 			stack->pushBool(false);
331 		} else {
332 			*(short *)((byte *)_buffer + start) = val;
333 			stack->pushBool(true);
334 		}
335 		return STATUS_OK;
336 	}
337 
338 	//////////////////////////////////////////////////////////////////////////
339 	// SetInt / SetLong
340 	//////////////////////////////////////////////////////////////////////////
341 	else if (strcmp(name, "SetInt") == 0 || strcmp(name, "SetLong") == 0) {
342 		stack->correctParams(2);
343 		int start = stack->pop()->getInt();
344 		int val = stack->pop()->getInt();
345 
346 		if (!checkBounds(script, start, sizeof(int))) {
347 			stack->pushBool(false);
348 		} else {
349 			*(int *)((byte *)_buffer + start) = val;
350 			stack->pushBool(true);
351 		}
352 		return STATUS_OK;
353 	}
354 
355 	//////////////////////////////////////////////////////////////////////////
356 	// SetFloat
357 	//////////////////////////////////////////////////////////////////////////
358 	else if (strcmp(name, "SetFloat") == 0) {
359 		stack->correctParams(2);
360 		int start = stack->pop()->getInt();
361 		float val = (float)stack->pop()->getFloat();
362 
363 		if (!checkBounds(script, start, sizeof(float))) {
364 			stack->pushBool(false);
365 		} else {
366 			*(float *)((byte *)_buffer + start) = val;
367 			stack->pushBool(true);
368 		}
369 		return STATUS_OK;
370 	}
371 
372 	//////////////////////////////////////////////////////////////////////////
373 	// SetDouble
374 	//////////////////////////////////////////////////////////////////////////
375 	else if (strcmp(name, "SetDouble") == 0) {
376 		stack->correctParams(2);
377 		int start = stack->pop()->getInt();
378 		double val = stack->pop()->getFloat();
379 
380 		if (!checkBounds(script, start, sizeof(double))) {
381 			stack->pushBool(false);
382 		} else {
383 			*(double *)((byte *)_buffer + start) = val;
384 			stack->pushBool(true);
385 		}
386 		return STATUS_OK;
387 	}
388 
389 	//////////////////////////////////////////////////////////////////////////
390 	// SetString
391 	//////////////////////////////////////////////////////////////////////////
392 	else if (strcmp(name, "SetString") == 0) {
393 		stack->correctParams(2);
394 		int start = stack->pop()->getInt();
395 		const char *val = stack->pop()->getString();
396 
397 		if (!checkBounds(script, start, strlen(val) + 1)) {
398 			stack->pushBool(false);
399 		} else {
400 			memcpy((byte *)_buffer + start, val, strlen(val) + 1);
401 			stack->pushBool(true);
402 		}
403 		return STATUS_OK;
404 	}
405 
406 	//////////////////////////////////////////////////////////////////////////
407 	// SetPointer
408 	//////////////////////////////////////////////////////////////////////////
409 	else if (strcmp(name, "SetPointer") == 0) {
410 		stack->correctParams(2);
411 		int start = stack->pop()->getInt();
412 		/* ScValue *val = */ stack->pop();
413 
414 		if (!checkBounds(script, start, sizeof(void *))) {
415 			stack->pushBool(false);
416 		} else {
417 			/*
418 			int pointer = (int)Val->getMemBuffer();
419 			memcpy((byte *)_buffer+Start, &Pointer, sizeof(void*));
420 			stack->pushBool(true);
421 			*/
422 			// TODO fix
423 			stack->pushBool(false);
424 
425 		}
426 		return STATUS_OK;
427 	}
428 
429 	//////////////////////////////////////////////////////////////////////////
430 	// DEBUG_Dump
431 	//////////////////////////////////////////////////////////////////////////
432 	else if (strcmp(name, "DEBUG_Dump") == 0) {
433 		stack->correctParams(0);
434 		if (_buffer && _size) {
435 			warning("SXMemBuffer::ScCallMethod - DEBUG_Dump");
436 			Common::DumpFile f;
437 			f.open("buffer.bin");
438 			f.write(_buffer, _size);
439 			f.close();
440 		}
441 		stack->pushNULL();
442 		return STATUS_OK;
443 	} else {
444 		return STATUS_FAILED;
445 	}
446 }
447 
448 
449 //////////////////////////////////////////////////////////////////////////
scGetProperty(const Common::String & name)450 ScValue *SXMemBuffer::scGetProperty(const Common::String &name) {
451 	_scValue->setNULL();
452 
453 	//////////////////////////////////////////////////////////////////////////
454 	// Type (RO)
455 	//////////////////////////////////////////////////////////////////////////
456 	if (name == "Type") {
457 		_scValue->setString("membuffer");
458 		return _scValue;
459 	}
460 
461 	//////////////////////////////////////////////////////////////////////////
462 	// Size (RO)
463 	//////////////////////////////////////////////////////////////////////////
464 	if (name == "Size") {
465 		_scValue->setInt(_size);
466 		return _scValue;
467 	} else {
468 		return BaseScriptable::scGetProperty(name);
469 	}
470 }
471 
472 
473 //////////////////////////////////////////////////////////////////////////
scSetProperty(const char * name,ScValue * value)474 bool SXMemBuffer::scSetProperty(const char *name, ScValue *value) {
475 	/*
476 	//////////////////////////////////////////////////////////////////////////
477 	// Length
478 	//////////////////////////////////////////////////////////////////////////
479 	if (strcmp(name, "Length")==0) {
480 	    int origLength = _length;
481 	    _length = max(value->getInt(0), 0);
482 
483 	    char propName[20];
484 	    if (_length < origLength) {
485 	        for(int i=_length; i < origLength; i++) {
486 	            sprintf(propName, "%d", i);
487 	            _values->DeleteProp(propName);
488 	        }
489 	    }
490 	    return STATUS_OK;
491 	}
492 	else*/ return BaseScriptable::scSetProperty(name, value);
493 }
494 
495 
496 //////////////////////////////////////////////////////////////////////////
persist(BasePersistenceManager * persistMgr)497 bool SXMemBuffer::persist(BasePersistenceManager *persistMgr) {
498 
499 	BaseScriptable::persist(persistMgr);
500 
501 	persistMgr->transferSint32(TMEMBER(_size));
502 
503 	if (persistMgr->getIsSaving()) {
504 		if (_size > 0) {
505 			persistMgr->putBytes((byte *)_buffer, _size);
506 		}
507 	} else {
508 		if (_size > 0) {
509 			_buffer = malloc(_size);
510 			persistMgr->getBytes((byte *)_buffer, _size);
511 		} else {
512 			_buffer = nullptr;
513 		}
514 	}
515 
516 	return STATUS_OK;
517 }
518 
519 
520 //////////////////////////////////////////////////////////////////////////
scCompare(BaseScriptable * val)521 int SXMemBuffer::scCompare(BaseScriptable *val) {
522 	if (_buffer == val->scToMemBuffer()) {
523 		return 0;
524 	} else {
525 		return 1;
526 	}
527 }
528 
529 } // End of namespace Wintermute
530