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