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 #include "common/debug.h"
24 #include "common/endian.h"
25 #include "common/stream.h"
26 
27 #include "toon/toon.h"
28 #include "toon/script.h"
29 
30 namespace Toon {
EMCInterpreter(ToonEngine * vm)31 EMCInterpreter::EMCInterpreter(ToonEngine *vm) : _vm(vm), _scriptData(0), _filename(0) {
32 
33 #define OPCODE(x) { &EMCInterpreter::x, #x }
34 	static const OpcodeEntry opcodes[] = {
35 		// 0x00
36 		OPCODE(op_jmp),
37 		OPCODE(op_setRetValue),
38 		OPCODE(op_pushRetOrPos),
39 		OPCODE(op_push),
40 		// 0x04
41 		OPCODE(op_push),
42 		OPCODE(op_pushReg),
43 		OPCODE(op_pushBPNeg),
44 		OPCODE(op_pushBPAdd),
45 		// 0x08
46 		OPCODE(op_popRetOrPos),
47 		OPCODE(op_popReg),
48 		OPCODE(op_popBPNeg),
49 		OPCODE(op_popBPAdd),
50 		// 0x0C
51 		OPCODE(op_addSP),
52 		OPCODE(op_subSP),
53 		OPCODE(op_sysCall),
54 		OPCODE(op_ifNotJmp),
55 		// 0x10
56 		OPCODE(op_negate),
57 		OPCODE(op_eval),
58 		OPCODE(op_setRetAndJmp)
59 	};
60 	_opcodes = opcodes;
61 #undef OPCODE
62 
63 	_parameter = 0;
64 }
65 
~EMCInterpreter()66 EMCInterpreter::~EMCInterpreter() {
67 }
68 
callback(Common::IFFChunk & chunk)69 bool EMCInterpreter::callback(Common::IFFChunk &chunk) {
70 	switch (chunk._type) {
71 	case MKTAG('T','E','X','T'):
72 		delete[] _scriptData->text;
73 		_scriptData->text = new byte[chunk._size];
74 		assert(_scriptData->text);
75 		if (chunk._stream->read(_scriptData->text, chunk._size) != chunk._size)
76 			error("Couldn't read TEXT chunk from file '%s'", _filename);
77 		break;
78 
79 	case MKTAG('O','R','D','R'):
80 		delete[] _scriptData->ordr;
81 		_scriptData->ordr = new uint16[chunk._size >> 1];
82 		assert(_scriptData->ordr);
83 		if (chunk._stream->read(_scriptData->ordr, chunk._size) != chunk._size)
84 			error("Couldn't read ORDR chunk from file '%s'", _filename);
85 
86 		for (int i = (chunk._size >> 1) - 1; i >= 0; --i)
87 			_scriptData->ordr[i] = READ_BE_UINT16(&_scriptData->ordr[i]);
88 		break;
89 
90 	case MKTAG('D','A','T','A'):
91 		delete[] _scriptData->data;
92 		_scriptData->data = new uint16[chunk._size >> 1];
93 		assert(_scriptData->data);
94 		if (chunk._stream->read(_scriptData->data, chunk._size) != chunk._size)
95 			error("Couldn't read DATA chunk from file '%s'", _filename);
96 
97 		for (int i = (chunk._size >> 1) - 1; i >= 0; --i)
98 			_scriptData->data[i] = READ_BE_UINT16(&_scriptData->data[i]);
99 		break;
100 
101 	default:
102 		warning("Unexpected chunk '%s' of size %d found in file '%s'", Common::tag2string(chunk._type).c_str(), chunk._size, _filename);
103 	}
104 
105 	return false;
106 }
107 
load(const char * filename,EMCData * scriptData,const Common::Array<const OpcodeV2 * > * opcodes)108 bool EMCInterpreter::load(const char *filename, EMCData *scriptData, const Common::Array<const OpcodeV2 *> *opcodes) {
109 	Common::SeekableReadStream *stream = _vm->resources()->openFile(filename);
110 	if (!stream) {
111 		error("Couldn't open script file '%s'", filename);
112 		return false; // for compilers that don't support NORETURN
113 	}
114 
115 	memset(scriptData, 0, sizeof(EMCData));
116 
117 	_scriptData = scriptData;
118 	_filename = filename;
119 
120 	IFFParser iff(*stream);
121 	Common::Functor1Mem< Common::IFFChunk &, bool, EMCInterpreter > c(this, &EMCInterpreter::callback);
122 	iff.parse(c);
123 
124 	if (!_scriptData->ordr)
125 		error("No ORDR chunk found in file: '%s'", filename);
126 
127 	if (!_scriptData->data)
128 		error("No DATA chunk found in file: '%s'", filename);
129 
130 	if (stream->err())
131 		error("Read error while parsing file '%s'", filename);
132 
133 	delete stream;
134 
135 	_scriptData->sysFuncs = opcodes;
136 
137 	Common::strlcpy(_scriptData->filename, filename, 13);
138 
139 	_scriptData = 0;
140 	_filename = 0;
141 
142 	return true;
143 }
144 
unload(EMCData * data)145 void EMCInterpreter::unload(EMCData *data) {
146 	if (!data)
147 		return;
148 
149 	delete[] data->text;
150 	data->text = NULL;
151 
152 	delete[] data->ordr;
153 	data->ordr = NULL;
154 
155 	delete[] data->data;
156 	 data->data = NULL;
157 }
158 
init(EMCState * scriptStat,const EMCData * data)159 void EMCInterpreter::init(EMCState *scriptStat, const EMCData *data) {
160 	scriptStat->dataPtr = data;
161 	scriptStat->ip = 0;
162 	scriptStat->stack[EMCState::kStackLastEntry] = 0;
163 	scriptStat->bp = EMCState::kStackSize + 1;
164 	scriptStat->sp = EMCState::kStackLastEntry;
165 	scriptStat->running = false;
166 }
167 
start(EMCState * script,int function)168 bool EMCInterpreter::start(EMCState *script, int function) {
169 	if (!script->dataPtr)
170 		return false;
171 
172 	uint16 functionOffset = script->dataPtr->ordr[function];
173 	if (functionOffset == 0xFFFF)
174 		return false;
175 
176 	script->ip = &script->dataPtr->data[functionOffset + 1];
177 
178 	return true;
179 }
180 
isValid(EMCState * script)181 bool EMCInterpreter::isValid(EMCState *script) {
182 	if (!script->ip || !script->dataPtr || _vm->shouldQuitGame())
183 		return false;
184 	return true;
185 }
186 
run(EMCState * script)187 bool EMCInterpreter::run(EMCState *script) {
188 	if (script->running)
189 		return false;
190 
191 	_parameter = 0;
192 
193 	if (!script->ip)
194 		return false;
195 
196 	script->running = true;
197 
198 	// Should be no Problem at all to cast to uint32 here, since that's the biggest ptrdiff the original
199 	// would allow, of course that's not realistic to happen to be somewhere near the limit of uint32 anyway.
200 	const uint32 instOffset = (uint32)((const byte *)script->ip - (const byte *)script->dataPtr->data);
201 	int16 code = *script->ip++;
202 	int16 opcode = (code >> 8) & 0x1F;
203 
204 	if (code & 0x8000) {
205 		opcode = 0;
206 		_parameter = code & 0x7FFF;
207 	} else if (code & 0x4000) {
208 		_parameter = (int8)(code);
209 	} else if (code & 0x2000) {
210 		_parameter = *script->ip++;
211 	} else {
212 		_parameter = 0;
213 	}
214 
215 	if (opcode > 18) {
216 		error("Unknown script opcode: %d in file '%s' at offset 0x%.08X", opcode, script->dataPtr->filename, instOffset);
217 	} else {
218 		static bool EMCDebug = false;
219 		if (EMCDebug)
220 			debugC(5, 0, "[0x%.08X] EMCInterpreter::%s([%d/%u])", instOffset * 2, _opcodes[opcode].desc, _parameter, (uint)_parameter);
221 		//printf( "[0x%.08X] EMCInterpreter::%s([%d/%u])\n", instOffset, _opcodes[opcode].desc, _parameter, (uint)_parameter);
222 
223 		(this->*(_opcodes[opcode].proc))(script);
224 	}
225 
226 	script->running = false;
227 	return (script->ip != 0);
228 }
229 
230 #pragma mark -
231 #pragma mark - Command implementations
232 #pragma mark -
233 
op_jmp(EMCState * script)234 void EMCInterpreter::op_jmp(EMCState *script) {
235 	script->ip = script->dataPtr->data + _parameter;
236 }
237 
op_setRetValue(EMCState * script)238 void EMCInterpreter::op_setRetValue(EMCState *script) {
239 	script->retValue = _parameter;
240 }
241 
op_pushRetOrPos(EMCState * script)242 void EMCInterpreter::op_pushRetOrPos(EMCState *script) {
243 	switch (_parameter) {
244 	case 0:
245 		script->stack[--script->sp] = script->retValue;
246 		break;
247 
248 	case 1:
249 		script->stack[--script->sp] = script->ip - script->dataPtr->data + 1;
250 		script->stack[--script->sp] = script->bp;
251 		script->bp = script->sp + 2;
252 		break;
253 
254 	default:
255 		script->ip = 0;
256 	}
257 }
258 
op_push(EMCState * script)259 void EMCInterpreter::op_push(EMCState *script) {
260 	script->stack[--script->sp] = _parameter;
261 }
262 
op_pushReg(EMCState * script)263 void EMCInterpreter::op_pushReg(EMCState *script) {
264 	script->stack[--script->sp] = script->regs[_parameter];
265 }
266 
op_pushBPNeg(EMCState * script)267 void EMCInterpreter::op_pushBPNeg(EMCState *script) {
268 	script->stack[--script->sp] = script->stack[(-(int32)(_parameter + 2)) + script->bp];
269 }
270 
op_pushBPAdd(EMCState * script)271 void EMCInterpreter::op_pushBPAdd(EMCState *script) {
272 	script->stack[--script->sp] = script->stack[(_parameter - 1) + script->bp];
273 }
274 
op_popRetOrPos(EMCState * script)275 void EMCInterpreter::op_popRetOrPos(EMCState *script) {
276 	switch (_parameter) {
277 	case 0:
278 		script->retValue = script->stack[script->sp++];
279 		break;
280 
281 	case 1:
282 		if (script->sp >= EMCState::kStackLastEntry) {
283 			script->ip = 0;
284 		} else {
285 			script->bp = script->stack[script->sp++];
286 			script->ip = script->dataPtr->data + script->stack[script->sp++];
287 		}
288 		break;
289 
290 	default:
291 		script->ip = 0;
292 	}
293 }
294 
op_popReg(EMCState * script)295 void EMCInterpreter::op_popReg(EMCState *script) {
296 	script->regs[_parameter] = script->stack[script->sp++];
297 }
298 
op_popBPNeg(EMCState * script)299 void EMCInterpreter::op_popBPNeg(EMCState *script) {
300 	script->stack[(-(int32)(_parameter + 2)) + script->bp] = script->stack[script->sp++];
301 }
302 
op_popBPAdd(EMCState * script)303 void EMCInterpreter::op_popBPAdd(EMCState *script) {
304 	script->stack[(_parameter - 1) + script->bp] = script->stack[script->sp++];
305 }
306 
op_addSP(EMCState * script)307 void EMCInterpreter::op_addSP(EMCState *script) {
308 	script->sp += _parameter;
309 }
310 
op_subSP(EMCState * script)311 void EMCInterpreter::op_subSP(EMCState *script) {
312 	script->sp -= _parameter;
313 }
314 
op_sysCall(EMCState * script)315 void EMCInterpreter::op_sysCall(EMCState *script) {
316 	const uint8 id = _parameter;
317 
318 	assert(script->dataPtr->sysFuncs);
319 	assert(id < script->dataPtr->sysFuncs->size());
320 
321 	if ((*script->dataPtr->sysFuncs)[id] && ((*script->dataPtr->sysFuncs)[id])->isValid()) {
322 		script->retValue = (*(*script->dataPtr->sysFuncs)[id])(script);
323 	} else {
324 		script->retValue = 0;
325 		warning("Unimplemented system call 0x%.02X/%d used in file '%s'", id, id, script->dataPtr->filename);
326 	}
327 }
328 
op_ifNotJmp(EMCState * script)329 void EMCInterpreter::op_ifNotJmp(EMCState *script) {
330 	if (!script->stack[script->sp++]) {
331 		_parameter &= 0x7FFF;
332 		script->ip = script->dataPtr->data + _parameter;
333 	}
334 }
335 
op_negate(EMCState * script)336 void EMCInterpreter::op_negate(EMCState *script) {
337 	int16 value = script->stack[script->sp];
338 	switch (_parameter) {
339 	case 0:
340 		if (!value)
341 			script->stack[script->sp] = 1;
342 		else
343 			script->stack[script->sp] = 0;
344 		break;
345 
346 	case 1:
347 		script->stack[script->sp] = -value;
348 		break;
349 
350 	case 2:
351 		script->stack[script->sp] = ~value;
352 		break;
353 
354 	default:
355 		warning("Unknown negation func: %d", _parameter);
356 		script->ip = 0;
357 	}
358 }
359 
op_eval(EMCState * script)360 void EMCInterpreter::op_eval(EMCState *script) {
361 	int16 ret = 0;
362 	bool error = false;
363 
364 	int16 val1 = script->stack[script->sp++];
365 	int16 val2 = script->stack[script->sp++];
366 
367 	switch (_parameter) {
368 	case 0:
369 		ret = (val2 && val1) ? 1 : 0;
370 		break;
371 
372 	case 1:
373 		ret = (val2 || val1) ? 1 : 0;
374 		break;
375 
376 	case 2:
377 		ret = (val1 == val2) ? 1 : 0;
378 		break;
379 
380 	case 3:
381 		ret = (val1 != val2) ? 1 : 0;
382 		break;
383 
384 	case 4:
385 		ret = (val1 > val2) ? 1 : 0;
386 		break;
387 
388 	case 5:
389 		ret = (val1 >= val2) ? 1 : 0;
390 		break;
391 
392 	case 6:
393 		ret = (val1 < val2) ? 1 : 0;
394 		break;
395 
396 	case 7:
397 		ret = (val1 <= val2) ? 1 : 0;
398 		break;
399 
400 	case 8:
401 		ret = val1 + val2;
402 		break;
403 
404 	case 9:
405 		ret = val2 - val1;
406 		break;
407 
408 	case 10:
409 		ret = val1 * val2;
410 		break;
411 
412 	case 11:
413 		ret = val2 / val1;
414 		break;
415 
416 	case 12:
417 		ret = val2 >> val1;
418 		break;
419 
420 	case 13:
421 		ret = val2 << val1;
422 		break;
423 
424 	case 14:
425 		ret = val1 & val2;
426 		break;
427 
428 	case 15:
429 		ret = val1 | val2;
430 		break;
431 
432 	case 16:
433 		ret = val2 % val1;
434 		break;
435 
436 	case 17:
437 		ret = val1 ^ val2;
438 		break;
439 
440 	default:
441 		warning("Unknown evaluate func: %d", _parameter);
442 		error = true;
443 	}
444 
445 	if (error)
446 		script->ip = 0;
447 	else
448 		script->stack[--script->sp] = ret;
449 }
450 
op_setRetAndJmp(EMCState * script)451 void EMCInterpreter::op_setRetAndJmp(EMCState *script) {
452 	if (script->sp >= EMCState::kStackLastEntry) {
453 		script->ip = 0;
454 	} else {
455 		script->retValue = script->stack[script->sp++];
456 		uint16 temp = script->stack[script->sp++];
457 		script->stack[EMCState::kStackLastEntry] = 0;
458 		script->ip = &script->dataPtr->data[temp];
459 	}
460 }
461 
saveState(EMCState * script,Common::WriteStream * stream)462 void EMCInterpreter::saveState(EMCState *script, Common::WriteStream *stream) {
463 	stream->writeSint16LE(script->bp);
464 	stream->writeSint16LE(script->sp);
465 	if (!script->ip) {
466 		stream->writeSint16LE(-1);
467 	} else {
468 		stream->writeSint16LE(script->ip - script->dataPtr->data);
469 	}
470 
471 	for (int32 i = 0; i < EMCState::kStackSize; i++) {
472 		stream->writeSint16LE(script->stack[i]);
473 	}
474 
475 	for (int32 i = 0; i < 30; i++) {
476 		stream->writeSint16LE(script->regs[i]);
477 	}
478 
479 	stream->writeSint16LE(script->retValue);
480 	stream->writeByte(script->running);
481 }
loadState(EMCState * script,Common::ReadStream * stream)482 void EMCInterpreter::loadState(EMCState *script, Common::ReadStream *stream) {
483 	script->bp = stream->readSint16LE();
484 	script->sp = stream->readSint16LE();
485 
486 	int16 scriptIp = stream->readSint16LE();
487 	if (scriptIp == -1) {
488 		script->ip = 0;
489 	} else {
490 		script->ip = scriptIp + script->dataPtr->data;
491 	}
492 
493 	for (int32 i = 0; i < EMCState::kStackSize; i++) {
494 		script->stack[i] = stream->readSint16LE();
495 	}
496 
497 	for (int32 i = 0; i < 30; i++) {
498 		script->regs[i] = stream->readSint16LE();
499 	}
500 
501 	script->retValue = stream->readSint16LE();
502 	script->running = stream->readByte();
503 }
504 
505 } // End of namespace Toon
506