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