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