[Scummvm-cvs-logs] SF.net SVN: scummvm:[42500] scummvm/trunk/engines/tinsel

dreammaster at users.sourceforge.net dreammaster at users.sourceforge.net
Wed Jul 15 10:55:12 CEST 2009


Revision: 42500
          http://scummvm.svn.sourceforge.net/scummvm/?rev=42500&view=rev
Author:   dreammaster
Date:     2009-07-15 08:55:12 +0000 (Wed, 15 Jul 2009)

Log Message:
-----------
Created a system for inserting arbitrary code fragments into game scripts, and added an initial fragment to fix the bug of being stuck in the past in the DW1 SCN version

Modified Paths:
--------------
    scummvm/trunk/engines/tinsel/pcode.cpp
    scummvm/trunk/engines/tinsel/pcode.h

Modified: scummvm/trunk/engines/tinsel/pcode.cpp
===================================================================
--- scummvm/trunk/engines/tinsel/pcode.cpp	2009-07-15 05:00:59 UTC (rev 42499)
+++ scummvm/trunk/engines/tinsel/pcode.cpp	2009-07-15 08:55:12 UTC (rev 42500)
@@ -112,6 +112,21 @@
 
 static uint32 hMasterScript;
 
+//----------------- SCRIPT BUGS WORKAROUNDS --------------
+
+const byte fragment1[] = {(byte)OP_ZERO, (byte) OP_GSTORE | OPSIZE16, 206, 0};
+const int fragment1_size = 4;
+
+const WorkaroundEntry workaroundList[] = {
+	// Global 206 in DW1-SCN is whether Rincewind is trying to take the book back to the present.
+	// In the GRA version, it was global 373, and was reset when he is returned to the past, but 
+	// was forgotten in the SCN version, so this ensures the flag is properly reset
+	{TINSEL_V1, true, 427942095, 1, fragment1_size, fragment1},
+	{TINSEL_V0, false, 0, 0, 0, NULL}
+};
+
+//----------------- LOCAL GLOBAL DATA --------------------
+
 /**
  * Keeps the code array pointer up to date.
  */
@@ -398,38 +413,93 @@
 }
 
 /**
- * Fetch (and sign extend, if necessary) a 8/16/32 bit value from the code
- * stream and advance the instruction pointer accordingly.
+ * Fetches up to 4 bytes from the code script
  */
