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  * Virtual processor.
22  */
23 
24 #include "tinsel/dw.h"
25 #include "tinsel/drives.h"
26 #include "tinsel/events.h"	// 'POINTED' etc.
27 #include "tinsel/handle.h"	// LockMem()
28 #include "tinsel/dialogs.h"	// for inventory id's
29 #include "tinsel/pcode.h"	// opcodes etc.
30 #include "tinsel/scn.h"	// FindChunk()
31 #include "common/serializer.h"
32 #include "tinsel/timers.h"
33 #include "tinsel/tinlib.h"	// Library routines
34 #include "tinsel/tinsel.h"
35 
36 #include "common/textconsole.h"
37 #include "common/util.h"
38 
39 namespace Tinsel {
40 
41 //----------------- EXTERN FUNCTIONS --------------------
42 
43 extern int CallLibraryRoutine(CORO_PARAM, int operand, int32 *pp, const INT_CONTEXT *pic, RESUME_STATE *pResumeState);
44 
45 //----------------- LOCAL DEFINES --------------------
46 
47 #define	GLOBALS_FILENAME	"gdata"		// name of globals file
48 
49 /** list of all opcodes */
50 enum OPCODE {
51 	OP_HALT = 0,	///< end of program
52 	OP_IMM = 1,		///< loads signed immediate onto stack
53 	OP_ZERO = 2,	///< loads zero onto stack
54 	OP_ONE = 3,		///< loads one onto stack
55 	OP_MINUSONE = 4,	///< loads minus one onto stack
56 	OP_STR = 5,		///< loads string offset onto stack
57 	OP_FILM = 6,	///< loads film offset onto stack
58 	OP_FONT = 7,	///< loads font offset onto stack
59 	OP_PAL = 8,		///< loads palette offset onto stack
60 	OP_LOAD = 9,	///< loads local variable onto stack
61 	OP_GLOAD = 10,	///< loads global variable onto stack - long offset to variable
62 	OP_STORE = 11,	///< pops stack and stores in local variable - long offset to variable
63 	OP_GSTORE = 12,	///< pops stack and stores in global variable - long offset to variable
64 	OP_CALL = 13,	///< procedure call
65 	OP_LIBCALL = 14,	///< library procedure call - long offset to procedure
66 	OP_RET = 15,		///< procedure return
67 	OP_ALLOC = 16,	///< allocate storage on stack
68 	OP_JUMP = 17,	///< unconditional jump	- signed word offset
69 	OP_JMPFALSE = 18,	///< conditional jump	- signed word offset
70 	OP_JMPTRUE = 19,	///< conditional jump	- signed word offset
71 	OP_EQUAL = 20,	///< tests top two items on stack for equality
72 	OP_LESS,	///< tests top two items on stack
73 	OP_LEQUAL,	///< tests top two items on stack
74 	OP_NEQUAL,	///< tests top two items on stack
75 	OP_GEQUAL,	///< tests top two items on stack
76 	OP_GREAT = 25,	///< tests top two items on stack
77 	OP_PLUS,	///< adds top two items on stack and replaces with result
78 	OP_MINUS,	///< subs top two items on stack and replaces with result
79 	OP_LOR,		///< logical or of top two items on stack and replaces with result
80 	OP_MULT,	///< multiplies top two items on stack and replaces with result
81 	OP_DIV = 30,		///< divides top two items on stack and replaces with result
82 	OP_MOD,		///< divides top two items on stack and replaces with modulus
83 	OP_AND,		///< bitwise ands top two items on stack and replaces with result
84 	OP_OR,		///< bitwise ors top two items on stack and replaces with result
85 	OP_EOR,		///< bitwise exclusive ors top two items on stack and replaces with result
86 	OP_LAND = 35,	///< logical ands top two items on stack and replaces with result
87 	OP_NOT,		///< logical nots top item on stack
88 	OP_COMP,	///< complements top item on stack
89 	OP_NEG,		///< negates top item on stack
90 	OP_DUP,		///< duplicates top item on stack
91 	OP_ESCON = 40,	///< start of escapable sequence
92 	OP_ESCOFF = 41,	///< end of escapable sequence
93 	OP_CIMM,	///< loads signed immediate onto stack (special to case statements)
94 	OP_CDFILM	///< loads film offset onto stack but not in current scene
95 };
96 
97 // modifiers for the above opcodes
98 #define	OPSIZE8		0x40	///< when this bit is set - the operand size is 8 bits
99 #define	OPSIZE16	0x80	///< when this bit is set - the operand size is 16 bits
100 
101 #define	OPMASK		0x3F	///< mask to isolate the opcode
102 
103 bool g_bNoPause = false;
104 
105 //----------------- LOCAL GLOBAL DATA --------------------
106 
107 // FIXME: Avoid non-const global vars
108 
109 static int32 *g_pGlobals = 0;		// global vars
110 
111 static int g_numGlobals = 0;		// How many global variables to save/restore
112 
113 static INT_CONTEXT *g_icList = 0;
114 
115 static uint32 g_hMasterScript;
116 
117 //----------------- SCRIPT BUGS WORKAROUNDS --------------
118 
119 /**
120  * This structure is used to introduce bug fixes into the scripts used by the games.
121  */
122 struct WorkaroundEntry {
123 	TinselEngineVersion version;	///< Engine version this workaround applies to
124 	bool scnFlag;					///< Only applicable for Tinsel 1 (DW 1)
125 	bool isDemo;					///< Flags whether it's for a demo
126 	Common::Platform platform;		///< Platform filter
127 	SCNHANDLE hCode;				///< Script to apply fragment to
128 	int ip;							///< Script offset to run this fragment before
129 	int numBytes;					///< Number of bytes in the script
130 	const byte *script;				///< Instruction(s) to execute
131 };
132 
133 #define FRAGMENT_WORD(x)	(byte)(x & 0xFF), (byte)(x >> 8)
134 #define FRAGMENT_DWORD(x)	(byte)(x & 0xFF), (byte)(x >> 8), (byte)(x >> 16), (byte)(x >> 24)
135 
136 static const byte fragment1[] = {OP_ZERO, OP_GSTORE | OPSIZE16, 206, 0};
137 static const byte fragment2[] = {OP_LIBCALL | OPSIZE8, 110};
138 static const byte fragment3[] = {OP_ZERO, OP_GSTORE | OPSIZE16, FRAGMENT_WORD(490)};
139 static const byte fragment4[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(900), OP_JUMP | OPSIZE16, FRAGMENT_WORD(466)};
140 static const byte fragment5[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(901), OP_JUMP | OPSIZE16, FRAGMENT_WORD(488)};
141 static const byte fragment6[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(903), OP_JUMP | OPSIZE16, FRAGMENT_WORD(516)};
142 static const byte fragment7[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(908), OP_JUMP | OPSIZE16, FRAGMENT_WORD(616)};
143 static const byte fragment8[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(910), OP_JUMP | OPSIZE16, FRAGMENT_WORD(644)};
144 static const byte fragment9[] = {OP_JUMP | OPSIZE8, 123};
145 static const byte fragment10[] = {OP_IMM | OPSIZE16, FRAGMENT_WORD(160), OP_JUMP | OPSIZE16, FRAGMENT_WORD(136)};
146 static const byte fragment11[] = {OP_JMPTRUE | OPSIZE16, FRAGMENT_WORD(1572),
147 		OP_ONE, OP_LIBCALL | OPSIZE8, 14,									// Re-show the cursor
148 		OP_IMM | OPSIZE16, FRAGMENT_WORD(322), OP_LIBCALL | OPSIZE8, 46,	// Give back the whistle
149 		OP_JUMP | OPSIZE16, FRAGMENT_WORD(1661)};
150 static const byte fragment12[] = {OP_JMPTRUE | OPSIZE16, FRAGMENT_WORD(1491),
151 		OP_ONE, OP_LIBCALL | OPSIZE8, 14,									// Re-show the cursor
152 		OP_IMM | OPSIZE16, FRAGMENT_WORD(322), OP_LIBCALL | OPSIZE8, 46,	// Give back the whistle
153 		OP_JUMP | OPSIZE16, FRAGMENT_WORD(1568)};
154 static const byte fragment13[] = {OP_ZERO, OP_GSTORE | OPSIZE16, FRAGMENT_WORD(306)};
155 static const byte fragment14[] = {OP_LIBCALL | OPSIZE8, 58,
156 		OP_IMM, FRAGMENT_DWORD((42 << 23)), OP_ONE, OP_ZERO, OP_LIBCALL | OPSIZE8, 44,
157 		OP_LIBCALL | OPSIZE8, 97, OP_JUMP | OPSIZE16, FRAGMENT_WORD(2220)
158 };
159 static const byte fragment15[] = { OP_JMPFALSE | OPSIZE16, FRAGMENT_WORD(154) };
160 
161 #undef FRAGMENT_WORD
162 
163 const WorkaroundEntry workaroundList[] = {
164 	// DW1-SCN: Global 206 is whether Rincewind is trying to take the
165 	// book back to the present. In the GRA version, it was global 373,
166 	// and was reset when he is returned to the past, but was forgotten
167 	// in the SCN version, so this ensures the flag is properly reset.
168 	{TINSEL_V1, true, false, Common::kPlatformUnknown, 427942095, 1, sizeof(fragment1), fragment1},
169 
170 	// DW1-GRA: Rincewind exiting the Inn is blocked by the luggage.
171 	// Whilst you can then move into walkable areas, saving and
172 	// restoring the game, it will error if you try to move. This
173 	// fragment turns off NPC blocking for the Outside Inn rooms so that
174 	// the luggage won't block Past Outside Inn.
175 	// See bug report #2525010.
176 	{TINSEL_V1, false, false, Common::kPlatformUnknown, 444622076, 0,  sizeof(fragment2), fragment2},
177 	// Present Outside Inn
178 	{TINSEL_V1, false, false, Common::kPlatformUnknown, 352600876, 0,  sizeof(fragment2), fragment2},
179 
180 	// DW1-GRA: Talking to palace guards in Act 2 gives !!!HIGH
181 	// STRING||| - this happens if you initiate dialog with one of the
182 	// guards, but not the other. So these fragments provide the correct
183 	// talk parameters where needed.
184 	// See bug report #2831159.
185 	{TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 463, sizeof(fragment4), fragment4},
186 	{TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 485, sizeof(fragment5), fragment5},
187 	{TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 513, sizeof(fragment6), fragment6},
188 	{TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 613, sizeof(fragment7), fragment7},
189 	{TINSEL_V1, false, false, Common::kPlatformUnknown, 310506872, 641, sizeof(fragment8), fragment8},
190 
191 	// DW1-SCN: The script for the lovable street-Starfish does a
192 	// 'StopSample' after flicking the coin to ensure it's sound is
193 	// stopped, but which also accidentally can stop any active
194 	// conversation with the Amazon.
195 	{TINSEL_V1, true, false, Common::kPlatformUnknown, 394640351, 121, sizeof(fragment9), fragment9},
196 
197 	// DW2: In the garden, global #490 is set when the bees begin their
198 	// 'out of hive' animation, and reset when done. But if the game is
199 	// saved/restored during it, the animation sequence is reset without
200 	// the global being cleared. This causes bugs in several actions
201 	// which try to disable the bees animation, since they wait
202 	// indefinitely for the global to be cleared, incorrectly believing
203 	// the animation is currently playing. This includes:
204 	//  * Giving the brochure to the beekeeper (bug #2680397)
205 	//  * Stealing the mallets from the wizards (bug #2820788).
206 	// This fix ensures that the global is reset when the Garden scene
207 	// is loaded (both entering and restoring a game).
208 	{TINSEL_V2, true, false, Common::kPlatformUnknown, 2888147476U, 0, sizeof(fragment3), fragment3},
209 
210 	// DW1-GRA: Corrects text being drawn partially off-screen during
211 	// the blackboard description of the Librarian.
212 	{TINSEL_V1, false, false, Common::kPlatformUnknown, 293831402, 133, sizeof(fragment10), fragment10},
213 
214 	// DW1-GRA/SCN: Corrects the dead-end of being able to give the
215 	// whistle back to the pirate before giving him the parrot.
216 	// See bug report #2934211.
217 	{TINSEL_V1, true, false, Common::kPlatformUnknown, 352601285, 1569, sizeof(fragment11), fragment11},
218 	{TINSEL_V1, false, false, Common::kPlatformUnknown, 352602304, 1488, sizeof(fragment12), fragment12},
219 
220 	// DW2: Corrects a bug with global 306 not being cleared if you leave
221 	// the marketplace scene whilst D'Blah is talking (even if it's not
222 	// actually audible); returning to the scene and clicking on him multiple
223 	// times would cause the game to crash
224 	{TINSEL_V2, true, false, Common::kPlatformUnknown, 1109294728, 0, sizeof(fragment13), fragment13},
225 
226 	// DW1 PSX DEMO: Alters a script in the PSX DW1 demo to show the Idle animation scene rather than
227 	// quitting the game when no user input happens for a while
228 	{TINSEL_V1, true, true, Common::kPlatformPSX, 0, 2186, sizeof(fragment14), fragment14},
229 
230 	// DW1-GRA: Fixes hang in Temple, when trying to use items on the big hammer
231 	{TINSEL_V1, false, false, Common::kPlatformUnknown, 276915849, 0x98, sizeof(fragment15), fragment15},
232 
233 	{TINSEL_V0, false, false, Common::kPlatformUnknown, 0, 0, 0, NULL}
234 };
235 
236 //----------------- LOCAL GLOBAL DATA --------------------
237 
238 /**
239  * Keeps the code array pointer up to date.
240  */
LockCode(INT_CONTEXT * ic)241 void LockCode(INT_CONTEXT *ic) {
242 	if (ic->GSort == GS_MASTER) {
243 		if (TinselV2)
244 			// Get the srcipt handle from a specific global chunk
245 			ic->code = (byte *)LockMem(g_hMasterScript);
246 		else
247 			ic->code = (byte *)FindChunk(MASTER_SCNHANDLE, CHUNK_PCODE);
248 	} else
249 		ic->code = (byte *)LockMem(ic->hCode);
250 }
251 
252 /**
253  * Find a free interpret context and allocate it to the calling process.
254  */
AllocateInterpretContext(GSORT gsort)255 static INT_CONTEXT *AllocateInterpretContext(GSORT gsort) {
256 	INT_CONTEXT *pic;
257 	int	i;
258 
259 	for (i = 0, pic = g_icList; i < NUM_INTERPRET; i++, pic++) {
260 		if (pic->GSort == GS_NONE) {
261 			pic->pProc = CoroScheduler.getCurrentProcess();
262 			pic->GSort = gsort;
263 			return pic;
264 		}
265 #ifdef DEBUG
266 		else {
267 			if (pic->pProc == CoroScheduler.getCurrentProcess())
268 				error("Found unreleased interpret context");
269 		}
270 #endif
271 	}
272 
273 	error("Out of interpret contexts");
274 }
275 
FreeWaitCheck(PINT_CONTEXT pic,bool bVoluntary)276 static void FreeWaitCheck(PINT_CONTEXT pic, bool bVoluntary) {
277 	int i;
278 
279 	// Is this waiting for something?
280 	if (pic->waitNumber1) {
281 		for (i = 0; i < NUM_INTERPRET; i++) {
282 			if ((g_icList + i)->waitNumber2 == pic->waitNumber1) {
283 				(g_icList + i)->waitNumber2 = 0;
284 				break;
285 			}
286 		}
287 	}
288 
289 	// Is someone waiting for this?
290 	if (pic->waitNumber2) {
291 		for (i = 0; i < NUM_INTERPRET; i++) {
292 			if ((g_icList + i)->waitNumber1 == pic->waitNumber2) {
293 				(g_icList + i)->waitNumber1 = 0;
294 				(g_icList + i)->resumeCode = bVoluntary ? RES_FINISHED : RES_CUTSHORT;
295 				CoroScheduler.reschedule((g_icList + i)->pProc);
296 				break;
297 			}
298 		}
299 		assert(i < NUM_INTERPRET);
300 	}
301 }
302 
303 /**
304  * Normal release of an interpret context.
305  * Called from the end of Interpret().
306  */
FreeInterpretContextPi(INT_CONTEXT * pic)307 static void FreeInterpretContextPi(INT_CONTEXT *pic) {
308 	FreeWaitCheck(pic, true);
309 	if (TinselV2)
310 		memset(pic, 0, sizeof(INT_CONTEXT));
311 	pic->GSort = GS_NONE;
312 }
313 
314 /**
315  * Free interpret context owned by a dying process.
316  * Ensures that interpret contexts don't get lost when an Interpret()
317  * call doesn't complete.
318  */
FreeInterpretContextPr(Common::PROCESS * pProc)319 void FreeInterpretContextPr(Common::PROCESS *pProc) {
320 	INT_CONTEXT *pic;
321 	int	i;
322 
323 	for (i = 0, pic = g_icList; i < NUM_INTERPRET; i++, pic++) {
324 		if (pic->GSort != GS_NONE && pic->pProc == pProc) {
325 			FreeWaitCheck(pic, false);
326 			if (TinselV2)
327 				memset(pic, 0, sizeof(INT_CONTEXT));
328 			pic->GSort = GS_NONE;
329 			break;
330 		}
331 	}
332 }
333 
334 /**
335  * Free all interpret contexts except for the master script's
336  */
FreeMostInterpretContexts()337 void FreeMostInterpretContexts() {
338 	INT_CONTEXT *pic;
339 	int	i;
340 
341 	for (i = 0, pic = g_icList; i < NUM_INTERPRET; i++, pic++) {
342 		if ((pic->GSort != GS_MASTER) && (pic->GSort != GS_GPROCESS)) {
343 			memset(pic, 0, sizeof(INT_CONTEXT));
344 			pic->GSort = GS_NONE;
345 		}
346 	}
347 }
348 
349 /**
350  * Free the master script's interpret context.
351  */
FreeMasterInterpretContext()352 void FreeMasterInterpretContext() {
353 	INT_CONTEXT *pic;
354 	int	i;
355 
356 	for (i = 0, pic = g_icList; i < NUM_INTERPRET; i++, pic++)	{
357 		if ((pic->GSort == GS_MASTER) || (pic->GSort == GS_GPROCESS)) {
358 			memset(pic, 0, sizeof(INT_CONTEXT));
359 			pic->GSort = GS_NONE;
360 			return;
361 		}
362 	}
363 }
364 
365 /**
366  * Allocate and initialize an interpret context.
367  * Called from a process prior to Interpret().
368  * @param gsort			which sort of code
369  * @param hCode			Handle to code to execute
370  * @param event			Causal event
371  * @param hpoly			Associated polygon (if any)
372  * @param actorId		Associated actor (if any)
373  * @param pinvo			Associated inventory object
374  */
InitInterpretContext(GSORT gsort,SCNHANDLE hCode,TINSEL_EVENT event,HPOLYGON hpoly,int actorid,INV_OBJECT * pinvo,int myEscape)375 INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode,	TINSEL_EVENT event,
376 		HPOLYGON hpoly, int actorid, INV_OBJECT *pinvo, int myEscape) {
377 	INT_CONTEXT *ic;
378 
379 	ic = AllocateInterpretContext(gsort);
380 
381 	// Previously parameters to Interpret()
382 	ic->hCode = hCode;
383 	LockCode(ic);
384 	ic->event = event;
385 	ic->hPoly = hpoly;
386 	ic->idActor = actorid;
387 	ic->pinvo = pinvo;
388 
389 	// Previously local variables in Interpret()
390 	ic->bHalt = false;		// set to exit interpeter
391 	ic->escOn = myEscape > 0;
392 	ic->myEscape = myEscape;
393 	ic->sp = 0;
394 	ic->bp = ic->sp + 1;
395 	ic->ip = 0;			// start of code
396 
397 	ic->resumeState = RES_NOT;
398 
399 	return ic;
400 }
401 
402 /**
403  * Allocate and initialize an interpret context with restored data.
404  */
RestoreInterpretContext(INT_CONTEXT * ric)405 INT_CONTEXT *RestoreInterpretContext(INT_CONTEXT *ric) {
406 	INT_CONTEXT *ic;
407 
408 	ic = AllocateInterpretContext(GS_NONE);	// Sort will soon be overridden
409 
410 	memcpy(ic, ric, sizeof(INT_CONTEXT));
411 	ic->pProc = CoroScheduler.getCurrentProcess();
412 	ic->resumeState = RES_1;
413 
414 	LockCode(ic);
415 
416 	return ic;
417 }
418 
419 /**
420  * Allocates enough RAM to hold the global Glitter variables.
421  */
RegisterGlobals(int num)422 void RegisterGlobals(int num) {
423 	if (g_pGlobals == NULL) {
424 		g_numGlobals = num;
425 
426 		g_hMasterScript = !TinselV2 ? 0 :
427 			READ_32(FindChunk(MASTER_SCNHANDLE, CHUNK_MASTER_SCRIPT));
428 
429 		// Allocate RAM for pGlobals and make sure it's allocated
430 		g_pGlobals = (int32 *)calloc(g_numGlobals, sizeof(int32));
431 		if (g_pGlobals == NULL) {
432 			error("Cannot allocate memory for global data");
433 		}
434 
435 		// Allocate RAM for interpret contexts and make sure it's allocated
436 		g_icList = (INT_CONTEXT *)calloc(NUM_INTERPRET, sizeof(INT_CONTEXT));
437 		if (g_icList == NULL) {
438 			error("Cannot allocate memory for interpret contexts");
439 		}
440 		CoroScheduler.setResourceCallback(FreeInterpretContextPr);
441 	} else {
442 		// Check size is still the same
443 		assert(g_numGlobals == num);
444 
445 		memset(g_pGlobals, 0, g_numGlobals * sizeof(int32));
446 		memset(g_icList, 0, NUM_INTERPRET * sizeof(INT_CONTEXT));
447 	}
448 
449 	if (TinselV2) {
450 		// read initial values
451 		CdCD(Common::nullContext);
452 
453 		Common::File f;
454 		if (!f.open(GLOBALS_FILENAME))
455 			error(CANNOT_FIND_FILE, GLOBALS_FILENAME);
456 
457 		int32 length = f.readSint32LE();
458 		if (length != num)
459 			error(FILE_IS_CORRUPT, GLOBALS_FILENAME);
460 
461 		for (int i = 0; i < length; ++i)
462 			g_pGlobals[i] = f.readSint32LE();
463 
464 		if (f.eos() || f.err())
465 			error(FILE_IS_CORRUPT, GLOBALS_FILENAME);
466 
467 		f.close();
468 	}
469 }
470 
FreeGlobals()471 void FreeGlobals() {
472 	free(g_pGlobals);
473 	g_pGlobals = NULL;
474 
475 	free(g_icList);
476 	g_icList = NULL;
477 }
478 
479 /**
480  * (Un)serialize the global data for save/restore game.
481  */
syncGlobInfo(Common::Serializer & s)482 void syncGlobInfo(Common::Serializer &s) {
483 	for (int i = 0; i < g_numGlobals; i++) {
484 		s.syncAsSint32LE(g_pGlobals[i]);
485 	}
486 }
487 
488 /**
489  * (Un)serialize an interpreter context for save/restore game.
490  */
syncWithSerializer(Common::Serializer & s)491 void INT_CONTEXT::syncWithSerializer(Common::Serializer &s) {
492 	if (s.isLoading()) {
493 		// Null out the pointer fields
494 		pProc = NULL;
495 		code = NULL;
496 		pinvo = NULL;
497 	}
498 	// Write out used fields
499 	s.syncAsUint32LE(GSort);
500 	s.syncAsUint32LE(hCode);
501 	s.syncAsUint32LE(event);
502 	s.syncAsSint32LE(hPoly);
503 	s.syncAsSint32LE(idActor);
504 
505 	for (int i = 0; i < PCODE_STACK_SIZE; ++i)
506 		s.syncAsSint32LE(stack[i]);
507 
508 	s.syncAsSint32LE(sp);
509 	s.syncAsSint32LE(bp);
510 	s.syncAsSint32LE(ip);
511 	s.syncAsUint32LE(bHalt);
512 	s.syncAsUint32LE(escOn);
513 	s.syncAsSint32LE(myEscape);
514 }
515 
516 /**
517  * Return pointer to and size of global data for save/restore game.
518  */
SaveInterpretContexts(INT_CONTEXT * sICInfo)519 void SaveInterpretContexts(INT_CONTEXT *sICInfo) {
520 	memcpy(sICInfo, g_icList, NUM_INTERPRET * sizeof(INT_CONTEXT));
521 }
522 
523 /**
524  * Fetches up to 4 bytes from the code script
525  */
GetBytes(const byte * scriptCode,const WorkaroundEntry * & wkEntry,int & ip,uint numBytes)526 static int32 GetBytes(const byte *scriptCode, const WorkaroundEntry* &wkEntry, int &ip, uint numBytes) {
527 	assert(numBytes <= 4 && numBytes != 3);
528 	const byte *code = scriptCode;
529 
530 	if (wkEntry != NULL) {
531 		if (ip >= wkEntry->numBytes) {
532 			// Finished the workaround
533 			ip = wkEntry->ip;
534 			wkEntry = NULL;
535 		} else {
536 			code = wkEntry->script;
537 		}
538 	}
539 
540 	uint32 tmp;
541 	switch (numBytes) {
542 	case 0:
543 		// Instruction byte
544 		tmp = code[ip++ * (TinselV0 ? 4 : 1)];
545 		break;
546 	case 1:
547 		// Fetch and sign extend a 8 bit value to 32 bits.
548 		tmp = (int8)code[ip++];
549 		break;
550 	case 2:
551 		// Fetch and sign extend a 16 bit value to 32 bits.
552 		tmp = (int16)READ_LE_UINT16(code + ip);
553 		ip += 2;
554 		break;
555 	default:
556 		if (TinselV0)
557 			tmp = (int32)READ_LE_UINT32(code + ip++ * 4);
558 		else {
559 			tmp = (int32)READ_LE_UINT32(code + ip);
560 			ip += 4;
561 		}
562 		break;
563 	}
564 
565 	return tmp;
566 }
567 
568 /**
569  * Fetch (and sign extend, if necessary) a 8/16/32 bit value from the code
570  * stream and advance the instruction pointer accordingly.
571  */
Fetch(byte opcode,const byte * code,const WorkaroundEntry * & wkEntry,int & ip)572 static int32 Fetch(byte opcode, const byte *code, const WorkaroundEntry* &wkEntry, int &ip) {
573 	if (TinselV0)
574 		// Fetch a 32 bit value.
575 		return GetBytes(code, wkEntry, ip, 4);
576 	else if (opcode & OPSIZE8)
577 		// Fetch and sign extend a 8 bit value to 32 bits.
578 		return GetBytes(code, wkEntry, ip, 1);
579 	else if (opcode & OPSIZE16)
580 		return GetBytes(code, wkEntry, ip, 2);
581 
582 	return GetBytes(code, wkEntry, ip, 4);
583 }
584 
585 /**
586  * Interprets the PCODE instructions in the code array.
587  */
Interpret(CORO_PARAM,INT_CONTEXT * ic)588 void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
589 	do {
590 		int tmp, tmp2;
591 		int ip = ic->ip;
592 		const WorkaroundEntry *wkEntry = ic->fragmentPtr;
593 
594 		if (wkEntry == NULL) {
595 			// Check to see if a workaround fragment needs to be executed
596 			for (wkEntry = workaroundList; wkEntry->script != NULL; ++wkEntry) {
597 				if ((wkEntry->version == TinselVersion) &&
598 					(wkEntry->hCode == ic->hCode) &&
599 					(wkEntry->ip == ip) &&
600 					(wkEntry->isDemo == _vm->getIsADGFDemo()) &&
601 					((wkEntry->platform == Common::kPlatformUnknown) || (wkEntry->platform == _vm->getPlatform())) &&
602 					(!TinselV1 || (wkEntry->scnFlag == ((_vm->getFeatures() & GF_SCNFILES) != 0)))) {
603 					// Point to start of workaround fragment
604 					ip = 0;
605 					break;
606 				}
607 			}
608 			if (wkEntry->script == NULL)
609 				wkEntry = NULL;
610 		}
611 
612 		byte opcode = (byte)GetBytes(ic->code, wkEntry, ip, 0);
613 		if (TinselV0 && ((opcode & OPMASK) > OP_IMM))
614 			opcode += 3;
615 
616 		debug(7, "ip=%d  Opcode %d (-> %d)", ic->ip, opcode, opcode & OPMASK);
617 		switch (opcode & OPMASK) {
618 		case OP_HALT:			// end of program
619 
620 			ic->bHalt = true;
621 			break;
622 
623 		case OP_IMM:			// loads immediate data onto stack
624 		case OP_STR:			// loads string handle onto stack
625 		case OP_FILM:			// loads film handle onto stack
626 		case OP_CDFILM:			// loads film handle onto stack
627 		case OP_FONT:			// loads font handle onto stack
628 		case OP_PAL:			// loads palette handle onto stack
629 
630 			ic->stack[++ic->sp] = Fetch(opcode, ic->code, wkEntry, ip);
631 			break;
632 
633 		case OP_ZERO:			// loads zero onto stack
634 			ic->stack[++ic->sp] = 0;
635 			break;
636 
637 		case OP_ONE:			// loads one onto stack
638 			ic->stack[++ic->sp] = 1;
639 			break;
640 
641 		case OP_MINUSONE:		// loads minus one onto stack
642 			ic->stack[++ic->sp] = -1;
643 			break;
644 
645 		case OP_LOAD:			// loads local variable onto stack
646 
647 			ic->stack[++ic->sp] = ic->stack[ic->bp + Fetch(opcode, ic->code, wkEntry, ip)];
648 			break;
649 
650 		case OP_GLOAD:				// loads global variable onto stack
651 
652 			tmp = Fetch(opcode, ic->code, wkEntry, ip);
653 			assert(0 <= tmp && tmp < g_numGlobals);
654 			ic->stack[++ic->sp] = g_pGlobals[tmp];
655 			break;
656 
657 		case OP_STORE:				// pops stack and stores in local variable
658 
659 			ic->stack[ic->bp + Fetch(opcode, ic->code, wkEntry, ip)] = ic->stack[ic->sp--];
660 			break;
661 
662 		case OP_GSTORE:				// pops stack and stores in global variable
663 
664 			tmp = Fetch(opcode, ic->code, wkEntry, ip);
665 			assert(0 <= tmp && tmp < g_numGlobals);
666 			g_pGlobals[tmp] = ic->stack[ic->sp--];
667 			break;
668 
669 		case OP_CALL:				// procedure call
670 
671 			tmp = Fetch(opcode, ic->code, wkEntry, ip);
672 			//assert(0 <= tmp && tmp < codeSize);	// TODO: Verify jumps are not out of bounds
673 			ic->stack[ic->sp + 1] = 0;	// static link
674 			ic->stack[ic->sp + 2] = ic->bp;	// dynamic link
675 			ic->stack[ic->sp + 3] = ip;	// return address
676 			ic->bp = ic->sp + 1;		// set new base pointer
677 			ip = tmp;	// set ip to procedure address
678 			break;
679 
680 		case OP_LIBCALL:		// library procedure or function call
681 
682 			tmp = Fetch(opcode, ic->code, wkEntry, ip);
683 			// NOTE: Interpret() itself is not using the coroutine facilities,
684 			// but still accepts a CORO_PARAM, so from the outside it looks
685 			// like a coroutine. In fact it may still acts as a kind of "proxy"
686 			// for some underlying coroutine. To enable this, we just pass on
687 			// 'coroParam' to CallLibraryRoutine(). If we then detect that
688 			// coroParam was set to a non-zero value, this means that some
689 			// coroutine code did run at some point, and we are now supposed
690 			// to sleep or die -- hence, we 'return' if coroParam != 0.
691 			//
692 			// This works because Interpret() is fully re-entrant: If we return
693 			// now and are later called again, then we will end up in the very
694 			// same spot (i.e. here).
695 			//
696 			// The reasons we do it this way, instead of turning Interpret into
697 			// a 'proper' coroutine are (1) we avoid implementation problems
698 			// (CORO_INVOKE involves adding 'case' statements, but Interpret
699 			// already has a huge switch/case, so that would not work out of the
700 			// box), (2) we incurr less overhead, (3) it's easier to debug,
701 			// (4) it's simply cool ;).
702 			tmp2 = CallLibraryRoutine(coroParam, tmp, &ic->stack[ic->sp], ic, &ic->resumeState);
703 			if (coroParam)
704 				return;
705 			if (!TinselV0)
706 				ic->sp += tmp2;
707 			LockCode(ic);
708 			if (TinselV2 && (ic->resumeState == RES_1))
709 				ic->resumeState = RES_NOT;
710 			break;
711 
712 		case OP_RET:			// procedure return
713 
714 			ic->sp = ic->bp - 1;		// restore stack
715 			ip = ic->stack[ic->sp + 3];	// return address
716 			ic->bp = ic->stack[ic->sp + 2];	// restore previous base pointer
717 			break;
718 
719 		case OP_ALLOC:			// allocate storage on stack
720 
721 			ic->sp += (int32)Fetch(opcode, ic->code, wkEntry, ip);
722 			break;
723 
724 		case OP_JUMP:	// unconditional jump
725 
726 			ip = Fetch(opcode, ic->code, wkEntry, ip);
727 			wkEntry = NULL;					// In case a jump occurs from a workaround
728 			break;
729 
730 		case OP_JMPFALSE:	// conditional jump
731 
732 			tmp = Fetch(opcode, ic->code, wkEntry, ip);
733 			if (ic->stack[ic->sp--] == 0) {
734 				// condition satisfied - do the jump
735 				ip = tmp;
736 				wkEntry = NULL;					// In case a jump occurs from a workaround
737 			}
738 			break;
739 
740 		case OP_JMPTRUE:	// conditional jump
741 
742 			tmp = Fetch(opcode, ic->code, wkEntry, ip);
743 			if (ic->stack[ic->sp--] != 0) {
744 				// condition satisfied - do the jump
745 				ip = tmp;
746 				wkEntry = NULL;					// In case a jump occurs from a workaround
747 			}
748 			break;
749 
750 		case OP_EQUAL:			// tests top two items on stack for equality
751 		case OP_LESS:			// tests top two items on stack
752 		case OP_LEQUAL:			// tests top two items on stack
753 		case OP_NEQUAL:			// tests top two items on stack
754 		case OP_GEQUAL:			// tests top two items on stack
755 		case OP_GREAT:			// tests top two items on stack
756 		case OP_LOR:			// logical or of top two items on stack and replaces with result
757 		case OP_LAND:			// logical ands top two items on stack and replaces with result
758 
759 			// pop one operand
760 			ic->sp--;
761 			assert(ic->sp >= 0);
762 			tmp = ic->stack[ic->sp];
763 			tmp2 = ic->stack[ic->sp + 1];
764 
765 			// replace other operand with result of operation
766 			switch (opcode) {
767 			case OP_EQUAL:  tmp = (tmp == tmp2); break;
768 			case OP_LESS:   tmp = (tmp <  tmp2); break;
769 			case OP_LEQUAL: tmp = (tmp <= tmp2); break;
770 			case OP_NEQUAL: tmp = (tmp != tmp2); break;
771 			case OP_GEQUAL: tmp = (tmp >= tmp2); break;
772 			case OP_GREAT:  tmp = (tmp >  tmp2); break;
773 
774 			case OP_LOR:    tmp = (tmp || tmp2); break;
775 			case OP_LAND:   tmp = (tmp && tmp2); break;
776 			}
777 
778 			ic->stack[ic->sp] = tmp;
779 			break;
780 
781 		case OP_PLUS:			// adds top two items on stack and replaces with result
782 		case OP_MINUS:			// subs top two items on stack and replaces with result
783 		case OP_MULT:			// multiplies top two items on stack and replaces with result
784 		case OP_DIV:			// divides top two items on stack and replaces with result
785 		case OP_MOD:			// divides top two items on stack and replaces with modulus
786 		case OP_AND:			// bitwise ands top two items on stack and replaces with result
787 		case OP_OR:				// bitwise ors top two items on stack and replaces with result
788 		case OP_EOR:			// bitwise exclusive ors top two items on stack and replaces with result
789 
790 			// pop one operand
791 			ic->sp--;
792 			assert(ic->sp >= 0);
793 			tmp = ic->stack[ic->sp];
794 			tmp2 = ic->stack[ic->sp + 1];
795 
796 			// replace other operand with result of operation
797 			switch (opcode) {
798 			case OP_PLUS:   tmp += tmp2; break;
799 			case OP_MINUS:  tmp -= tmp2; break;
800 			case OP_MULT:   tmp *= tmp2; break;
801 			case OP_DIV:    tmp /= tmp2; break;
802 			case OP_MOD:    tmp %= tmp2; break;
803 			case OP_AND:    tmp &= tmp2; break;
804 			case OP_OR:     tmp |= tmp2; break;
805 			case OP_EOR:    tmp ^= tmp2; break;
806 			}
807 			ic->stack[ic->sp] = tmp;
808 			break;
809 
810 		case OP_NOT:			// logical nots top item on stack
811 
812 			ic->stack[ic->sp] = !ic->stack[ic->sp];
813 			break;
814 
815 		case OP_COMP:			// complements top item on stack
816 			ic->stack[ic->sp] = ~ic->stack[ic->sp];
817 			break;
818 
819 		case OP_NEG:			// negates top item on stack
820 			ic->stack[ic->sp] = -ic->stack[ic->sp];
821 			break;
822 
823 		case OP_DUP:			// duplicates top item on stack
824 			ic->stack[ic->sp + 1] = ic->stack[ic->sp];
825 			ic->sp++;
826 			break;
827 
828 		case OP_ESCON:
829 			g_bNoPause = true;
830 			ic->escOn = true;
831 			ic->myEscape = GetEscEvents();
832 			break;
833 
834 		case OP_ESCOFF:
835 			ic->escOn = false;
836 			ic->myEscape = 0;
837 			break;
838 
839 		default:
840 			error("Interpret() - Unknown opcode");
841 		}
842 
843 		// check for stack under-overflow
844 		assert(ic->sp >= 0 && ic->sp < PCODE_STACK_SIZE);
845 		ic->ip = ip;
846 		ic->fragmentPtr = wkEntry;
847 	} while (!ic->bHalt);
848 
849 	// make sure stack is unwound
850 	assert(ic->sp == 0);
851 
852 	FreeInterpretContextPi(ic);
853 }
854 
855 /**
856  * Associates an interpret context with the
857  * process that will run it.
858  */
AttachInterpret(INT_CONTEXT * pic,Common::PROCESS * pProc)859 void AttachInterpret(INT_CONTEXT *pic, Common::PROCESS *pProc) {
860 	// Attach the process which is using this context
861 	pic->pProc = pProc;
862 }
863 
864 /**
865  * Generate a number that isn't being used.
866  */
UniqueWaitNumber()867 static uint32 UniqueWaitNumber() {
868 	uint32 retval;
869 	int i;
870 
871 	for (retval = DwGetCurrentTime(); 1; retval--) {
872 		if (retval == 0)
873 			retval = (uint32)-1;
874 
875 		for (i = 0; i < NUM_INTERPRET; i++) {
876 			if ((g_icList+i)->waitNumber1 == retval
877 			 || (g_icList+i)->waitNumber2 == retval)
878 				break;
879 		}
880 
881 		if (i == NUM_INTERPRET)
882 			return retval;
883 	}
884 }
885 
886 /**
887  * WaitInterpret
888  */
WaitInterpret(CORO_PARAM,Common::PPROCESS pWaitProc,bool * result)889 void WaitInterpret(CORO_PARAM, Common::PPROCESS pWaitProc, bool *result) {
890 	int i;
891 	Common::PPROCESS currentProcess = CoroScheduler.getCurrentProcess();
892 	assert(currentProcess);
893 	assert(currentProcess != pWaitProc);
894 	if (result) *result = false;
895 
896 	/*
897 	 * Calling process is the waiter, find its interpret context.
898 	 */
899 
900 	CORO_BEGIN_CONTEXT;
901 		PINT_CONTEXT picWaiter, picWaitee;
902 	CORO_END_CONTEXT(_ctx);
903 
904 
905 	CORO_BEGIN_CODE(_ctx);
906 
907 	for (i = 0, _ctx->picWaiter = g_icList; i < NUM_INTERPRET; i++, _ctx->picWaiter++) {
908 		if (_ctx->picWaiter->GSort != GS_NONE && _ctx->picWaiter->pProc == currentProcess) {
909 			break;
910 		}
911 	}
912 
913 	/*
914 	 * Find the interpret context of the process we're waiting for
915 	 */
916 	for (i = 0, _ctx->picWaitee = g_icList; i < NUM_INTERPRET; i++, _ctx->picWaitee++) {
917 		if (_ctx->picWaitee->GSort != GS_NONE && _ctx->picWaitee->pProc == pWaitProc) {
918 			break;
919 		}
920 	}
921 
922 	/*
923 	 * Set the first as waiting for the second
924 	 */
925 	assert(_ctx->picWaitee->waitNumber2 == 0);
926 	_ctx->picWaiter->waitNumber1 = _ctx->picWaitee->waitNumber2 = UniqueWaitNumber();
927 	_ctx->picWaiter->resumeCode = RES_WAITING;
928 
929 	/*
930 	 * Wait for it
931 	 */
932 	CORO_GIVE_WAY;
933 	while (_ctx->picWaiter->resumeCode == RES_WAITING) {
934 		CORO_SLEEP(1);
935 	}
936 
937 	if (result)
938 		*result = (_ctx->picWaiter->resumeCode == RES_FINISHED);
939 	CORO_END_CODE;
940 }
941 
942 /**
943  * CheckOutWaiters
944  */
CheckOutWaiters()945 void CheckOutWaiters() {
946 	int i, j;
947 
948 	// Check all waited for have someone waiting
949 	for (i = 0; i < NUM_INTERPRET; i++)	{
950 		// If someone is supposedly waiting for this one
951 		if ((g_icList + i)->GSort != GS_NONE && (g_icList + i)->waitNumber2) {
952 			// Someone really must be waiting for this one
953 			for (j = 0; j < NUM_INTERPRET; j++) {
954 				if ((g_icList + j)->GSort != GS_NONE
955 				 && (g_icList + j)->waitNumber1 == (g_icList + i)->waitNumber2) {
956 					break;
957 				}
958 			}
959 			assert(j < NUM_INTERPRET);
960 		}
961 	}
962 
963 	// Check waiting for someone to wait for
964 	for (i = 0; i < NUM_INTERPRET; i++) {
965 		// If someone is supposedly waiting for this one
966 		if ((g_icList + i)->GSort != GS_NONE && (g_icList + i)->waitNumber1) {
967 			// Someone really must be waiting for this one
968 			for (j = 0; j < NUM_INTERPRET; j++) {
969 				if ((g_icList + j)->GSort != GS_NONE
970 				 && (g_icList + j)->waitNumber2 == (g_icList + i)->waitNumber1) {
971 					break;
972 				}
973 			}
974 			assert(j < NUM_INTERPRET);
975 		}
976 	}
977 }
978 
979 } // End of namespace Tinsel
980