-static int32 Fetch(byte opcode, byte *code, int &ip) {
-	int32 tmp;
-	if (TinselV0) {
-		// Fetch a 32 bit value.
-		tmp = (int32)READ_LE_UINT32(code + ip++ * 4);
-	} else if (opcode & OPSIZE8) {
+static int32 GetBytes(const byte *scriptCode, const WorkaroundEntry* &wkEntry, int &ip, uint numBytes) {
+	assert(numBytes <= 4 && numBytes != 3);
+	const byte *code = scriptCode;
+
+	if (wkEntry != NULL) {
+		if (ip >= wkEntry->numBytes) {
+			// Finished the workaround
+			ip = wkEntry->ip;
+			wkEntry = NULL;
+		} else {
+			code = wkEntry->script;
+		}
+	}
+
+	uint32 tmp;
+	switch (numBytes) {
+	case 0:
+		// Instruction byte
+		tmp = code[ip++ * (TinselV0 ? 4 : 1)];
+		break;
+	case 1:
 		// Fetch and sign extend a 8 bit value to 32 bits.
-		tmp = *(int8 *)(code + ip);
-		ip += 1;
-	} else if (opcode & OPSIZE16) {
+		tmp = (int8)code[ip++];
+		break;
+	case 2:
 		// Fetch and sign extend a 16 bit value to 32 bits.
 		tmp = (int16)READ_LE_UINT16(code + ip);
 		ip += 2;
-	} else {
-		// Fetch a 32 bit value.
-		tmp = (int32)READ_LE_UINT32(code + ip);
-		ip += 4;
+		break;
+	default:
+		if (TinselV0)
+			tmp = (int32)READ_LE_UINT32(code + ip++ * 4);
+		else {
+			tmp = (int32)READ_LE_UINT32(code + ip);
+			ip += 4;
+		}
+		break;
 	}
+
 	return tmp;
 }
 
 /**
+ * Fetch (and sign extend, if necessary) a 8/16/32 bit value from the code
+ * stream and advance the instruction pointer accordingly.
+ */
+static int32 Fetch(byte opcode, const byte *code, const WorkaroundEntry* &wkEntry, int &ip) {
+	if (TinselV0)
+		// Fetch a 32 bit value.
+		return GetBytes(code, wkEntry, ip, 4);
+	else if (opcode & OPSIZE8)
+		// Fetch and sign extend a 8 bit value to 32 bits.
+		return GetBytes(code, wkEntry, ip, 1);
+	else if (opcode & OPSIZE16)
+		return GetBytes(code, wkEntry, ip, 2);
+	
+	return GetBytes(code, wkEntry, ip, 4);
+}
+
+/**
  * Interprets the PCODE instructions in the code array.
  */
 void Interpret(CORO_PARAM, INT_CONTEXT *ic) {
 	do {
 		int tmp, tmp2;
 		int ip = ic->ip;
-		byte opcode = ic->code[ip++ * (TinselV0 ? 4 : 1)];
+		const WorkaroundEntry *wkEntry = ic->fragmentPtr;
+
+		if (wkEntry == NULL) {
+			// Check to see if a workaround fragment needs to be executed
+			for (wkEntry = workaroundList; wkEntry->script != NULL; ++wkEntry) {
+				if ((wkEntry->version == TinselVersion) && 
+					(wkEntry->hCode == ic->hCode) &&
+					(wkEntry->ip == ip) &&
+					(!TinselV1 || (wkEntry->scnFlag == ((_vm->getFeatures() & GF_SCNFILES) != 0)))) {
+					// Point to start of workaround fragment
+					ip = 0;
+					break;
+				}
+			}
+			if (wkEntry->script == NULL)
+				wkEntry = NULL;
+		}
+
+		byte opcode = (byte)GetBytes(ic->code, wkEntry, ip, 0);
 		if (TinselV0 && ((opcode & OPMASK) > OP_IMM))
 			opcode += 3;
 
@@ -447,7 +517,7 @@
 		case OP_FONT:			// loads font handle onto stack
 		case OP_PAL:			// loads palette handle onto stack
 
-			ic->stack[++ic->sp] = Fetch(opcode, ic->code, ip);
+			ic->stack[++ic->sp] = Fetch(opcode, ic->code, wkEntry, ip);
 			break;
 
 		case OP_ZERO:			// loads zero onto stack
@@ -464,31 +534,31 @@
 
 		case OP_LOAD:			// loads local variable onto stack
 
-			ic->stack[++ic->sp] = ic->stack[ic->bp + Fetch(opcode, ic->code, ip)];
+			ic->stack[++ic->sp] = ic->stack[ic->bp + Fetch(opcode, ic->code, wkEntry, ip)];
 			break;
 
 		case OP_GLOAD:				// loads global variable onto stack
 
-			tmp = Fetch(opcode, ic->code, ip);
+			tmp = Fetch(opcode, ic->code, wkEntry, ip);
 			assert(0 <= tmp && tmp < numGlobals);
 			ic->stack[++ic->sp] = pGlobals[tmp];
 			break;
 
 		case OP_STORE:				// pops stack and stores in local variable
 
-			ic->stack[ic->bp + Fetch(opcode, ic->code, ip)] = ic->stack[ic->sp--];
+			ic->stack[ic->bp + Fetch(opcode, ic->code, wkEntry, ip)] = ic->stack[ic->sp--];
 			break;
 
 		case OP_GSTORE:				// pops stack and stores in global variable
 
-			tmp = Fetch(opcode, ic->code, ip);
+			tmp = Fetch(opcode, ic->code, wkEntry, ip);
 			assert(0 <= tmp && tmp < numGlobals);
 			pGlobals[tmp] = ic->stack[ic->sp--];
 			break;
 
 		case OP_CALL:				// procedure call
 
-			tmp = Fetch(opcode, ic->code, ip);
+			tmp = Fetch(opcode, ic->code, wkEntry, ip);
 			//assert(0 <= tmp && tmp < codeSize);	// TODO: Verify jumps are not out of bounds
 			ic->stack[ic->sp + 1] = 0;	// static link
 			ic->stack[ic->sp + 2] = ic->bp;	// dynamic link
@@ -499,7 +569,7 @@
 
 		case OP_LIBCALL:		// library procedure or function call
 
-			tmp = Fetch(opcode, ic->code, ip);
+			tmp = Fetch(opcode, ic->code, wkEntry, ip);
 			// NOTE: Interpret() itself is not using the coroutine facilities,
 			// but still accepts a CORO_PARAM, so from the outside it looks
 			// like a coroutine. In fact it may still acts as a kind of "proxy"
@@ -538,17 +608,17 @@
 
 		case OP_ALLOC:			// allocate storage on stack
 
-			ic->sp += (int32)Fetch(opcode, ic->code, ip);
+			ic->sp += (int32)Fetch(opcode, ic->code, wkEntry, ip);
 			break;
 
 		case OP_JUMP:	// unconditional jump
 
-			ip = Fetch(opcode, ic->code, ip);
+			ip = Fetch(opcode, ic->code, wkEntry, ip);
 			break;
 
 		case OP_JMPFALSE:	// conditional jump
 
-			tmp = Fetch(opcode, ic->code, ip);
+			tmp = Fetch(opcode, ic->code, wkEntry, ip);
 			if (ic->stack[ic->sp--] == 0) {
 				// condition satisfied - do the jump
 				ip = tmp;
@@ -557,7 +627,7 @@
 
 		case OP_JMPTRUE:	// conditional jump
 
-			tmp = Fetch(opcode, ic->code, ip);
+			tmp = Fetch(opcode, ic->code, wkEntry, ip);
 			if (ic->stack[ic->sp--] != 0) {
 				// condition satisfied - do the jump
 				ip = tmp;
@@ -660,6 +730,7 @@
 		// check for stack under-overflow
 		assert(ic->sp >= 0 && ic->sp < PCODE_STACK_SIZE);
 		ic->ip = ip;
+		ic->fragmentPtr = wkEntry;
 	} while (!ic->bHalt);
 
 	// make sure stack is unwound

Modified: scummvm/trunk/engines/tinsel/pcode.h
===================================================================
--- scummvm/trunk/engines/tinsel/pcode.h	2009-07-15 05:00:59 UTC (rev 42499)
+++ scummvm/trunk/engines/tinsel/pcode.h	2009-07-15 08:55:12 UTC (rev 42500)
@@ -54,6 +54,17 @@
 
 enum RESCODE {RES_WAITING, RES_FINISHED, RES_CUTSHORT};
 
+// The following structure is used to introduce bug fixes into the scripts used by the games
+
+struct WorkaroundEntry {
+	TinselEngineVersion version;
+	bool scnFlag;					// Only applicable for Tinsel 1 (DW 1)
+	SCNHANDLE hCode;				// Script to apply fragment to
+	int ip;							// Script offset to run this fragment before
+	int numBytes;					// Number of bytes in the script
+	const byte *script;				// Instruction(s) to execute
+};
+
 struct INT_CONTEXT {
 
 	// Elements for interpret context management
@@ -82,6 +93,9 @@
 	RESCODE resumeCode;
 	RESUME_STATE resumeState;
 
+	// Used to store execution state within a script workaround fragment
+	const WorkaroundEntry *fragmentPtr;
+
 	void syncWithSerializer(Common::Serializer &s);
 };
 typedef INT_CONTEXT *PINT_CONTEXT;


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list