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

thebluegr at users.sourceforge.net thebluegr at users.sourceforge.net
Mon Dec 1 21:35:37 CET 2008


Revision: 35196
          http://scummvm.svn.sourceforge.net/scummvm/?rev=35196&view=rev
Author:   thebluegr
Date:     2008-12-01 20:35:36 +0000 (Mon, 01 Dec 2008)

Log Message:
-----------
Merged the tinsel 2 engine with tinsel 1. Both Discworld 1 and Discworld 2 should be completable

Modified Paths:
--------------
    scummvm/trunk/engines/tinsel/actors.cpp
    scummvm/trunk/engines/tinsel/actors.h
    scummvm/trunk/engines/tinsel/anim.cpp
    scummvm/trunk/engines/tinsel/anim.h
    scummvm/trunk/engines/tinsel/background.cpp
    scummvm/trunk/engines/tinsel/background.h
    scummvm/trunk/engines/tinsel/bg.cpp
    scummvm/trunk/engines/tinsel/config.cpp
    scummvm/trunk/engines/tinsel/config.h
    scummvm/trunk/engines/tinsel/coroutine.h
    scummvm/trunk/engines/tinsel/cursor.cpp
    scummvm/trunk/engines/tinsel/cursor.h
    scummvm/trunk/engines/tinsel/debugger.cpp
    scummvm/trunk/engines/tinsel/detection.cpp
    scummvm/trunk/engines/tinsel/dw.h
    scummvm/trunk/engines/tinsel/effect.cpp
    scummvm/trunk/engines/tinsel/events.cpp
    scummvm/trunk/engines/tinsel/events.h
    scummvm/trunk/engines/tinsel/faders.cpp
    scummvm/trunk/engines/tinsel/faders.h
    scummvm/trunk/engines/tinsel/font.cpp
    scummvm/trunk/engines/tinsel/font.h
    scummvm/trunk/engines/tinsel/graphics.cpp
    scummvm/trunk/engines/tinsel/graphics.h
    scummvm/trunk/engines/tinsel/handle.cpp
    scummvm/trunk/engines/tinsel/handle.h
    scummvm/trunk/engines/tinsel/heapmem.cpp
    scummvm/trunk/engines/tinsel/mareels.cpp
    scummvm/trunk/engines/tinsel/module.mk
    scummvm/trunk/engines/tinsel/move.cpp
    scummvm/trunk/engines/tinsel/move.h
    scummvm/trunk/engines/tinsel/multiobj.cpp
    scummvm/trunk/engines/tinsel/multiobj.h
    scummvm/trunk/engines/tinsel/music.cpp
    scummvm/trunk/engines/tinsel/music.h
    scummvm/trunk/engines/tinsel/object.cpp
    scummvm/trunk/engines/tinsel/object.h
    scummvm/trunk/engines/tinsel/palette.cpp
    scummvm/trunk/engines/tinsel/palette.h
    scummvm/trunk/engines/tinsel/pcode.cpp
    scummvm/trunk/engines/tinsel/pcode.h
    scummvm/trunk/engines/tinsel/pdisplay.cpp
    scummvm/trunk/engines/tinsel/pid.h
    scummvm/trunk/engines/tinsel/play.cpp
    scummvm/trunk/engines/tinsel/polygons.cpp
    scummvm/trunk/engines/tinsel/polygons.h
    scummvm/trunk/engines/tinsel/rince.cpp
    scummvm/trunk/engines/tinsel/rince.h
    scummvm/trunk/engines/tinsel/saveload.cpp
    scummvm/trunk/engines/tinsel/savescn.cpp
    scummvm/trunk/engines/tinsel/savescn.h
    scummvm/trunk/engines/tinsel/scene.cpp
    scummvm/trunk/engines/tinsel/scene.h
    scummvm/trunk/engines/tinsel/sched.cpp
    scummvm/trunk/engines/tinsel/sched.h
    scummvm/trunk/engines/tinsel/scn.cpp
    scummvm/trunk/engines/tinsel/scn.h
    scummvm/trunk/engines/tinsel/scroll.cpp
    scummvm/trunk/engines/tinsel/scroll.h
    scummvm/trunk/engines/tinsel/sound.cpp
    scummvm/trunk/engines/tinsel/sound.h
    scummvm/trunk/engines/tinsel/strres.cpp
    scummvm/trunk/engines/tinsel/strres.h
    scummvm/trunk/engines/tinsel/text.cpp
    scummvm/trunk/engines/tinsel/text.h
    scummvm/trunk/engines/tinsel/timers.cpp
    scummvm/trunk/engines/tinsel/timers.h
    scummvm/trunk/engines/tinsel/tinlib.cpp
    scummvm/trunk/engines/tinsel/tinlib.h
    scummvm/trunk/engines/tinsel/tinsel.cpp
    scummvm/trunk/engines/tinsel/tinsel.h

Added Paths:
-----------
    scummvm/trunk/engines/tinsel/bmv.cpp
    scummvm/trunk/engines/tinsel/dialogs.cpp
    scummvm/trunk/engines/tinsel/dialogs.h
    scummvm/trunk/engines/tinsel/drives.cpp
    scummvm/trunk/engines/tinsel/drives.h
    scummvm/trunk/engines/tinsel/mareels.h
    scummvm/trunk/engines/tinsel/pdisplay.h
    scummvm/trunk/engines/tinsel/play.h
    scummvm/trunk/engines/tinsel/sysvar.cpp
    scummvm/trunk/engines/tinsel/sysvar.h

Removed Paths:
-------------
    scummvm/trunk/engines/tinsel/inventory.cpp
    scummvm/trunk/engines/tinsel/inventory.h

Modified: scummvm/trunk/engines/tinsel/actors.cpp
===================================================================
--- scummvm/trunk/engines/tinsel/actors.cpp	2008-11-30 21:17:58 UTC (rev 35195)
+++ scummvm/trunk/engines/tinsel/actors.cpp	2008-12-01 20:35:36 UTC (rev 35196)
@@ -25,19 +25,23 @@
  */
 
 #include "tinsel/actors.h"
+#include "tinsel/background.h"
 #include "tinsel/events.h"
 #include "tinsel/film.h"	// for FREEL
 #include "tinsel/handle.h"
-#include "tinsel/inventory.h"	// INV_NOICON
+#include "tinsel/dialogs.h"	// INV_NOICON
 #include "tinsel/move.h"
 #include "tinsel/multiobj.h"
 #include "tinsel/object.h"	// for POBJECT
 #include "tinsel/pcode.h"
 #include "tinsel/pid.h"
+#include "tinsel/play.h"
 #include "tinsel/polygons.h"
 #include "tinsel/rince.h"
 #include "tinsel/sched.h"
 #include "tinsel/serializer.h"
+#include "tinsel/sysvar.h"
+#include "tinsel/tinsel.h"
 #include "tinsel/token.h"
 
 #include "common/util.h"
@@ -51,25 +55,37 @@
 #include "common/pack-start.h"	// START STRUCT PACKING
 
 /** actor struct - one per actor */
-struct ACTOR_STRUC {
+struct T1_ACTOR_STRUC {
 	int32 masking;			//!< type of actor masking
 	SCNHANDLE hActorId;		//!< handle actor ID string index
 	SCNHANDLE hActorCode;	//!< handle to actor script
 } PACKED_STRUCT;
 
+struct T2_ACTOR_STRUC {
+	SCNHANDLE hActorId;	// handle actor ID string index
+	SCNHANDLE hTagText;	// tag
+	int32 tagPortionV;	// defines tag area
+	int32 tagPortionH;	// defines tag area
+	SCNHANDLE hActorCode;	// handle to actor script
+} PACKED_STRUCT;
+
 #include "common/pack-end.h"	// END STRUCT PACKING
 
+//----------------- LOCAL MACROS ----------------------------
 
+#define RANGE_CHECK(num)	assert(num > 0 && num <= NumActors);
 
 //----------------- LOCAL GLOBAL DATA --------------------
 
+#define MAX_REELS 6
+
 static int LeadActorId = 0;		// The lead actor
 
 static int NumActors = 0;	// The total number of actors in the game
 
 struct ACTORINFO {
-	bool		alive;		// TRUE == alive
-	bool		hidden;		// TRUE == hidden
+	bool		bAlive;		// TRUE == alive
+	bool		bHidden;	// TRUE == hidden
 	bool		completed;	// TRUE == script played out
 
 	int			x, y, z;
@@ -81,34 +97,65 @@
 	int			presRnum;	// the present reel number
 	SCNHANDLE 	presFilm;	// the film that reel belongs to
 	OBJECT		*presObj;	// reference for position information
-	int			presX, presY;
+	int			presPlayX, presPlayY;
 
 	bool		tagged;		// actor tagged?
 	SCNHANDLE	hTag;		// handle to tag text
 	int			tType;		// e.g. TAG_Q1TO3
 
-	bool		escOn;
-	int			escEv;
+	bool		bEscOn;
+	int			escEvent;
 
-	COLORREF	tColour;	// Text colour
+	COLORREF	textColour;	// Text colour
 
 	SCNHANDLE 	playFilm;	// revert to this after talks
 	SCNHANDLE 	talkFilm;	// this be deleted in the future!
 	SCNHANDLE 	latestFilm;	// the last film ordered
-	bool		talking;
+	bool		bTalking;
 
 	int			steps;
+	int			loopCount;
 
+	// DW2 new fields and alternates
+	int			presColumns[MAX_REELS];	// the present columns
+	OBJECT		*presObjs[MAX_REELS];	// reference for position information
+	int			filmNum;
 };
 
-static ACTORINFO *actorInfo = 0;
+struct TAGACTOR {
+	// Copies of compiled data
+	int			id;
+	SCNHANDLE	hTagText;		// handle to tag text
+	int32		tagPortionV;	// which portion is active
+	int32		tagPortionH;	// which portion is active
+	SCNHANDLE	hActorCode;		// The actor's script
 
+	int			tagFlags;
+	SCNHANDLE	hOverrideTag;	// Override tag.
+};
+typedef TAGACTOR *PTAGACTOR;
+
+
+static ACTORINFO *actorInfo = NULL;
+
 static COLORREF defaultColour = 0;		// Text colour
 
 static bool bActorsOn = false;
 
 static int ti = 0;
 
+#define MAX_TAGACTORS 10
+
+static TAGACTOR taggedActors[MAX_TAGACTORS];
+
+static int numTaggedActors = 0;
+
+static uint8 *zFactors = NULL;
+
+static Z_POSITIONS zPositions[NUM_ZPOSITIONS];
+
+//-------------------- METHOD LIST -----------------------
+
 /**
  * Called once at start-up time, and again at restart time.
  * Registers the total number of actors in the game.
@@ -122,10 +169,13 @@
 		// Check we can save so many
 		assert(NumActors <= MAX_SAVED_ALIVES);
 
-		// Allocate RAM for actorInfo
+		// Allocate RAM for actor structures
+
 		// FIXME: For now, we always allocate MAX_SAVED_ALIVES blocks,
 		//   as this makes the save/load code simpler
 		actorInfo = (ACTORINFO *)calloc(MAX_SAVED_ALIVES, sizeof(ACTORINFO));
+		if (TinselV2)
+			zFactors = (uint8 *)malloc(MAX_SAVED_ALIVES);
 
 		// make sure memory allocated
 		if (actorInfo == NULL) {
@@ -136,11 +186,13 @@
 		assert(num == NumActors);
 
 		memset(actorInfo, 0, MAX_SAVED_ALIVES * sizeof(ACTORINFO));
+		if (TinselV2)
+			memset(zFactors, 0, MAX_SAVED_ALIVES);
 	}
 
 	// All actors start off alive.
 	while (num--)
-		actorInfo[num].alive = true;
+		actorInfo[num].bAlive = true;
 }
 
 void FreeActors() {
@@ -154,7 +206,7 @@
  * Called from dec_lead(), i.e. normally once at start of master script.
  * @param leadID			Lead Id
  */
-void setleadid(int leadID) {
+void SetLeadId(int leadID) {
 	LeadActorId = leadID;
 	actorInfo[leadID-1].mtype = ACT_MASK;
 }
@@ -162,41 +214,84 @@
 /**
  * No comment.
  */
-int LeadId(void) {
+int GetLeadId(void) {
 	return LeadActorId;
 }
 
+bool ActorIsGhost(int actor) {
+	return actor == SysVar(ISV_GHOST_ACTOR);
+}
+
 struct ATP_INIT {
 	int		id;		// Actor number
-	USER_EVENT	event;		// Event
-	BUTEVENT	bev;		// Causal mouse event
+	TINSEL_EVENT	event;		// Event
+	PLR_EVENT	bev;		// Causal mouse event
+
+	PINT_CONTEXT	pic;
 };
 
 /**
+ * Convert actor id to index into TaggedActors[]
+ */
+static int TaggedActorIndex(int actor) {
+	int i;
+
+	for (i = 0; i < numTaggedActors; i++) {
+		if (taggedActors[i].id == actor)
+			return i;
+	}
+
+	error("You may say to yourself \"this is not my tagged actor\"");
+}
+
+/**
  * Runs actor's glitter code.
  */
 static void ActorTinselProcess(CORO_PARAM, const void *param) {
 	// COROUTINE
 	CORO_BEGIN_CONTEXT;
 		INT_CONTEXT *pic;
+		bool bTookControl;
 	CORO_END_CONTEXT(_ctx);
 
 	// get the stuff copied to process when it was created
-	ATP_INIT *atp = (ATP_INIT *)param;
+	const ATP_INIT *atp = (const ATP_INIT *)param;
 
 	CORO_BEGIN_CODE(_ctx);
 
-	CORO_INVOKE_1(AllowDclick, atp->bev);		// May kill us if single click
+	if (TinselV2) {
+		// Take control for CONVERSE events
+		if (atp->event == CONVERSE) {
+			_ctx->bTookControl = GetControl();
+			HideConversation(true);
+		} else
+			_ctx->bTookControl = false;
 
-	// Run the Glitter code
-	assert(actorInfo[atp->id - 1].actorCode); // no code to run
+		// Run the Glitter code
+		CORO_INVOKE_1(Interpret, atp->pic);
 
-	_ctx->pic = InitInterpretContext(GS_ACTOR, actorInfo[atp->id - 1].actorCode, atp->event, NOPOLY, atp->id, NULL);
-	CORO_INVOKE_1(Interpret, _ctx->pic);
+		// Restore conv window if applicable
+		if (atp->event == CONVERSE) {
+			// Free control if we took it
+			if (_ctx->bTookControl)
+				ControlOn();
 
-	// If it gets here, actor's code has run to completion
-	actorInfo[atp->id - 1].completed = true;
+			HideConversation(false);
+		}
+	} else {
+		CORO_INVOKE_1(AllowDclick, atp->bev);		// May kill us if single click
 
+		// Run the Glitter code
+		assert(actorInfo[atp->id - 1].actorCode); // no code to run
+
+		_ctx->pic = InitInterpretContext(GS_ACTOR, actorInfo[atp->id - 1].actorCode, 
+			atp->event, NOPOLY, atp->id, NULL);
+		CORO_INVOKE_1(Interpret, _ctx->pic);
+
+		// If it gets here, actor's code has run to completion
+		actorInfo[atp->id - 1].completed = true;
+	}
+
 	CORO_END_CODE;
 }
 
@@ -215,7 +310,7 @@
 	CORO_END_CONTEXT(_ctx);
 
 	// get the stuff copied to process when it was created
-	RATP_INIT *r = (RATP_INIT *)param;
+	const RATP_INIT *r = (const RATP_INIT *)param;
 
 	CORO_BEGIN_CODE(_ctx);
 
@@ -240,7 +335,7 @@
  * @param event			Event structure
  * @param be			ButEvent
  */
-void actorEvent(int ano, USER_EVENT event, BUTEVENT be) {
+void ActorEvent(int ano, TINSEL_EVENT event, PLR_EVENT be) {
 	ATP_INIT atp;
 
 	// Only if there is Glitter code associated with this actor.
@@ -248,20 +343,59 @@
 		atp.id = ano;
 		atp.event = event;
 		atp.bev = be;
+		atp.pic = NULL;
 		g_scheduler->createProcess(PID_TCODE, ActorTinselProcess, &atp, sizeof(atp));
 	}
 }
 
 /**
+ * Starts up process to run actor's glitter code.
+ */
+void ActorEvent(CORO_PARAM, int ano, TINSEL_EVENT tEvent, bool bWait, int myEscape, bool *result) {
+	ATP_INIT atp;
+	int	index;
+	CORO_BEGIN_CONTEXT;
+		PPROCESS pProc;
+	CORO_END_CONTEXT(_ctx);
+
+	CORO_BEGIN_CODE(_ctx);
+
+	index = TaggedActorIndex(ano);
+	assert(taggedActors[index].hActorCode);
+	if (result) *result = false;
+
+	atp.id = 0;
+	atp.event = tEvent;
+	atp.pic = InitInterpretContext(GS_ACTOR,
+			taggedActors[index].hActorCode,
+			tEvent,
+			NOPOLY,			// No polygon
+			ano,			// Actor
+			NULL,			// No object
+			myEscape);
+	
+	if (atp.pic != NULL) {
+		_ctx->pProc = g_scheduler->createProcess(PID_TCODE, ActorTinselProcess, &atp, sizeof(atp));
+		AttachInterpret(atp.pic, _ctx->pProc);
+
+		if (bWait)
+			CORO_INVOKE_2(WaitInterpret,_ctx->pProc, result);
+	}
+
+	CORO_END_CODE;
+}
+
+ 
+/**
  * Called at the start of each scene for each actor with a code block.
  * @param as			Actor structure
  * @param bRunScript	Flag for whether to run actor's script for the scene
  */
-void StartActor(const ACTOR_STRUC *as, bool bRunScript) {
+void StartActor(const T1_ACTOR_STRUC *as, bool bRunScript) {
 	SCNHANDLE hActorId = FROM_LE_32(as->hActorId);
 
 	// Zero-out many things
-	actorInfo[hActorId - 1].hidden = false;
+	actorInfo[hActorId - 1].bHidden = false;
 	actorInfo[hActorId - 1].completed = false;
 	actorInfo[hActorId - 1].x = 0;
 	actorInfo[hActorId - 1].y = 0;
@@ -276,10 +410,10 @@
 	// Run actor's script for this scene
 	if (bRunScript) {
 		if (bActorsOn)
-			actorInfo[hActorId - 1].alive = true;
+			actorInfo[hActorId - 1].bAlive = true;
 
-		if (actorInfo[hActorId - 1].alive && FROM_LE_32(as->hActorCode))
-			actorEvent(hActorId, STARTUP, BE_NONE);
+		if (actorInfo[hActorId - 1].bAlive && FROM_LE_32(as->hActorCode))
+			ActorEvent(hActorId, STARTUP, PLR_NOEVENT);
 	}
 }
 
@@ -289,18 +423,47 @@
  * @param numActors		Number of actors
  * @param bRunScript	Flag for whether to run actor scene scripts
  */
-void StartActors(SCNHANDLE ah, int numActors, bool bRunScript) {
+void StartTaggedActors(SCNHANDLE ah, int numActors, bool bRunScript) {
 	int	i;
 
-	// Only actors with code blocks got (x, y) re-initialised, so...
-	for (i = 0; i < NumActors; i++) {
-		actorInfo[i].x = actorInfo[i].y = 0;
-		actorInfo[i].mtype = 0;
+	if (TinselV2) {
+		// Clear it all out for a fresh start
+		memset(taggedActors, 0, sizeof(taggedActors));
+		numTaggedActors = numActors;
+	} else {
+		// Only actors with code blocks got (x, y) re-initialised, so...
+		for (i = 0; i < NumActors; i++) {
+			actorInfo[i].x = actorInfo[i].y = 0;
+			actorInfo[i].mtype = 0;
+		}
 	}
 
-	const ACTOR_STRUC *as = (const ACTOR_STRUC *)LockMem(ah);
-	for (i = 0; i < numActors; i++, as++) {
-		StartActor(as, bRunScript);
+	if (!TinselV2) {
+		// Tinsel 1 load variation
+		const T1_ACTOR_STRUC *as = (const T1_ACTOR_STRUC *)LockMem(ah);
+		for (i = 0; i < numActors; i++, as++) {
+			StartActor(as, bRunScript);
+		}
+	} else if (numActors > 0) {
+		// Tinsel 2 load variation
+		const T2_ACTOR_STRUC *as = (T2_ACTOR_STRUC *)LockMem(ah);
+		for (i = 0; i < numActors; i++, as++) {
+			assert(as->hActorCode);
+
+			// Store current scene's parameters for this tagged actor
+			taggedActors[i].id			= FROM_LE_32(as->hActorId);
+			taggedActors[i].hTagText	= FROM_LE_32(as->hTagText);
+			taggedActors[i].tagPortionV	= FROM_LE_32(as->tagPortionV);
+			taggedActors[i].tagPortionH	= FROM_LE_32(as->tagPortionH);
+			taggedActors[i].hActorCode	= FROM_LE_32(as->hActorCode);
+
+			// Run actor's script for this scene
+			if (bRunScript) {
+				// Send in reverse order - they get swapped round in the scheduler
+				ActorEvent(nullContext, taggedActors[i].id, SHOWEVENT, false, 0);
+				ActorEvent(nullContext, taggedActors[i].id, STARTUP, false, 0);
+			}
+		}
 	}
 }
 
@@ -308,18 +471,34 @@
  * Called between scenes, zeroises all actors.
  */
 void DropActors(void) {
+
 	for (int i = 0; i < NumActors; i++) {
-		actorInfo[i].actorCode = 0;	// No script
-		actorInfo[i].presReel = NULL;	// No reel running
-		actorInfo[i].presFilm = 0;	//   ditto
-		actorInfo[i].presObj = NULL;	// No object
-		actorInfo[i].x = 0;		// No position
-		actorInfo[i].y = 0;		//   ditto
+		if (TinselV2) {
+			// Save text colour
+			COLORREF tColour = actorInfo[i].textColour;
 
-		actorInfo[i].talkFilm = 0;
-		actorInfo[i].latestFilm = 0;
-		actorInfo[i].playFilm = 0;
-		actorInfo[i].talking = false;
+			memset(&actorInfo[i], 0, sizeof(ACTORINFO));
+
+			// Restor text colour
+			actorInfo[i].textColour = tColour;
+
+			// Clear extra arrays
+			memset(zFactors, 0, NumActors);
+			memset(zPositions, 0, sizeof(zPositions));
+		} else {
+			// In Tinsel v1, only certain fields get reset
+			actorInfo[i].actorCode = 0;		// No script
+			actorInfo[i].presReel = NULL;	// No reel running
+			actorInfo[i].presFilm = 0;		//   ditto
+			actorInfo[i].presObj = NULL;	// No object
+			actorInfo[i].x = 0;				// No position
+			actorInfo[i].y = 0;				//   ditto
+
+			actorInfo[i].talkFilm = 0;
+			actorInfo[i].latestFilm = 0;
+			actorInfo[i].playFilm = 0;
+			actorInfo[i].bTalking = false;
+		}
 	}
 }
 
@@ -328,17 +507,17 @@
  * @param ano			Actor Id
  */
 void DisableActor(int ano) {
-	PMACTOR	pActor;
+	PMOVER	pActor;
 
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	actorInfo[ano - 1].alive = false;	// Record as dead
+	actorInfo[ano - 1].bAlive = false;	// Record as dead
 	actorInfo[ano - 1].x = actorInfo[ano - 1].y = 0;
 
 	// Kill off moving actor properly
 	pActor = GetMover(ano);
 	if (pActor)
-		KillMActor(pActor);
+		KillMover(pActor);
 }
 
 /**
@@ -349,14 +528,14 @@
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
 	// Re-incarnate only if it's dead, or it's script ran to completion
-	if (!actorInfo[ano - 1].alive || actorInfo[ano - 1].completed) {
-		actorInfo[ano - 1].alive = true;
-		actorInfo[ano - 1].hidden = false;
+	if (!actorInfo[ano - 1].bAlive || actorInfo[ano - 1].completed) {
+		actorInfo[ano - 1].bAlive = true;
+		actorInfo[ano - 1].bHidden = false;
 		actorInfo[ano - 1].completed = false;
 
 		// Re-run actor's script for this scene
 		if (actorInfo[ano-1].actorCode)
-			actorEvent(ano, STARTUP, BE_NONE);
+			ActorEvent(ano, STARTUP, PLR_NOEVENT);
 	}
 }
 
@@ -367,7 +546,7 @@
 bool actorAlive(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	return actorInfo[ano - 1].alive;
+	return actorInfo[ano - 1].bAlive;
 }
 
 /**
@@ -444,16 +623,16 @@
  * or there are no more tagged actors to look at.
  */
 int NextTaggedActor(void) {
-	PMACTOR	pActor;
+	PMOVER	pActor;
 	bool	hid;
 
 	do {
 		if (actorInfo[ti].tagged) {
 			pActor = GetMover(ti+1);
 			if (pActor)
-				hid = getMActorHideState(pActor);
+				hid = MoverHidden(pActor);
 			else
-				hid = actorInfo[ti].hidden;
+				hid = actorInfo[ti].bHidden;
 
 			if (!hid) {
 				return ++ti;
@@ -465,6 +644,41 @@
 }
 
 /**
+ * Called from TagProcess, NextTaggedActor() is
+ * called repeatedly until the caller gets fed up or
+ * there are no more tagged actors to look at.
+ */
+int NextTaggedActor(int previous) {
+	PMOVER  pMover;
+
+	// Convert actor number to index
+	if (!previous)
+		previous = -1;
+	else
+		previous = TaggedActorIndex(previous);
+
+	while (++previous < numTaggedActors) {
+		pMover = GetMover(taggedActors[previous].id);
+
+		// No tag on lead actor while he's moving
+		if ((taggedActors[previous].id) == GetLeadId() && MoverMoving(pMover)) {
+			taggedActors[previous].tagFlags &= ~(POINTING | TAGWANTED);
+			continue;
+		}
+
+		// Not if the actor doesn't exist at the moment
+		if (pMover && !MoverIs(pMover))
+			continue;
+
+		if (!(pMover ? MoverHidden(pMover) : ActorHidden(taggedActors[previous].id))) {
+			return taggedActors[previous].id;
+		}
+	}
+
+	return 0;
+}
+
+/**
  * Returns the masking type of the actor.
  * @param ano			Actor Id
  */
@@ -481,41 +695,88 @@
  * @param x				X position
  * @param y				Y position
  */
-void storeActorPos(int ano, int x, int y) {
+void StoreActorPos(int ano, int x, int y) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
 	actorInfo[ano - 1].x = x;
 	actorInfo[ano - 1].y = y;
 }
 
-void storeActorSteps(int ano, int steps) {
+void StoreActorSteps(int ano, int steps) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
 	actorInfo[ano - 1].steps = steps;
 }
 
-int getActorSteps(int ano) {
+int GetActorSteps(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
 	return actorInfo[ano - 1].steps;
 }
 
-void storeActorZpos(int ano, int z) {
+void StoreActorZpos(int ano, int z, int column) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	actorInfo[ano - 1].z = z;
+	if (!TinselV2) {
+		// Prior to Tinsel 2, only a single z value was stored
+		actorInfo[ano - 1].z = z;
+	} else {
+		// Alter existing entry, if there is one
+		for (int i = 0; i < NUM_ZPOSITIONS; i++) {
+			if (zPositions[i].actor == ano && zPositions[i].column == column) {
+				zPositions[i].z = z;
+				return;
+			}
+		}
+
+		// No existing entry found, so find an empty slot
+		for (int i = 0; i < NUM_ZPOSITIONS; i++) {
+			if (zPositions[i].actor == 0) {
+				zPositions[i].actor = (short)ano;
+				zPositions[i].column = (short)column;
+				zPositions[i].z = z;
+				return;
+			}
+		}
+
+		error("NUM_ZPOSITIONS exceeded");
+	}
 }
 
+int GetActorZpos(int ano, int column) {
+	RANGE_CHECK(ano);
 
+	// Find entry, there should be one
+	for (int i = 0; i < NUM_ZPOSITIONS; i++) {
+		if (zPositions[i].actor == ano && zPositions[i].column == column) {
+			return zPositions[i].z;
+		}
+	}
+
+	return 1000;	// Nominal value
+}
+
+void IncLoopCount(int ano) {
+	RANGE_CHECK(ano);
+
+	actorInfo[ano - 1].loopCount++;
+}
+
+int GetLoopCount(int ano) {
+	RANGE_CHECK(ano);
+
+	return actorInfo[ano - 1].loopCount;
+}
+
 void GetActorPos(int ano, int *x, int *y) {
-	PMACTOR pActor;
+	PMOVER pActor;
 
 	assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // unknown actor
 
 	pActor = GetMover(ano);
 
 	if (pActor)
-		GetMActorPosition(pActor, x, y);
+		GetMoverPosition(pActor, x, y);
 	else {
 		*x = actorInfo[ano - 1].x;
 		*y = actorInfo[ano - 1].y;
@@ -531,15 +792,18 @@
  */
 void GetActorMidTop(int ano, int *x, int *y) {
 	// Not used in JAPAN version
-	PMACTOR pActor;
+	PMOVER pActor;
 
 	assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // unknown actor
 
 	pActor = GetMover(ano);
 
 	if (pActor)
-		GetMActorMidTopPosition(pActor, x, y);
-	else if (actorInfo[ano - 1].presObj) {
+		GetMoverMidTop(pActor, x, y);
+	else if (TinselV2) {
+		*x = (GetActorLeft(ano) + GetActorRight(ano)) / 2;
+		*y = GetActorTop(ano);
+	} else if (actorInfo[ano - 1].presObj) {
 		*x = (MultiLeftmost(actorInfo[ano - 1].presObj)
 		      + MultiRightmost(actorInfo[ano - 1].presObj)) / 2;
 		*y = MultiHighest(actorInfo[ano - 1].presObj);
@@ -554,10 +818,39 @@
 int GetActorLeft(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	if (!actorInfo[ano - 1].presObj)
-		return 0;
+	if (!TinselV2) {
+		// Tinsel 1 version
+		if (!actorInfo[ano - 1].presObj)
+			return 0;
 
-	return MultiLeftmost(actorInfo[ano - 1].presObj);
+		return MultiLeftmost(actorInfo[ano - 1].presObj);
+	}
+
+	// Tinsel 2 version
+	PMOVER pMover = GetMover(ano);
+	int i;
+	bool bIsObj;
+	int left = 0;
+
+	if (pMover != NULL) {
+		return GetMoverLeft(pMover);
+	} else {
+		for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
+			// If there's an object
+			// and it is not a blank frame for it...
+			if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano - 1].presObjs[i])) {
+				if (!bIsObj) {
+					bIsObj = true;
+					left = MultiLeftmost(actorInfo[ano - 1].presObjs[i]);
+				} else {
+					if (MultiLeftmost(actorInfo[ano - 1].presObjs[i]) < left)
+						left = MultiLeftmost(actorInfo[ano - 1].presObjs[i]);
+				}
+			}
+		}
+
+		return bIsObj ? left : 0;
+	}
 }
 
 /**
@@ -567,10 +860,38 @@
 int GetActorRight(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	if (!actorInfo[ano - 1].presObj)
-		return 0;
+	if (!TinselV2) {
+		// Tinsel 1 version
+		if (!actorInfo[ano - 1].presObj)
+			return 0;
 
-	return MultiRightmost(actorInfo[ano - 1].presObj);
+		return MultiRightmost(actorInfo[ano - 1].presObj);
+	}
+
+	// Tinsel 2 version
+	PMOVER pMover = GetMover(ano);
+	int i;
+	bool bIsObj;
+	int right = 0;
+
+	if (pMover != NULL) {
+		return GetMoverRight(pMover);
+	} else {
+		for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
+			// If there's an object
+			// and it is not a blank frame for it...
+			if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano-1].presObjs[i])) {
+				if (!bIsObj) {
+					bIsObj = true;
+					right = MultiRightmost(actorInfo[ano-1].presObjs[i]);
+				} else {
+					if (MultiRightmost(actorInfo[ano-1].presObjs[i]) > right)
+						right = MultiRightmost(actorInfo[ano-1].presObjs[i]);
+				}
+			}
+		}
+		return bIsObj ? right : 0;
+	}
 }
 
 /**
@@ -580,10 +901,39 @@
 int GetActorTop(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	if (!actorInfo[ano - 1].presObj)
-		return 0;
+	if (!TinselV2) {
+		// Tinsel 1 version
+		if (!actorInfo[ano - 1].presObj)
+			return 0;
 
-	return MultiHighest(actorInfo[ano - 1].presObj);
+		return MultiHighest(actorInfo[ano - 1].presObj);
+	}
+
+	// Tinsel 2 version
+	PMOVER pMover = GetMover(ano);
+	int i;
+	bool bIsObj;
+	int top = 0;
+
+	if (pMover != NULL) {
+		return GetMoverTop(pMover);
+	} else {
+		for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
+			// If there's an object
+			// and it is not a blank frame for it...
+			if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano-1].presObjs[i])) {
+				if (!bIsObj) {
+					bIsObj = true;
+					top = MultiHighest(actorInfo[ano-1].presObjs[i]);
+				} else {
+					if (MultiHighest(actorInfo[ano-1].presObjs[i]) < top)
+						top = MultiHighest(actorInfo[ano-1].presObjs[i]);
+				}
+			}
+		}
+
+		return bIsObj ? top : 0;
+	}
 }
 
 /**
@@ -592,38 +942,122 @@
 int GetActorBottom(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	if (!actorInfo[ano - 1].presObj)
-		return 0;
+	if (!TinselV2) {
+		// Tinsel 1 version
+		if (!actorInfo[ano - 1].presObj)
+			return 0;
 
-	return MultiLowest(actorInfo[ano - 1].presObj);
+		return MultiLowest(actorInfo[ano - 1].presObj);
+	}
+
+	// Tinsel 2 version
+	PMOVER pMover = GetMover(ano);
+	int i;
+	bool bIsObj;
+	int bottom = 0;
+
+	if (pMover != NULL) {
+		return GetMoverBottom(pMover);
+	} else {
+		for (i = 0, bIsObj = false; i < MAX_REELS; i++) {
+			// If there's an object
+			// and it is not a blank frame for it...
+			if (actorInfo[ano-1].presObjs[i] && MultiHasShape(actorInfo[ano-1].presObjs[i])) {
+				if (!bIsObj) {
+					bIsObj = true;
+					bottom = MultiLowest(actorInfo[ano-1].presObjs[i]);
+				} else {
+					if (MultiLowest(actorInfo[ano-1].presObjs[i]) > bottom)
+						bottom = MultiLowest(actorInfo[ano-1].presObjs[i]);
+				}
+			}
+		}
+		return bIsObj ? bottom : 0;
+	}
 }
 
 /**
+ * Shows the given actor
+ */
+void ShowActor(CORO_PARAM, int ano) {
+	PMOVER pMover;
+	RANGE_CHECK(ano);
+
+	CORO_BEGIN_CONTEXT;
+	CORO_END_CONTEXT(_ctx);
+
+	CORO_BEGIN_CODE(_ctx);
+
+	// reset hidden flag
+	actorInfo[ano - 1].bHidden = false;
+
+	// Send event to tagged actors
+	if (IsTaggedActor(ano))
+		CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, ano, SHOWEVENT, true, 0));
+
+	// If moving actor involved, un-hide it
+	pMover = GetMover(ano);
+	if (pMover)
+		UnHideMover(pMover);
+
+	CORO_END_CODE;
+}
+
+/**
  * Set actor hidden status to true.
  * For a moving actor, actually hide it.
  * @param ano			Actor Id
  */
-void HideActor(int ano) {
-	PMACTOR pActor;
-
+void HideActor(CORO_PARAM, int ano) {
+	PMOVER pMover;
 	assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor
 
+	CORO_BEGIN_CONTEXT;
+	CORO_END_CONTEXT(_ctx);
+
+	CORO_BEGIN_CODE(_ctx);
+
+	if (TinselV2) {
+		actorInfo[ano - 1].bHidden = true;
+
+		// Send event to tagged actors
+		// (this is duplicated in HideMover())
+		if (IsTaggedActor(ano)) {
+			CORO_INVOKE_ARGS(ActorEvent, (CORO_SUBCTX, ano, HIDEEVENT, true, 0));
+
+			// It may be pointed to
+			SetActorPointedTo(ano, false);
+			SetActorTagWanted(ano, false, false, 0);
+		}
+	}
+
 	// Get moving actor involved
-	pActor = GetMover(ano);
+	pMover = GetMover(ano);
 
-	if (pActor)
-		hideMActor(pActor, 0);
-	else
-		actorInfo[ano - 1].hidden = true;
+	if (pMover)
+		HideMover(pMover, 0);
+	else if (!TinselV2)
+		actorInfo[ano - 1].bHidden = true;
+
+	CORO_END_CODE;
 }
 
 /**
+ * Return actor hidden status.
+ */
+bool ActorHidden(int ano) {
+	RANGE_CHECK(ano);
+
+	return actorInfo[ano - 1].bHidden;
+}
+
+/**
  * Hide an actor if it's a moving actor.
  * @param ano			Actor Id
  * @param sf			sf
  */
 bool HideMovingActor(int ano, int sf) {
-	PMACTOR pActor;
+	PMOVER pActor;
 
 	assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor
 
@@ -631,7 +1065,7 @@
 	pActor = GetMover(ano);
 
 	if (pActor) {
-		hideMActor(pActor, sf);
+		HideMover(pActor, sf);
 		return true;
 	} else {
 		if (actorInfo[ano - 1].presObj != NULL)
@@ -645,7 +1079,7 @@
  * @param ano			Actor Id
  */
 void unHideMovingActor(int ano) {
-	PMACTOR pActor;
+	PMOVER pActor;
 
 	assert((ano > 0 && ano <= NumActors) || ano == LEAD_ACTOR); // illegal actor
 
@@ -654,7 +1088,7 @@
 
 	assert(pActor); // not a moving actor
 
-	unhideMActor(pActor);
+	UnHideMover(pActor);
 }
 
 /**
@@ -663,7 +1097,7 @@
  * actor's walk (if any) from the new co-ordinates.
  */
 void restoreMovement(int ano) {
-	PMACTOR pActor;
+	PMOVER pActor;
 
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
@@ -672,11 +1106,11 @@
 
 	assert(pActor); // not a moving actor
 
-	if (pActor->objx == actorInfo[ano - 1].x && pActor->objy == actorInfo[ano - 1].y)
+	if (pActor->objX == actorInfo[ano - 1].x && pActor->objY == actorInfo[ano - 1].y)
 		return;
 
-	pActor->objx = actorInfo[ano - 1].x;
-	pActor->objy = actorInfo[ano - 1].y;
+	pActor->objX = actorInfo[ano - 1].x;
+	pActor->objY = actorInfo[ano - 1].y;
 
 	if (pActor->actorObj)
 		SSetActorDest(pActor);
@@ -686,28 +1120,28 @@
  * More properly should be called:
  * 'store_actor_reel_and/or_film_and/or_object()'
  */
-void storeActorReel(int ano, const FREEL *reel, SCNHANDLE film, OBJECT *pobj, int reelnum, int x, int y) {
-	PMACTOR pActor;
+void storeActorReel(int ano, const FREEL *reel, SCNHANDLE hFilm, OBJECT *pobj, int reelnum, int x, int y) {
+	PMOVER pActor;
 
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
 	pActor = GetMover(ano);
 
-	// Only store the reel and film for a moving actor if NOT called from MActorProcess()
-	// (MActorProcess() calls with reel=film=NULL, pobj not NULL)
+	// Only store the reel and film for a moving actor if NOT called from MoverProcess()
+	// (MoverProcess() calls with reel=film=NULL, pobj not NULL)
 	if (!pActor
-	|| !(reel == NULL && film == 0 && pobj != NULL)) {
+	|| !(reel == NULL && hFilm == 0 && pobj != NULL)) {
 		actorInfo[ano - 1].presReel = reel;	// Store reel
 		actorInfo[ano - 1].presRnum = reelnum;	// Store reel number
-		actorInfo[ano - 1].presFilm = film;	// Store film
-		actorInfo[ano - 1].presX = x;
-		actorInfo[ano - 1].presY = y;
+		actorInfo[ano - 1].presFilm = hFilm;	// Store film
+		actorInfo[ano - 1].presPlayX = x;
+		actorInfo[ano - 1].presPlayY = y;
 	}
 
-	// Only store the object for a moving actor if called from MActorProcess()
+	// Only store the object for a moving actor if called from MoverProcess()
 	if (!pActor) {
 		actorInfo[ano - 1].presObj = pobj;	// Store object
-	} else if (reel == NULL && film == 0 && pobj != NULL) {
+	} else if (reel == NULL && hFilm == 0 && pobj != NULL) {
 		actorInfo[ano - 1].presObj = pobj;	// Store object
 	}
 }
@@ -723,50 +1157,50 @@
 
 /***************************************************************************/
 
-void setActorPlayFilm(int ano, SCNHANDLE film) {
+void SetActorPlayFilm(int ano, SCNHANDLE hFilm) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	actorInfo[ano - 1].playFilm = film;
+	actorInfo[ano - 1].playFilm = hFilm;
 }
 
-SCNHANDLE getActorPlayFilm(int ano) {
+SCNHANDLE GetActorPlayFilm(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
 	return actorInfo[ano - 1].playFilm;
 }
 
-void setActorTalkFilm(int ano, SCNHANDLE film) {
+void SetActorTalkFilm(int ano, SCNHANDLE hFilm) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	actorInfo[ano - 1].talkFilm = film;
+	actorInfo[ano - 1].talkFilm = hFilm;
 }
 
-SCNHANDLE getActorTalkFilm(int ano) {
+SCNHANDLE GetActorTalkFilm(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
 	return actorInfo[ano - 1].talkFilm;
 }
 
-void setActorTalking(int ano, bool tf) {
+void SetActorTalking(int ano, bool tf) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	actorInfo[ano - 1].talking = tf;;
+	actorInfo[ano - 1].bTalking = tf;;
 }
 
-bool isActorTalking(int ano) {
+bool ActorIsTalking(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	return actorInfo[ano - 1].talking;
+	return actorInfo[ano - 1].bTalking;
 }
 
-void setActorLatestFilm(int ano, SCNHANDLE film) {
+void SetActorLatestFilm(int ano, SCNHANDLE hFilm) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	actorInfo[ano - 1].latestFilm = film;
+	actorInfo[ano - 1].latestFilm = hFilm;
 	actorInfo[ano - 1].steps = 0;
 }
 
-SCNHANDLE getActorLatestFilm(int ano) {
+SCNHANDLE GetActorLatestFilm(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
 	return actorInfo[ano - 1].latestFilm;
@@ -774,23 +1208,36 @@
 
 /***************************************************************************/
 
-void updateActorEsc(int ano, bool escOn, int escEvent) {
+void UpdateActorEsc(int ano, bool escOn, int escEvent) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	actorInfo[ano - 1].escOn = escOn;
-	actorInfo[ano - 1].escEv = escEvent;
+	actorInfo[ano - 1].bEscOn = escOn;
+	actorInfo[ano - 1].escEvent = escEvent;
 }
 
-bool actorEsc(int ano) {
+void UpdateActorEsc(int ano, int escEvent) {
+	RANGE_CHECK(ano);
+
+	if (escEvent) {
+		actorInfo[ano - 1].bEscOn = true;
+		actorInfo[ano - 1].escEvent = escEvent;
+	} else {
+		actorInfo[ano - 1].bEscOn = false;
+		actorInfo[ano - 1].escEvent = GetEscEvents();
+	}
+
+}
+
+bool ActorEsc(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	return actorInfo[ano - 1].escOn;
+	return actorInfo[ano - 1].bEscOn;
 }
 
-int actorEev(int ano) {
+int ActorEev(int ano) {
 	assert(ano > 0 && ano <= NumActors); // illegal actor number
 
-	return actorInfo[ano - 1].escEv;
+	return actorInfo[ano - 1].escEvent;
 }
 
 /**
@@ -801,7 +1248,7 @@
 
 	z += z ? -1 : 0;
 
-	zPos = y + (z << 10);
+	zPos = y + (z << ZSHIFT);
 	MultiSetZPosition(pObj, zPos);
 	return zPos;
 }
@@ -809,9 +1256,18 @@
 /**
  * Guess what these do.
  */
-void MAsetZPos(PMACTOR pActor, int y, int32 zFactor) {
-	if (!pActor->aHidden)
-		AsetZPos(pActor->actorObj, y, zFactor);
+void SetMoverZ(PMOVER pMover, int y, int32 zFactor) {
+	if (!pMover->bHidden) {
+		if (!TinselV2)
+			AsetZPos(pMover->actorObj, y, zFactor);
+		else if (MoverIsSWalking(pMover) && pMover->zOverride != -1) {
+			// Special for SWalk()
+			MultiSetZPosition(pMover->actorObj, (pMover->zOverride << ZSHIFT) + y);
+		} else {
+			// Normal case
+			MultiSetZPosition(pMover->actorObj, (zFactor << ZSHIFT) + y);
+		}
+	}
 }
 
 /**
@@ -828,51 +1284,127 @@
 	if (ano == -1)
 		defaultColour = RGB(r1, g1, b1);
 	else
-		actorInfo[ano - 1].tColour = RGB(r1, g1, b1);
+		actorInfo[ano - 1].textColour = RGB(r1, g1, b1);
 }
 
 /**
+ * Called from ActorRGB() - Stores actor's speech colour.
+ */
+
+void SetActorRGB(int ano, COLORREF colour) {
+	assert(ano >= 0 && ano <= NumActors);
+
+	if (ano)
+		actorInfo[ano - 1].textColour = colour;
+	else
+		defaultColour = colour;
+}
+
+/**
  * Get the actor's stored speech colour.
  * @param ano			Actor Id
  */
-COLORREF getActorTcol(int ano) {
+COLORREF GetActorRGB(int ano) {
 	// Not used in JAPAN version
-	assert(ano > 0 && ano <= NumActors); // illegal actor number
+	assert((ano >= -1) && (ano <= NumActors)); // illegal actor number
 
-	if (actorInfo[ano - 1].tColour)
-		return actorInfo[ano - 1].tColour;
+	if ((ano == -1) || !actorInfo[ano - 1].textColour)
+		return defaultColour;
 	else
-		return defaultColour;
+		return actorInfo[ano - 1].textColour;
 }
 
 /**
+ * Set the actor's Z-factor
+ */
+void SetActorZfactor(int ano, uint32 zFactor) {
+	RANGE_CHECK(ano);
+
+	zFactors[ano - 1] = (uint8)zFactor;
+}
+
+uint32 GetActorZfactor(int ano) {
+	RANGE_CHECK(ano);
+
+	return zFactors[ano - 1];
+}
+
+/**
  * Store relevant information pertaining to currently existing actors.
  */
 int SaveActors(SAVED_ACTOR *sActorInfo) {
-	int	i, j;
+	int	i, j, k;
 
 	for (i = 0, j = 0; i < NumActors; i++) {
-		if (actorInfo[i].presObj != NULL) {
-			assert(j < MAX_SAVED_ACTORS); // Saving too many actors
+		for (k = 0; k < (TinselV2 ? MAX_REELS : 1); ++k) {
+			bool presFlag = !TinselV2 ? actorInfo[i].presObj != NULL :
+				(actorInfo[i].presObjs[k] != NULL) && !IsCdPlayHandle(actorInfo[i].presFilm);
+			if (presFlag) {
 
-//			sActorInfo[j].hidden	= actorInfo[i].hidden;
-			sActorInfo[j].bAlive	= actorInfo[i].alive;
-//			sActorInfo[j].x		= (short)actorInfo[i].x;
-//			sActorInfo[j].y		= (short)actorInfo[i].y;
-			sActorInfo[j].z		= (short)actorInfo[i].z;
-//			sActorInfo[j].presReel	= actorInfo[i].presReel;
-			sActorInfo[j].presRnum	= (short)actorInfo[i].presRnum;
-			sActorInfo[j].presFilm	= actorInfo[i].presFilm;
-			sActorInfo[j].presX	= (short)actorInfo[i].presX;
-			sActorInfo[j].presY	= (short)actorInfo[i].presY;
-			sActorInfo[j].actorID	= (short)(i+1);
-			j++;
+				assert(j < MAX_SAVED_ACTORS); // Saving too many actors
+
+				if (!TinselV2) {
+					sActorInfo[j].bAlive	= actorInfo[i].bAlive;
+					sActorInfo[j].zFactor	= (short)actorInfo[i].z;
+					sActorInfo[j].presRnum	= (short)actorInfo[i].presRnum;
+				}
+
+				sActorInfo[j].actorID	= (short)(i+1);
+				if (TinselV2)
+					sActorInfo[j].bHidden	= actorInfo[i].bHidden;
+	//			sActorInfo[j].x		= (short)actorInfo[i].x;
+	//			sActorInfo[j].y		= (short)actorInfo[i].y;
+	//			sActorInfo[j].presReel	= actorInfo[i].presReel;
+				sActorInfo[j].presFilm	= actorInfo[i].presFilm;
+				sActorInfo[j].presPlayX	= (short)actorInfo[i].presPlayX;
+				sActorInfo[j].presPlayY	= (short)actorInfo[i].presPlayY;
+				j++;
+
+				break;
+			}
 		}
 	}
 
 	return j;
 }
 
+/**
+ * Restore actor data
+ */
+void RestoreActors(int numActors, PSAVED_ACTOR sActorInfo) {
+	int	i, aIndex;
+
+	for (i = 0; i < numActors; i++) {
+		aIndex = sActorInfo[i].actorID - 1;
+
+		actorInfo[aIndex].bHidden = sActorInfo[i].bHidden;
+
+		// Play the same reel.
+		if (sActorInfo[i].presFilm != 0) {
+			RestoreActorReels(sActorInfo[i].presFilm, sActorInfo[i].actorID,
+				sActorInfo[i].presPlayX, sActorInfo[i].presPlayY);
+		}
+	}
+}
+
+void SaveZpositions(void *zpp) {
+	memcpy(zpp, zPositions, sizeof(zPositions));
+}
+
+void RestoreZpositions(void *zpp) {
+	memcpy(zPositions, zpp, sizeof(zPositions));
+}
+
+void SaveActorZ(byte *saveActorZ) {
+	assert(NumActors <= MAX_SAVED_ACTOR_Z);
+
+	memcpy(saveActorZ, zFactors, NumActors);
+}
+
+void RestoreActorZ(byte *saveActorZ) {
+	memcpy(zFactors, saveActorZ, NumActors);
+}
+
 void setactorson(void) {
 	bActorsOn = true;
 }
@@ -880,18 +1412,307 @@
 void ActorsLife(int ano, bool bAlive) {
 	assert((ano > 0 && ano <= NumActors) || ano == -1); // illegal actor number
 
-	actorInfo[ano-1].alive = bAlive;
+	actorInfo[ano-1].bAlive = bAlive;
 }
 
 
 void syncAllActorsAlive(Serializer &s) {
 	for (int i = 0; i < MAX_SAVED_ALIVES; i++) {
-		s.syncAsByte(actorInfo[i].alive);
+		s.syncAsByte(actorInfo[i].bAlive);
 		s.syncAsByte(actorInfo[i].tagged);
 		s.syncAsByte(actorInfo[i].tType);
 		s.syncAsUint32LE(actorInfo[i].hTag);
 	}
 }
 
+/**
+ * Called from EndActor()
+ */
+void dwEndActor(int ano) {
+	int i;
 
+	RANGE_CHECK(ano);
+
+	// Make play.c think it's been replaced
+// The following line may have been indirectly making text go away!
+//	actorInfo[ano - 1].presFilm = NULL;
+// but things were returning after a cut scene.
+// so re-instate it and de-register the object
+	actorInfo[ano - 1].presFilm = 0;
+	actorInfo[ano-1].filmNum++;
+
+	for (i = 0; i < MAX_REELS; i++) {
+		// It may take a frame to remove this, so make it invisible
+		if (actorInfo[ano-1].presObjs[i] != NULL) {
+			MultiHideObject(actorInfo[ano-1].presObjs[i]);
+			actorInfo[ano-1].presObjs[i] = NULL;
+		}
+	}
+}
+
+
+/**
+ * Returns a tagged actor's tag portion.
+ */
+void GetActorTagPortion(int ano, unsigned *top, unsigned *bottom, unsigned *left, unsigned *right) {
+	// Convert actor number to index
+	ano = TaggedActorIndex(ano);
+
+	*top = taggedActors[ano].tagPortionV >> 16;
+	*bottom = taggedActors[ano].tagPortionV & 0xffff;
+	*left = taggedActors[ano].tagPortionH >> 16;
+	*right = taggedActors[ano].tagPortionH & 0xffff;
+
+	// ensure validity
+	assert(*top >= 1 && *top <= 8);
+	assert(*bottom >= *top && *bottom <= 8);
+	assert(*left >= 1 && *left <= 8);
+	assert(*right >= *left && *right <= 8);
+}
+
+/**
+ * Returns handle to tagged actor's tag text.
+ */
+SCNHANDLE GetActorTagHandle(int ano) {
+	// Convert actor number to index
+	ano = TaggedActorIndex(ano);
+
+	return taggedActors[ano].hOverrideTag ?
+		taggedActors[ano].hOverrideTag : taggedActors[ano].hTagText;
+}
+
+void SetActorPointedTo(int actor, bool bPointedTo) {
+	// Convert actor number to index
+	actor = TaggedActorIndex(actor);
+
+	if (bPointedTo)
+		taggedActors[actor].tagFlags |= POINTING;
+	else
+		taggedActors[actor].tagFlags &= ~POINTING;
+}
+
+bool ActorIsPointedTo(int actor) {
+	// Convert actor number to index
+	actor = TaggedActorIndex(actor);
+
+	return (taggedActors[actor].tagFlags & POINTING);
+}
+
+void SetActorTagWanted(int actor, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag) {
+	// Convert actor number to index
+	actor = TaggedActorIndex(actor);
+
+	if (bTagWanted) {
+		taggedActors[actor].tagFlags |= TAGWANTED;
+		taggedActors[actor].hOverrideTag = hOverrideTag;
+	} else {
+		taggedActors[actor].tagFlags &= ~TAGWANTED;
+		taggedActors[actor].hOverrideTag = 0;
+	}
+
+	if (bCursor)
+		taggedActors[actor].tagFlags |= FOLLOWCURSOR;
+	else
+		taggedActors[actor].tagFlags &= ~FOLLOWCURSOR;
+}
+
+bool ActorTagIsWanted(int actor) {
+	// Convert actor number to index
+	actor = TaggedActorIndex(actor);
+
+	return (taggedActors[actor].tagFlags & TAGWANTED);
+}
+
+/**
+ * Given cursor position and an actor number, ascertains 
+ * whether the cursor is within the actor's tag area.
+ * Returns True for a positive result, False for negative.
+ */
+bool InHotSpot(int ano, int curX, int curY) {
+	int	aTop, aBot;	// Top and bottom limits }
+	int	aHeight;	// Height		 } of active area
+	int	aLeft, aRight;	// Left and right	 }
+	int	aWidth;		// Width		 }
+	unsigned topEighth, botEighth, leftEighth, rightEighth;
+
+	// First check if within broad range
+	if (curX < (aLeft = GetActorLeft(ano))		// too far left
+		||  curX > (aRight = GetActorRight(ano))	// too far right
+		||  curY < (aTop = GetActorTop(ano))		// too high
+		||  curY > (aBot = GetActorBottom(ano)) )	// too low
+			return false;
+
+	GetActorTagPortion(ano, &topEighth, &botEighth, &leftEighth, &rightEighth);
+
+	aWidth = aRight - aLeft;
+	aLeft += ((leftEighth - 1)*aWidth)/8;
+	aRight -= ((8 - rightEighth)*aWidth)/8;
+
+	// check if within x-range
+	if (curX < aLeft || curX > aRight)
+		return false;
+
+	aHeight = aBot - aTop;
+	aTop += ((topEighth - 1)*aHeight)/8;
+	aBot -= ((8 - botEighth)*aHeight)/8;
+
+	// check if within y-range
+	if (curY < aTop || curY > aBot)
+		return false;
+
+	return true;
+}
+
+/**
+ * Front Tagged Actor
+ */
+int FrontTaggedActor(void) {
+	int i;
+
+	for (i = 0; i < numTaggedActors; i++) {
+		if (taggedActors[i].tagFlags & POINTING)
+			return taggedActors[i].id;
+	}
+	return 0;
+}
+
+/**
+ * GetActorTagPos
+ */
+void GetActorTagPos(int actor, int *pTagX, int *pTagY, bool bAbsolute) {
+	unsigned topEighth, botEighth;
+	int	aTop;		// Top and bottom limits }
+	int	aHeight;	// Height		 } of active area
+	int	Loffset, Toffset;
+
+	GetActorTagPortion(actor, &topEighth, &botEighth, (unsigned *)&Loffset, (unsigned *)&Toffset);
+
+	aTop = GetActorTop(actor);
+	aHeight = GetActorBottom(actor) - aTop;
+	aTop += ((topEighth - 1) * aHeight) / 8;
+
+	*pTagX = ((GetActorLeft(actor) + GetActorRight(actor)) / 2);
+	*pTagY = aTop;
+
+	if (!bAbsolute) {
+		PlayfieldGetPos(FIELD_WORLD, &Loffset, &Toffset);
+		*pTagX -= Loffset;
+		*pTagY -= Toffset;
+	}
+}
+
+/**
+ * Is Tagged Actor
+ */
+bool IsTaggedActor(int actor) {
+	int i;
+
+	for (i = 0; i < numTaggedActors; i++) {
+		if (taggedActors[i].id == actor)
+			return true;
+	}
+	return false;
+}
+
+/**
+ * StoreActorPresFilm
+ */
+void StoreActorPresFilm(int ano, SCNHANDLE hFilm, int x, int y) {
+	int i;
+
+	RANGE_CHECK(ano);
+
+	actorInfo[ano-1].presFilm = hFilm;
+	actorInfo[ano-1].presPlayX = x;
+	actorInfo[ano-1].presPlayY = y;
+	actorInfo[ano-1].filmNum++;
+
+	for (i = 0; i < MAX_REELS; i++) {
+		// It may take a frame to remove this, so make it invisible
+		if (actorInfo[ano - 1].presObjs[i] != NULL)
+			MultiHideObject(actorInfo[ano - 1].presObjs[i]);
+
+		actorInfo[ano - 1].presColumns[i] = -1;
+		actorInfo[ano - 1].presObjs[i] = NULL;
+	}
+}
+
+/**
+ * GetActorPresFilm
+ */
+SCNHANDLE GetActorPresFilm(int ano) {
+	RANGE_CHECK(ano);
+
+	return actorInfo[ano - 1].presFilm;
+}
+
+
+/**
+ * GetActorFilmNumber
+ */
+int GetActorFilmNumber(int ano) {
+	RANGE_CHECK(ano);
+
+	return actorInfo[ano - 1].filmNum;
+}
+
+/**
+ * More properly should be called:
+ *		'StoreActorReelAndObject()'
+ */
+void StoreActorReel(int actor, int column, OBJECT *pObj) {
+	RANGE_CHECK(actor);
+	int i;
+ 
+	for (i = 0; i < MAX_REELS; i++) {
+		if (actorInfo[actor-1].presColumns[i] == -1) {
+			// Store reel and object
+			actorInfo[actor - 1].presColumns[i] = column;
+			actorInfo[actor - 1].presObjs[i] = pObj;
+			break;
+		}
+	}
+
+	assert(i < MAX_REELS);
+}
+
+/**
+ * NotPlayingReel
+ */
+void NotPlayingReel(int actor, int filmNumber, int column) {
+	int	i;
+
+	RANGE_CHECK(actor);
+
+	if (actorInfo[actor-1].filmNum != filmNumber)
+		return;
+
+	// De-register this reel
+	for (i = 0; i < MAX_REELS; i++) {
+		if (actorInfo[actor-1].presColumns[i] == column) {
+			actorInfo[actor-1].presObjs[i] = NULL;
+			actorInfo[actor-1].presColumns[i] = -1;
+			break;
+		}
+	}
+
+	// De-register the film if this was the last reel
+	for (i = 0; i < MAX_REELS; i++) {
+		if (actorInfo[actor-1].presColumns[i] != -1)
+			break;
+	}
+	if (i == MAX_REELS)
+		actorInfo[actor-1].presFilm = 0;
+}
+
+bool ActorReelPlaying(int actor, int column) {
+	RANGE_CHECK(actor);
+
+	for (int i = 0; i < MAX_REELS; i++) {
+		if (actorInfo[actor - 1].presColumns[i] == column)
+			return true;
+	}
+	return false;
+}
+
 } // end of namespace Tinsel

Modified: scummvm/trunk/engines/tinsel/actors.h
===================================================================
--- scummvm/trunk/engines/tinsel/actors.h	2008-11-30 21:17:58 UTC (rev 35195)
+++ scummvm/trunk/engines/tinsel/actors.h	2008-12-01 20:35:36 UTC (rev 35196)
@@ -29,24 +29,30 @@
 
 
 #include "tinsel/dw.h"		// for SCNHANDLE
-#include "tinsel/events.h"	// for USER_EVENT
+#include "tinsel/events.h"	// for TINSEL_EVENT
 #include "tinsel/palette.h"	// for COLORREF
 
 namespace Tinsel {
 
 struct FREEL;
 struct INT_CONTEXT;
-struct MACTOR;
+struct MOVER;
 struct OBJECT;
 
+#define ACTORTAG_KEY 0x1000000
 
+#define OTH_RELATEDACTOR	0x00000fff
+#define OTH_RELATIVE		0x00001000
+#define OTH_ABSOLUTE		0x00002000
+
 /*----------------------------------------------------------------------*/
 
 void RegisterActors(int num);
 void FreeActors(void);
-void setleadid(int rid);
-int LeadId(void);
-void StartActors(SCNHANDLE ah, int numActors, bool bRunScript);
+void SetLeadId(int rid);
+int GetLeadId(void);
+bool ActorIsGhost(int actor);
+void StartTaggedActors(SCNHANDLE ah, int numActors, bool bRunScript);
 void DropActors(void);		// No actor reels running
 void DisableActor(int actor);
 void EnableActor(int actor);
@@ -63,61 +69,111 @@
 int GetActorRight(int ano);
 int GetActorTop(int ano);
 int GetActorBottom(int ano);
-void HideActor(int ano);
+void ShowActor(CORO_PARAM, int ano);
+void HideActor(CORO_PARAM, int ano);
+bool ActorHidden(int ano);
 bool HideMovingActor(int id, int sf);
 void unHideMovingActor(int id);
 void restoreMovement(int id);
-void storeActorReel(int ano, const FREEL *reel, SCNHANDLE film, OBJECT *pobj, int reelnum, int x, int y);
+void storeActorReel(int ano, const FREEL *reel, SCNHANDLE hFilm, OBJECT *pobj, int reelnum, int x, int y);
 const FREEL *actorReel(int ano);
 SCNHANDLE actorFilm(int ano);
 
-void setActorPlayFilm(int ano, SCNHANDLE film);
-SCNHANDLE getActorPlayFilm(int ano);
-void setActorTalkFilm(int ano, SCNHANDLE film);
-SCNHANDLE getActorTalkFilm(int ano);
-void setActorTalking(int ano, bool tf);
-bool isActorTalking(int ano);
-void setActorLatestFilm(int ano, SCNHANDLE film);
-SCNHANDLE getActorLatestFilm(int ano);
+void SetActorPlayFilm(int ano, SCNHANDLE hFilm);
+SCNHANDLE GetActorPlayFilm(int ano);
+void SetActorTalkFilm(int ano, SCNHANDLE hFilm);
+SCNHANDLE GetActorTalkFilm(int ano);
+void SetActorTalking(int ano, bool tf);
+bool ActorIsTalking(int ano);
+void SetActorLatestFilm(int ano, SCNHANDLE hFilm);
+SCNHANDLE GetActorLatestFilm(int ano);
 
-void updateActorEsc(int ano, bool escOn, int escEv);
-bool actorEsc(int ano);
-int actorEev(int ano);
-void storeActorPos(int ano, int x, int y);
-void storeActorSteps(int ano, int steps);
-int getActorSteps(int ano);
-void storeActorZpos(int ano, int z);
+void UpdateActorEsc(int ano, bool escOn, int escEvent);
+void UpdateActorEsc(int ano, int escEvent);
+bool ActorEsc(int ano);
+int ActorEev(int ano);
+void StoreActorPos(int ano, int x, int y);
+void StoreActorSteps(int ano, int steps);
+int GetActorSteps(int ano);
+void StoreActorZpos(int ano, int z, int column = -1);
+int GetActorZpos(int ano, int column);
+void IncLoopCount(int ano);
+int GetLoopCount(int ano);
 SCNHANDLE GetActorTag(int ano);
 void FirstTaggedActor(void);
 int NextTaggedActor(void);
+int NextTaggedActor(int previous);
 int AsetZPos(OBJECT *pObj, int y, int32 zFactor);
-void MAsetZPos(MACTOR *pActor, int y, int32 zFactor);
-void actorEvent(int ano, USER_EVENT event, BUTEVENT be);
+void SetMoverZ(MOVER *pMover, int y, int32 zFactor);
+void ActorEvent(int ano, TINSEL_EVENT event, PLR_EVENT be);
 
 void storeActorAttr(int ano, int r1, int g1, int b1);
-COLORREF getActorTcol(int ano);
+COLORREF GetActorRGB(int ano);
+void SetActorRGB(int ano, COLORREF colour);
+void SetActorZfactor(int ano, uint32 zFactor);
+uint32 GetActorZfactor(int ano);
 
 void setactorson(void);
 
 void ActorsLife(int id, bool bAlive);
 
+void dwEndActor(int ano);
+
+void ActorEvent(CORO_PARAM, int ano, TINSEL_EVENT tEvent, bool bWait, int myEscape, bool *result = NULL);
+
+void GetActorTagPortion(int ano, unsigned *top, unsigned *bottom, unsigned *left, unsigned *right);
+SCNHANDLE GetActorTagHandle(int ano);
+void SetActorPointedTo(int actor, bool bPointedTo);
+bool ActorIsPointedTo(int actor);
+void SetActorTagWanted(int actor, bool bTagWanted, bool bCursor, SCNHANDLE hOverrideTag);
+bool ActorTagIsWanted(int actor);
+bool InHotSpot(int ano, int curX, int curY);
+int FrontTaggedActor(void);
+void GetActorTagPos(int actor, int *pTagX, int *pTagY, bool bAbsolute);
+bool IsTaggedActor(int actor);
+void StoreActorPresFilm(int ano, SCNHANDLE hFilm, int x, int y);
+SCNHANDLE GetActorPresFilm(int ano);
+int GetActorFilmNumber(int ano);
+void StoreActorReel(int actor, int column, OBJECT *pObj);
+void NotPlayingReel(int actor, int filmNumber, int column);
+bool ActorReelPlaying(int actor, int column);
+void SetActorPlayFilm(int ano, SCNHANDLE hFilm);
+SCNHANDLE GetActorPlayFilm(int ano);
+
 /*----------------------------------------------------------------------*/
 
 struct SAVED_ACTOR {
 	short		actorID;
-	short		z;
+	short		zFactor;
 	bool		bAlive;
+	bool		bHidden;
 	SCNHANDLE 	presFilm;	//!< the film that reel belongs to
 	short		presRnum;	//!< the present reel number
-	short		presX, presY;
+	short		presPlayX, presPlayY;
 };
+typedef SAVED_ACTOR *PSAVED_ACTOR;
 
+#define NUM_ZPOSITIONS	200	// Reasonable-sounding number
+
+struct Z_POSITIONS {
+	short	actor;
+	short	column;
+	int		z;
+};
+
 int SaveActors(SAVED_ACTOR *sActorInfo);
 
-	
 void RestoreActorProcess(int id, INT_CONTEXT *pic);
 
+int SaveActors(PSAVED_ACTOR sActorInfo);
+void RestoreActors(int numActors, PSAVED_ACTOR sActorInfo);
 
+void SaveZpositions(void *zpp);
+void RestoreZpositions(void *zpp);
+
+void SaveActorZ(byte *saveActorZ);
+void RestoreActorZ(byte *saveActorZ);
+
 /*----------------------------------------------------------------------*/
 
 } // end of namespace Tinsel

Modified: scummvm/trunk/engines/tinsel/anim.cpp
===================================================================
--- scummvm/trunk/engines/tinsel/anim.cpp	2008-11-30 21:17:58 UTC (rev 35195)
+++ scummvm/trunk/engines/tinsel/anim.cpp	2008-12-01 20:35:36 UTC (rev 35196)
@@ -29,32 +29,12 @@
 #include "tinsel/multiobj.h"	// multi-part object defintions etc.
 #include "tinsel/object.h"
 #include "tinsel/sched.h"
+#include "tinsel/tinsel.h"
 
 #include "common/util.h"
 
 namespace Tinsel {
 
-/** Animation script commands */
-enum {
-	ANI_END			= 0,	//!< end of animation script
-	ANI_JUMP		= 1,	//!< animation script jump
-	ANI_HFLIP		= 2,	//!< flip animated object horizontally
-	ANI_VFLIP		= 3,	//!< flip animated object vertically
-	ANI_HVFLIP		= 4,	//!< flip animated object in both directions
-	ANI_ADJUSTX		= 5,	//!< adjust animated object x animation point
-	ANI_ADJUSTY		= 6,	//!< adjust animated object y animation point
-	ANI_ADJUSTXY	= 7,	//!< adjust animated object x & y animation points
-	ANI_NOSLEEP		= 8,	//!< do not sleep for this frame
-	ANI_CALL		= 9,	//!< call routine
-	ANI_HIDE		= 10	//!< hide animated object
-};
-
-/** animation script command possibilities */
-union ANI_SCRIPT {
-	int32 op;		//!< treat as an opcode or operand
-	uint32 hFrame;	//!< treat as a animation frame handle
-};
-
 /**
  * Advance to next frame routine.
  * @param pAnim			Animation data structure
@@ -64,6 +44,9 @@
 	const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);
 
 	while (1) {	// repeat until a real image
+		debugC(DEBUG_DETAILED, kTinselDebugAnimations, 
+		"DoNextFrame %ph index=%d, op=%xh", (byte *)pAnim, pAnim->scriptIndex, 
+		FROM_LE_32(pAni[pAnim->scriptIndex].op));
 
 		switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) {
 		case ANI_END:	// end of animation script
@@ -104,7 +87,6 @@
 
 			// go fetch a real image
 			break;
-
 		case ANI_HVFLIP:	// flip animated object in both directions
 
 			// next opcode
@@ -230,6 +212,12 @@
 void InitStepAnimScript(ANIM *pAnim, OBJECT *pAniObj, SCNHANDLE hNewScript, int aniSpeed) {
 	OBJECT *pObj;			// multi-object list iterator
 
+	debugC(DEBUG_DETAILED, kTinselDebugAnimations, 
+		"InitStepAnimScript Object=(%d,%d,%xh) script=%xh aniSpeed=%d rec=%ph",
+		!pAniObj ? 0 : fracToInt(pAniObj->xPos),
+		!pAniObj ? 0 : fracToInt(pAniObj->yPos),
+		!pAniObj ? 0 : pAniObj->hImg, hNewScript, aniSpeed, (byte *)pAnim);
+
 	pAnim->aniDelta = 1;		// will animate on next call to NextAnimRate
 	pAnim->pObject = pAniObj;	// set object to animate
 	pAnim->hScript = hNewScript;	// set animation script
@@ -254,9 +242,13 @@
 		// re-init animation delta counter
 		pAnim->aniDelta = pAnim->aniRate;
 
-		// move to next frame
-		while ((state = DoNextFrame(pAnim)) == ScriptNoSleep)
-			;
+		if (TinselV2)
+			state = DoNextFrame(pAnim);
+		else {
+			// move to next frame
+			while ((state = DoNextFrame(pAnim)) == ScriptNoSleep)
+				;
+		}
 
 		return state;
 	}
@@ -274,7 +266,7 @@
 	// get a pointer to the script
 	const ANI_SCRIPT *pAni = (const ANI_SCRIPT *)LockMem(pAnim->hScript);
 
-	if (numFrames <= 0)
+	if (!TinselV2 && (numFrames <= 0))
 		// do nothing
 		return;
 
@@ -282,9 +274,10 @@
 
 		switch ((int32)FROM_LE_32(pAni[pAnim->scriptIndex].op)) {
 		case ANI_END:	// end of animation script
-			// going off the end is probably a error
-			error("SkipFrames(): formally 'assert(0)!'");
-			break;
+			// going off the end is probably a error, but only in Tinsel 1
+			if (!TinselV2)
+				error("SkipFrames(): formally 'assert(0)!'");
+			return;
 
 		case ANI_JUMP:	// do animation jump
 
@@ -293,6 +286,10 @@
 
 			// jump to new frame position
 			pAnim->scriptIndex += (int32)FROM_LE_32(pAni[pAnim->scriptIndex].op);
+
+			if (TinselV2)
+				// Done if skip to jump
+				return;
 			break;
 
 		case ANI_HFLIP:	// flip animated object horizontally
@@ -383,7 +380,10 @@
 		default:	// must be an actual animation frame handle
 
 			// one less frame
-			if (numFrames-- > 0) {
+			if (numFrames == 0)
+				return;
+
+			if (numFrames == -1 || numFrames-- > 0) {
 				// next opcode
 				pAnim->scriptIndex++;
 			} else {
@@ -401,4 +401,48 @@
 	}
 }
 
+/**
+ * About to jump or end
+ * @param pAnim			Animation data structure
+ */
+bool AboutToJumpOrEnd(PANIM pAnim) {
+	if (pAnim->aniDelta == 1) {
+		// get a pointer to the script
+		ANI_SCRIPT *pAni = (ANI_SCRIPT *)LockMem(pAnim->hScript);
+		int	zzz = pAnim->scriptIndex;
+
+		for (;;) {
+			// repeat until a real image
+			switch (FROM_LE_32(pAni[zzz].op)) {
+			case ANI_END:		// end of animation script
+			case ANI_JUMP:		// do animation jump
+				return true;
+
+			case ANI_HFLIP:		// flip animated object horizontally
+			case ANI_VFLIP:		// flip animated object vertically
+			case ANI_HVFLIP:	// flip animated object in both directions
+				zzz++;
+				break;
+
+			case ANI_ADJUSTX:	// adjust animated object x animation point
+			case ANI_ADJUSTY:	// adjust animated object y animation point
+				zzz += 2;
+				break;
+
+			case ANI_ADJUSTXY:	// adjust animated object x & y animation points
+				zzz += 3;
+				break;
+
+			case ANI_HIDE:		// hide animated object
+			default:		// must be an actual animation frame handle
+				return false;
+			}
+		}
+	}
+
+	return false;
+}
+
+
+
 } // end of namespace Tinsel

Modified: scummvm/trunk/engines/tinsel/anim.h
===================================================================
--- scummvm/trunk/engines/tinsel/anim.h	2008-11-30 21:17:58 UTC (rev 35195)
+++ scummvm/trunk/engines/tinsel/anim.h	2008-12-01 20:35:36 UTC (rev 35196)
@@ -41,8 +41,34 @@
 	uint32 hScript;		//!< animation script handle
 	int scriptIndex;	//!< current position in animation script
 };
+typedef ANIM *PANIM;
 
+typedef void (*PANI_ADDR)(struct ANIM *);
 
+/** Animation script commands */
+enum {
+	ANI_END			= 0,	//!< end of animation script
+	ANI_JUMP		= 1,	//!< animation script jump
+	ANI_HFLIP		= 2,	//!< flip animated object horizontally
+	ANI_VFLIP		= 3,	//!< flip animated object vertically
+	ANI_HVFLIP		= 4,	//!< flip animated object in both directions
+	ANI_ADJUSTX		= 5,	//!< adjust animated object x animation point
+	ANI_ADJUSTY		= 6,	//!< adjust animated object y animation point
+	ANI_ADJUSTXY	= 7,	//!< adjust animated object x & y animation points
+	ANI_NOSLEEP		= 8,	//!< do not sleep for this frame
+	ANI_CALL		= 9,	//!< call routine
+	ANI_HIDE		= 10,	//!< hide animated object
+	ANI_STOP		= 11	//!< stop sound
+};
+
+/** animation script command possibilities */
+union ANI_SCRIPT {
+	int32 op;			//!< treat as an opcode or operand
+	uint32 hFrame;		//!< treat as a animation frame handle
+//	PANI_ADDR pFunc;	//!< treat as a animation function call
+};
+
+
 /*----------------------------------------------------------------------*\
 |*			Anim Function Prototypes			*|
 \*----------------------------------------------------------------------*/
@@ -66,6 +92,8 @@
 	ANIM *pAnim,		// animation data structure
 	int numFrames);		// number of frames to skip
 
+bool AboutToJumpOrEnd(PANIM pAnim);
+
 } // end of namespace Tinsel
 
 #endif		// TINSEL_ANIM_H

Modified: scummvm/trunk/engines/tinsel/background.cpp
===================================================================
--- scummvm/trunk/engines/tinsel/background.cpp	2008-11-30 21:17:58 UTC (rev 35195)
+++ scummvm/trunk/engines/tinsel/background.cpp	2008-12-01 20:35:36 UTC (rev 35196)
@@ -37,6 +37,9 @@
 // current background
 BACKGND *pCurBgnd = NULL;
 
+// FIXME: Not yet used
+static bool bEntireRedraw;
+
 /**
  * Called to initialise a background.
  * @param pBgnd			Pointer to data struct for current background
@@ -125,6 +128,27 @@
 }
 
 /**
+ * Returns the x position of the centre of the specified playfield
+ * @param which			Which playfield
+ */
+
+int PlayfieldGetCentreX(int which) {
+	PLAYFIELD *pPlayfield; // pointer to relavent playfield
+
+	// make sure there is a background
+	assert(pCurBgnd != NULL);
+
+	// make sure the playfield number is in range
+	assert(which >= 0 && which < pCurBgnd->numPlayfields);
+
+	// get playfield pointer
+	pPlayfield = pCurBgnd->fieldArray + which;
+
+	// get current integer position
+	return fracToInt(pPlayfield->fieldX) + SCREEN_WIDTH/2;
+}
+
+/**
  * Returns the display list for the specified playfield.
  * @param which			Which playfield
  */
@@ -229,4 +253,9 @@
 	ResetClipRect();
 }
 
+void ForceEntireRedraw(void) {
+	bEntireRedraw = true;
+}
+
+
 } // end of namespace Tinsel

Modified: scummvm/trunk/engines/tinsel/background.h
===================================================================
--- scummvm/trunk/engines/tinsel/background.h	2008-11-30 21:17:58 UTC (rev 35195)
+++ scummvm/trunk/engines/tinsel/background.h	2008-12-01 20:35:36 UTC (rev 35196)
@@ -27,10 +27,11 @@
 #ifndef TINSEL_BACKGND_H     // prevent multiple includes
 #define TINSEL_BACKGND_H
 
+#include "common/frac.h"
+#include "common/rect.h"
+#include "tinsel/coroutine.h"
 #include "tinsel/dw.h"	// for SCNHANDLE
 #include "tinsel/palette.h"	// palette definitions
-#include "common/frac.h"
-#include "common/rect.h"
 
 namespace Tinsel {
 
@@ -78,6 +79,8 @@
 void InitBackground(		// called to initialise a background
 	BACKGND *pBgnd);	// pointer to data struct for current background
 
+void StartupBackground(CORO_PARAM, SCNHANDLE hFilm);
+
 void StopBgndScrolling(void);	// Stops all background playfields from scrolling
 
 void PlayfieldSetPos(		// Sets the xy position of the specified playfield in the current background
@@ -90,6 +93,9 @@
 	int *pXpos,		// returns current x position
 	int *pYpos);		// returns current y position
 
+int PlayfieldGetCentreX( 	// Returns the xy position of the specified playfield in the current background
+	int which);		// which playfield
+
 OBJECT *GetPlayfieldList(	// Returns the display list for the specified playfield
 	int which);		// which playfield
 
@@ -100,8 +106,16 @@
 
 void RedrawBackgnd(void);	// Completely redraws all the playfield object lists for the current background
 
-SCNHANDLE BackPal(void);
+OBJECT *GetBgObject();
 
+SCNHANDLE BgPal(void);
+
+void ForceEntireRedraw(void);
+
+int BgWidth(void);
+
+int BgHeight(void);
+
 } // end of namespace Tinsel
 
 #endif	// TINSEL_BACKGND_H

Modified: scummvm/trunk/engines/tinsel/bg.cpp
===================================================================
--- scummvm/trunk/engines/tinsel/bg.cpp	2008-11-30 21:17:58 UTC (rev 35195)
+++ scummvm/trunk/engines/tinsel/bg.cpp	2008-12-01 20:35:36 UTC (rev 35196)
@@ -37,7 +37,8 @@
 #include "tinsel/pid.h"
 #include "tinsel/sched.h"
 #include "tinsel/timers.h"		// For ONE_SECOND constant
-#include "tinsel/tinlib.h"		// For control()
+#include "tinsel/tinlib.h"		// For Control()
+#include "tinsel/tinsel.h"
 
 #include "common/util.h"
 
@@ -45,49 +46,61 @@
 
 //----------------- LOCAL GLOBAL DATA --------------------
 
-static SCNHANDLE BackPalette = 0;	// Background's palette
-static OBJECT *pBG = 0;		// The main picture's object.
+#define MAX_BG	10
+
+static SCNHANDLE hBgPal = 0;	// Background's palette
+static POBJECT pBG[MAX_BG];
+static ANIM	thisAnim[MAX_BG];	// used by BGmainProcess()
 static int BGspeed = 0;
-static SCNHANDLE BgroundHandle = 0;	// Current scene handle - stored in case of Save_Scene()
-static bool DoFadeIn = false;
-static ANIM	thisAnim;	// used by BGmainProcess()
+static SCNHANDLE hBackground = 0;	// Current scene handle - stored in case of Save_Scene()
+static bool bDoFadeIn = false;
+static int bgReels;
 
 /**
+ * GetBgObject
+ */
+OBJECT *GetBgObject() {
+	return pBG[0];
+}
+
+/**
  * BackPal
  */
-SCNHANDLE BackPal(void) {
-	return BackPalette;
+SCNHANDLE BgPal(void) {
+	return hBgPal;
 }
 
 /**
  * SetDoFadeIn
 */
 void SetDoFadeIn(bool tf) {
-	DoFadeIn = tf;
+	bDoFadeIn = tf;
 }
 
 /**
  * Called before scene change.
  */
 void DropBackground(void) {
-	pBG = NULL;		// No background
-	BackPalette = 0;	// No background palette
+	pBG[0] = NULL;	// No background
+
+	if (!TinselV2)
+		hBgPal = 0;	// No background palette
 }
 
 /**
  * Return the width of the current background.
  */
-int BackgroundWidth(void) {
-	assert(pBG);
-	return MultiRightmost(pBG) + 1;
+int BgWidth(void) {
+	assert(pBG[0]);
+	return MultiRightmost(pBG[0]) + 1;
 }
 
 /**
  * Return the height of the current background.
  */
-int BackgroundHeight(void) {
-	assert(pBG);
-	return MultiLowest(pBG) + 1;
+int BgHeight(void) {
+	assert(pBG[0]);
+	return MultiLowest(pBG[0]) + 1;
 }
 
 /**
@@ -100,90 +113,137 @@
 
 	CORO_BEGIN_CODE(_ctx);
 
-	const FREEL *pfr;
+	const FILM *pFilm;
+	const FREEL *pReel;
 	const MULTI_INIT *pmi;
 
 	// get the stuff copied to process when it was created
-	pfr = (const FREEL *)param;
-
-	if (pBG == NULL) {
+	if (pBG[0] == NULL) {
 		/*** At start of scene ***/
 
-		// Get the MULTI_INIT structure
-		pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pfr->mobj));
+		if (!TinselV2) {
+			pReel = (const FREEL *)param;
 
-		// Initialise and insert the object, and initialise its script.
-		pBG = MultiInitObject(pmi);
-		MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG);
-		InitStepAnimScript(&thisAnim, pBG, FROM_LE_32(pfr->script), BGspeed);
+			// Get the MULTI_INIT structure
+			pmi = (const MULTI_INIT *)LockMem(FROM_LE_32(pReel->mobj));
 
-		if (DoFadeIn) {
-			FadeInFast(NULL);
-			DoFadeIn = false;
+			// Initialise and insert the object, and initialise its script.
+			pBG[0] = MultiInitObject(pmi);
+			MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG[0]);
+			InitStepAnimScript(&thisAnim[0], pBG[0], FROM_LE_32(pReel->script), BGspeed);
+			bgReels = 1;
+		} else {
+			/*** At start of scene ***/
+			pFilm = (const FILM *)LockMem(hBackground);
+			bgReels = pFilm->numreels;
+
+			int i;
+			for (i = 0; i < bgReels; i++) {
+				// Get the MULTI_INIT structure
+				pmi = (PMULTI_INIT) LockMem(pFilm->reels[i].mobj);
+
+				// Initialise and insert the object, and initialise its script.
+				pBG[i] = MultiInitObject(pmi);
+				MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pBG[i]);
+				MultiSetZPosition(pBG[i], 0);
+				InitStepAnimScript(&thisAnim[i], pBG[i], pFilm->reels[i].script, BGspeed);
+
+				if (i > 0)
+					pBG[i-1]->pSlave = pBG[i];
+			}
 		}
 
-		while (StepAnimScript(&thisAnim) != ScriptFinished)
+		if (bDoFadeIn) {
+			FadeInFast(NULL);
+			bDoFadeIn = false;
+		} else if (TinselV2)
+			PokeInTagColour();
+
+		for (;;) {
+			for (int i = 0; i < bgReels; i++) {
+				if (StepAnimScript(&thisAnim[i]) == ScriptFinished)
+					error("Background animation has finished!");
+			}
+
 			CORO_SLEEP(1);
-
-		error("Background animation has finished!");
+		}
 	} else {
 		// New background during scene
+		if (!TinselV2) {
+			pReel = (const FREEL *)param;
+			InitStepAnimScript(&thisAnim[0], pBG[0], FROM_LE_32(pReel->script), BGspeed);
+			StepAnimScript(&thisAnim[0]);
+		} else {
+			pFilm = (const FILM *)LockMem(hBackground);
+			assert(bgReels == pFilm->numreels);
 
-		// Just re-initialise the script.
-		InitStepAnimScript(&thisAnim, pBG, FROM_LE_32(pfr->script), BGspeed);
-		StepAnimScript(&thisAnim);
+			// Just re-initialise the scripts.
+			for (int i = 0; i < bgReels; i++) {
+				InitStepAnimScript(&thisAnim[i], pBG[i], pFilm->reels[i].script, BGspeed);
+				StepAnimScript(&thisAnim[i]);
+			}
+		}
 	}
 
 	CORO_END_CODE;
 }
 
 /**
- * setBackPal()
+ * AetBgPal()
  */
-void setBackPal(SCNHANDLE hPal) {
-	BackPalette = hPal;
+void SetBackPal(SCNHANDLE hPal) {
+	hBgPal = hPal;
 
-	fettleFontPal(BackPalette);
-	CreateTranslucentPalette(BackPalette);
+	FettleFontPal(hBgPal);
+	CreateTranslucentPalette(hBgPal);
 }
 
 void ChangePalette(SCNHANDLE hPal) {
-	SwapPalette(FindPalette(BackPalette), hPal);
+	SwapPalette(FindPalette(hBgPal), hPal);
 
-	setBackPal(hPal);
+	SetBackPal(hPal);
 }
 
 /**
  * Given the scene background film, extracts the palette handle for
  * everything else's use, then starts a display process for each reel
  * in the film.
- * @param bfilm			Scene background film
+ * @param hFilm			Scene background film
  */
-void startupBackground(SCNHANDLE bfilm) {
+void StartupBackground(CORO_PARAM, SCNHANDLE hFilm) {
+	CORO_BEGIN_CONTEXT;
+	CORO_END_CONTEXT(_ctx);
+
+	CORO_BEGIN_CODE(_ctx);
+
 	const FILM *pfilm;
 	IMAGE *pim;
 
-	BgroundHandle = bfilm;		// Save handle in case of Save_Scene()
+	hBackground = hFilm;		// Save handle in case of Save_Scene()
 
-	pim = GetImageFromFilm(bfilm, 0, NULL, NULL, &pfilm);
-	setBackPal(FROM_LE_32(pim->hImgPal));
+	pim = GetImageFromFilm(hFilm, 0, NULL, NULL, &pfilm);
+	SetBackPal(FROM_LE_32(pim->hImgPal));
 
 	// Extract the film speed
 	BGspeed = ONE_SECOND / FROM_LE_32(pfilm->frate);
 
-	if (pBG == NULL)
-		control(CONTROL_STARTOFF);	// New feature - start scene with control off
-
 	// Start display process for each reel in the film
-	assert(FROM_LE_32(pfilm->numreels) == 1); // Multi-reeled backgrounds withdrawn
 	g_scheduler->createProcess(PID_REEL, BGmainProcess, &pfilm->reels[0], sizeof(FREEL));
+
+	if (pBG[0] == NULL)
+		ControlStartOff();
+
+	if (TinselV2 && (coroParam != nullContext))
+		CORO_GIVE_WAY;
+
+	CORO_END_CODE;
 }
 
 /**
  * Return the current scene handle.
  */
 SCNHANDLE GetBgroundHandle(void) {
-	return BgroundHandle;
+	return hBackground;
 }
 
 } // end of namespace Tinsel

Added: scummvm/trunk/engines/tinsel/bmv.cpp
===================================================================
--- scummvm/trunk/engines/tinsel/bmv.cpp	                        (rev 0)
+++ scummvm/trunk/engines/tinsel/bmv.cpp	2008-12-01 20:35:36 UTC (rev 35196)
@@ -0,0 +1,1272 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ * The movie player.
+ */
+
+#include "common/file.h"
+#include "sound/mixer.h"
+#include "sound/audiostream.h"
+#include "tinsel/tinsel.h"
+#include "tinsel/background.h"
+#include "tinsel/cliprect.h"
+#include "tinsel/coroutine.h"
+#include "tinsel/config.h"
+#include "tinsel/dw.h"
+#include "tinsel/events.h"
+#include "tinsel/font.h"
+#include "tinsel/graphics.h"
+#include "tinsel/handle.h"
+#include "tinsel/heapmem.h"
+#include "tinsel/multiobj.h"
+#include "tinsel/object.h"
+#include "tinsel/palette.h"
+#include "tinsel/sched.h"
+#include "tinsel/strres.h"
+#include "tinsel/text.h"
+#include "tinsel/timers.h"
+#include "tinsel/tinlib.h"
+#include "tinsel/tinsel.h"
+
+namespace Tinsel {
+
+//----------------- GLOBAL GLOBAL DATA ------------------------
+
+bool bOldAudio;
+
+//----------------- LOCAL GLOBAL DATA ------------------------
+
+//static READREQ rr;
+
+//----------------- LOCAL DEFINES ----------------------------
+
+#define SZ_C_BLOB	65
+#define SZ_U_BLOB	128
+
+#define BLANK_SOUND	0x0	// for 16 bit silence
+
+#define PT_A	20	// Number of times PT_B may be reached
+#define PT_B	6
+
+
+#define SLOT_SIZE	(25*1024)
+//#define NUM_SLOTS	168
+#define NUM_SLOTS	122		// -> ~ 3MB
+
+
+#define PREFETCH	(NUM_SLOTS/2)	// For initial test
+
+#ifndef _Windows
+//#define ADVANCE_SOUND		12	// 1 second
+#define ADVANCE_SOUND		18	// 1 1/2 second
+//#define MAX_ADVANCE_SOUND	36	// 3 seconds
+#else
+#define ADVANCE_SOUND		18	// 1 1/2 seconds
+#endif
+#define SUBSEQUENT_SOUND	6	// 1/2 second
+
+
+
+// PACKET TYPE IDs & FLAGS
+
+#define CD_SLOT_NOP	0x00	// Skip to next slot
+#define CD_LE_FIN	0x01	// End of movie
+#define CD_PDELTA	0x02	// Image compressed to previous one
+#define CD_SDELTA	0x03	// Image self-compressed
+
+#define BIT0		0x01
+
+#define CD_XSCR		0x04	// Screen has a scroll offset
+#define CD_CMAP 	0x08	// Colour map is included
+#define CD_CMND 	0x10	// Command is included
+#define CD_AUDIO	0x20	// Audio data is included
+#define CD_EXTEND	0x40	// Extended modes "A"-"z"
+#define CD_PRINT	0x80	// goes in conjunction with CD_CMD
+
+// Data field sizes
+#define sz_XSCR_pkt		2
+#define sz_CMAP_pkt		0x300
+#define sz_CMD_TALK_pkt		10
+#define sz_CMD_PRINT_pkt	8
+#define sz_AUDIO_pkt		3675
+
+
+typedef struct {
+
+	short	x;
+	short	y;
+	short	stringId;
+	unsigned char	duration;
+	char	r;			// may be b!
+	char	g;
+	char	b;			// may be r!
+
+} TALK_CMD, *PTALK_CMD;
+
+typedef struct {
+
+	short	x;
+	short	y;
+	short	stringId;
+	unsigned char	duration;
+	unsigned char	fontId;
+
+} PRINT_CMD, *PPRINT_CMD;
+
+
+//----------------- LOCAL GLOBAL DATA ------------------------
+
+// Set when a movie is on
+static bool bMovieOn;
+
+// Set to kill one off
+static bool bAbort;
+
+// For escaping out of movies
+static int bmvEscape;
+
+// Movie file pointer
+static Common::File stream;
+
+// Movie file name
+static char szMovieFile[14];
+
+// Pointers to buffers
+static byte *bigBuffer; //, *screenBuffer;
+
+// Next data to use to extract a frame
+static int nextUseOffset;
+
+// Next data to use to extract sound data
+static int nextSoundOffset;
+
+// When above offset gets to what this is set at, rewind
+static int wrapUseOffset;
+
+// The offset of the most future packet
+static int mostFutureOffset;
+
+// The current frame
+static int currentFrame;
+static int currentSoundFrame;
+
+// Number of packets currently in RAM
+static int numAdvancePackets;
+
+// Next slot that will be read from disc
+static int nextReadSlot;
+
+// Set when the whole file has been read
+static bool bFileEnd;
+
+// Palette
+static COLORREF moviePal[256];
+
+static int blobsInBuffer;
+
+static struct {
+
+	POBJECT	pText;
+	int	dieFrame;
+
+} texts[2];
+
+static COLORREF talkColour;
+
+static int bigProblemCount;
+
+static bool bIsText;
+
+static int movieTick;
+static int startTick;
+static uint32 nextMovieTime = 0;
+
+static int nowTick;
+
+static uint16 Au_Prev1 = 0;
+static uint16 Au_Prev2 = 0;
+static byte *ScreenBeg;
+static byte *screenBuffer;
+
+static bool audioStarted;
+
+static Audio::AppendableAudioStream *audioStream = 0;
+static Audio::SoundHandle audioHandle;
+
+const uint16 Au_DecTable[16] = {16512, 8256, 4128, 2064, 1032, 516, 258, 192,
+		129, 88, 64, 56, 48, 40, 36, 32};
+
+//---------------- DECOMPRESSOR FUNCTIONS --------------------
+
+#define SCREEN_WIDE 640
+#define SCREEN_HIGH 429
+#define SAM_P_BLOB (32 * 2)
+
+#define ROR(x,v) x = ((x >> (v%32)) | (x << (32 - (v%32))));
+#define ROL(x,v) x = ((x << (v%32)) | (x >> (32 - (v%32))));
+#define NEXT_BYTE(v) v = forwardDirection ? v + 1 : v - 1;
+
+static void PrepBMV(const byte *sourceData, int length, short deltaFetchDisp) {
+	uint8 NibbleHi = 0;
+	const byte *saved_esi;
+	uint32 eax = 0;
+	uint32 edx = length;
+	int32 ebx = deltaFetchDisp;
+	uint32 ecx = 0;
+	const byte *esi;
+	byte *edi, *ebp;
+
+	bool forwardDirection = (deltaFetchDisp <= -SCREEN_WIDE) || (deltaFetchDisp >= 0);
+	if (forwardDirection) {
+		// Forward decompression
+		esi = sourceData;
+		edi = ScreenBeg;
+		ebp = ScreenBeg + SCREEN_WIDE * SCREEN_HIGH;
+	} else {
+		esi = sourceData + length - 1;
+		edi = ScreenBeg + SCREEN_WIDE * SCREEN_HIGH - 1;
+		ebp = ScreenBeg - 1;
+	}
+
+	bool firstLoop, flag;
+
+	int loopCtr = 0;
+	for (;;) {
+		flag = false;
+
+		if ((loopCtr == 0) || (edx == 4)) {
+			// Get the next hi,lo nibble
+			eax = (eax & 0xffffff00) | *esi;
+			firstLoop = true;
+		} else {
+			// Get the high nibble
+			eax = eax & 0xffffff00 | (NibbleHi >> 4);
+			firstLoop = false;
+		}
+
+ 		// Is lo nibble '00xx'?
+		if ((eax & 0xC) == 0) {
+			for (;;) {
+//@_rDN_Lp_1:
+				// Only execute this bit first the first time into the loop
+				if (!firstLoop) {
+					ROR(eax, 2);
+					ecx += 2;
+					eax = (eax & 0xffffff00) | *esi;
+
+					if ((eax & 0xC) != 0)
+						break;
+				}
+				firstLoop = false;
+
+//@_rD2nd_1:
+				ROR(eax, 2);		// Save bi-bit into hi 2 bits
+				ecx += 2;			//   and increase bit-shifter
+				// Shift another 2 bits to get hi nibble
+				eax = (eax & 0xffffff00) | ((eax & 0xff) >> 2);
+				NEXT_BYTE(esi);
+			
+				if ((eax & 0xC) != 0) {
+					flag = true;
+					ROL(eax, ecx);
+					break;
+				}
+			}
+		} else if (loopCtr != 0) {
+			flag = edx != 4;
+		}
+
+		if (flag) {
+//@_rdNum__1:
+			edx = 4;			// offset rDNum_Lo ; Next nibble is a 'lo'
+		} else {
+// @_rDNum_1
+			NibbleHi = (uint8)eax;
+			edx = 0;			// offset rDNum_Hi ; Next nibble is a 'hi' (reserved)
+			eax &= 0xffffff0f;
+			NEXT_BYTE(esi);
+			ROL(eax, ecx);
+		}
+//rDN_1:
+//@_rD_or_R:
+		bool actionFlag = (eax & 1) != 0;
+		eax >>= 1;
+		ecx = eax - 1;
+
+		// Move to next loop index
+		if (++loopCtr == 4) loopCtr = 1;
+
+		if (actionFlag) {
+			// Adjust loopCtr to fall into the correct processing case
+			loopCtr = loopCtr % 3 + 1;
+		}
+
+		switch (loopCtr) {
+		case 1:
+			// @_rDelta:
+			saved_esi = esi;			// Save the source pointer
+			esi = edi + ebx;			// Point it to existing data
+
+			while (ecx > 0) {
+				*edi = *esi;
+				NEXT_BYTE(esi);
+				NEXT_BYTE(edi);
+				--ecx;
+			}
+
+			esi = saved_esi;
+			break;
+
+		case 2:
+			// @_rRaw
+			// Copy data from source to dest
+			while (ecx > 0) {
+				*edi = *esi;
+				NEXT_BYTE(esi);
+				NEXT_BYTE(edi);
+				--ecx;
+			}
+			break;
+
+		case 3:
+			// @_rRun
+			// Repeating run of data
+			eax = forwardDirection ? *(edi - 1) : *(edi + 1);
+
+			while (ecx > 0) {
+				*edi = (uint8)eax;
+				NEXT_BYTE(edi);
+				--ecx;
+			}
+			break;
+		default:
+			break;
+		}
+
+		if (edi == ebp)
+			break;		// Exit if complete
+
+		eax = 0;
+	}
+}
+
+static void InitBMV(byte *memoryBuffer) {
+	// Clear the two extra 'off-screen' rows
+	memset(memoryBuffer, 0, SCREEN_WIDE);
+	memset(memoryBuffer + SCREEN_WIDE * (SCREEN_HIGH + 1), 0, SCREEN_WIDE);
+
+	if (audioStream) {
+		_vm->_mixer->stopHandle(audioHandle);
+
+		delete audioStream;
+		audioStream = 0;
+	}
+
+	// Set the screen beginning to the second line (ie. past the off-screen line)
+	ScreenBeg = memoryBuffer + SCREEN_WIDTH;
+	Au_Prev1 = Au_Prev2 = 0;
+}
+
+void PrepAudio(const byte *sourceData, int blobCount, byte *destPtr) {
+	uint16 dx1 = Au_Prev1;
+	uint16 dx2 = Au_Prev2;
+
+	uint16 *destP = (uint16 *) destPtr;
+	int8 *srcP = (int8 *) sourceData;
+
+	// Blob Loop
+	while (blobCount-- > 0) {
+		uint32 ebx = (uint8) *srcP++;
+		uint32 ebp = ebx & 0x1E;
+
+		int blobSize = SAM_P_BLOB / 2;
+
+		ebx = (((ebx & 0x0F) << 4) | ((ebx & 0xF0) >> 4)) & 0x1E;
+
+		ebp = Au_DecTable[ebp >> 1];
+		ebx = Au_DecTable[ebx >> 1];
+
+		// Inner loop
+		while (blobSize-- > 0) {
+			uint32 s1 = (((int32) *srcP++) * ((int32) ebp)) >> 5;
+			uint32 s2 = (((int32) *srcP++) * ((int32) ebx)) >> 5;
+
+			dx1 += s1 & 0xFFFF;
+			dx2 += s2 & 0xFFFF;
+
+			*destP++ = TO_BE_16(dx1);
+			*destP++ = TO_BE_16(dx2);
+		}
+	}
+
+	Au_Prev1 = dx1;
+	Au_Prev2 = dx2;
+}
+
+//----------------- BMV FUNCTIONS ----------------------------
+
+static bool MaintainBuffer(void);
+
+
+/**
+ * Called when a packet contains a palette field.
+ * Build a COLORREF array and queue it to the DAC.
+ */
+static void MoviePalette(int paletteOffset) {
+	int	i;
+	byte *r;
+
+	r = bigBuffer + paletteOffset;
+
+	for (i = 0; i < 256; i++, r += 3) 	{
+		moviePal[i] = RGB(*r, *(r + 1), *(r + 2));
+	}
+
+	UpdateDACqueue(1, 255, &moviePal[1]);
+
+	// Don't clobber talk
+	if (talkColour != 0)
+		SetTextPal(talkColour);
+}
+
+static void InitialiseMovieSound() {
+	audioStream =
+		Audio::makeAppendableAudioStream(22050,
+				Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_STEREO);
+	audioStarted = false;
+}
+
+static void StartMovieSound() {
+}
+
+static void FinishMovieSound() {
+	if (audioStream) {
+		_vm->_mixer->stopHandle(audioHandle);
+
+		delete audioStream;
+		audioStream = 0;
+	}
+}
+
+/**
+ * Called when a packet contains an audio field.
+ */
+static void MovieAudio(int audioOffset, int blobs) {
+	if (audioOffset == 0 && blobs == 0)
+		blobs = 57;
+
+	byte *data = new byte[blobs * 128];
+
+	if (audioOffset != 0)
+		PrepAudio(bigBuffer+audioOffset, blobs, data);
+	else
+		memset(data, 0, blobs * 128);
+
+	audioStream->queueBuffer(data, blobs * 128);
+
+	if (currentSoundFrame == ADVANCE_SOUND) {
+		if (!audioStarted) {
+			_vm->_mixer->playInputStream(Audio::Mixer::kSFXSoundType,
+					&audioHandle, audioStream, -1, Audio::Mixer::kMaxChannelVolume, 0, false);
+			audioStarted = true;
+		}
+	}
+}
+
+/*-----------------------------------------------------*\
+|-------------------------------------------------------|
+\*-----------------------------------------------------*/
+
+void FettleMovieText(void) {
+	int i;
+
+	bIsText = false;
+
+	for (i = 0; i < 2; i++) {
+		if (texts[i].pText) {
+			if (currentFrame > texts[i].dieFrame) {
+				MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), texts[i].pText);
+				texts[i].pText = NULL;
+			} else {
+				MultiForceRedraw(texts[i].pText);
+				bIsText = true;
+			}
+		}
+	}
+}
+
+/*-----------------------------------------------------*\
+|-------------------------------------------------------|
+\*-----------------------------------------------------*/
+
+void BmvDrawText(bool bDraw) {
+	int	w, h, x, y;
+
+	for (int i = 0; i < 2; i++) {
+		if (texts[i].pText) {
+			x = MultiLeftmost(texts[i].pText);
+			y = MultiHighest(texts[i].pText);
+			w = MIN(MultiRightmost(texts[i].pText) + 1, (int)SCREEN_WIDTH) - x;
+			h = MIN(MultiLowest(texts[i].pText) + 1, SCREEN_HIGH) - y;
+
+			const byte *src = ScreenBeg + (y * SCREEN_WIDTH) + x;
+			byte *dest = (byte *)_vm->screen().getBasePtr(x, y);
+
+			for (int j = 0; j < h; j++, dest += SCREEN_WIDTH, src += SCREEN_WIDTH) {
+				memcpy(dest, src, w);
+			}
+
+			if (bDraw) {
+				Common::Point ptWin;
+				Common::Rect rcPlayClip;
+
+				ptWin.x = ptWin.y = 0;
+				rcPlayClip.left = x;
+				rcPlayClip.top = y;
+				rcPlayClip.right = x+w;
+				rcPlayClip.bottom = y+h;
+				UpdateClipRect(GetPlayfieldList(FIELD_STATUS), &ptWin,	&rcPlayClip);
+			}
+		}
+	}
+}
+
+/*-----------------------------------------------------*\
+|-------------------------------------------------------|
+\*-----------------------------------------------------*/
+
+void MovieText(CORO_PARAM, int stringId, int x, int y, int fontId, COLORREF *pTalkColour, int duration) {
+	SCNHANDLE hFont;
+	int	index;
+
+	if (fontId == 1) {
+		// It's a 'print'
+
+		hFont = GetTagFontHandle();
+		index = 0;
+	} else {
+		// It's a 'talk'
+
+		if (pTalkColour != NULL)
+			SetTextPal(*pTalkColour);
+		hFont = GetTalkFontHandle();
+		index = 1;
+	}
+
+	if (texts[index].pText)
+		MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), texts[index].pText);
+
+	LoadSubString(stringId, 0, TextBufferAddr(), TBUFSZ);
+
+	texts[index].dieFrame = currentFrame + duration;
+	texts[index].pText = ObjectTextOut(coroParam, GetPlayfieldList(FIELD_STATUS),
+						TextBufferAddr(),
+						0,
+						x, y,
+						hFont,
+						TXT_CENTRE, 0);
+	KeepOnScreen(texts[index].pText, &x, &y);
+}
+
+/**
+ * Called when a packet contains a command field.
+ */
+static int MovieCommand(char cmd, int commandOffset) {
+	if (cmd & CD_PRINT) {
+		PPRINT_CMD pCmd;
+
+		pCmd = (PPRINT_CMD)(bigBuffer + commandOffset);
+
+		MovieText(nullContext, pCmd->stringId,
+				pCmd->x,
+				pCmd->y,
+				pCmd->fontId,
+				NULL,
+				pCmd->duration);
+
+		return sz_CMD_PRINT_pkt;
+	} else {
+		if (bSubtitles) {
+			PTALK_CMD pCmd;
+
+			pCmd = (PTALK_CMD)(bigBuffer + commandOffset);
+			talkColour = RGB(pCmd->r, pCmd->g, pCmd->b);
+
+			MovieText(nullContext, pCmd->stringId,
+					pCmd->x,
+					pCmd->y,
+					0,
+					&talkColour,
+					pCmd->duration);
+		}
+		return sz_CMD_TALK_pkt;
+	}
+}
+
+/**
+ * Called from PlayMovie() in tinlib.cpp
+ * Kicks off the playback of a movie, and waits around
+ * until it's finished.
+ */
+void PlayBMV(CORO_PARAM, SCNHANDLE hFileStem, int myEscape) {
+	CORO_BEGIN_CONTEXT;
+	CORO_END_CONTEXT(_ctx);
+
+	CORO_BEGIN_CODE(_ctx);
+
+	assert(!bMovieOn);
+
+	strcpy(szMovieFile, (char *)LockMem(hFileStem));
+	strcat(szMovieFile, BMOVIE_EXTENSION);
+
+	assert(strlen(szMovieFile) <= 12);
+
+	bMovieOn = true;
+	bAbort = false;
+	bmvEscape = myEscape;
+
+	do {
+		CORO_SLEEP(1);
+	} while (bMovieOn);
+
+	CORO_END_CODE;
+}
+
+/**
+ * Given a packet offset, calculates the offset of the
+ * next packet. The packet may not yet exist, and the
+ *return value may be off the end of bigBuffer.
+ */
+static int FollowingPacket(int thisPacket, bool bReallyImportant) {
+	unsigned char *data;
+	int	nextSlot, length;
+
+	// Set pointer to thisPacket's data
+	data = bigBuffer + thisPacket;
+
+	switch (*data) {
+	case CD_SLOT_NOP:
+		nextSlot = thisPacket/SLOT_SIZE;
+		if (thisPacket%SLOT_SIZE)
+			nextSlot++;
+
+		return nextSlot * SLOT_SIZE;
+
+	case CD_LE_FIN:
+		return -1;
+
+	default:
+		// Following 3 bytes are the length
+		if (bReallyImportant) {
+			// wrapped round or at least 3 bytes
+			assert(((nextReadSlot * SLOT_SIZE) < thisPacket) || 
+				((thisPacket + 3) < (nextReadSlot * SLOT_SIZE)));
+
+			if ((nextReadSlot * SLOT_SIZE >= thisPacket) && 
+				((thisPacket + 3) >= nextReadSlot*SLOT_SIZE)) {
+				// MaintainBuffer calls this back, but with false
+				MaintainBuffer();
+			}
+		} else {
+			// not wrapped and not 3 bytes
+			if (nextReadSlot*SLOT_SIZE >= thisPacket && thisPacket+3 >= nextReadSlot*SLOT_SIZE)
+				return thisPacket + 3;
+		}
+		length = *(int32 *)(bigBuffer + thisPacket + 1);
+		length &= 0x00ffffff;
+		return thisPacket + length + 4;
+	}
+}
+
+/**
+ * Called from the foreground when starting playback of a movie.
+ */
+static void LoadSlots(int number) {
+	int nextOffset;
+
+	assert(number + nextReadSlot < NUM_SLOTS);
+
+	if (stream.read(bigBuffer + nextReadSlot*SLOT_SIZE, number * SLOT_SIZE) !=
+			(uint32)(number * SLOT_SIZE)) {
+		int possibleSlots;
+
+		// May be a short file
+		possibleSlots = stream.size() / SLOT_SIZE;
+		if ((number + nextReadSlot) > possibleSlots) {
+			bFileEnd = true;
+			nextReadSlot = possibleSlots;
+		} else
+			error(FILE_IS_CORRUPT, szMovieFile);
+	}
+
+	nextReadSlot += number;
+
+	nextOffset = FollowingPacket(nextUseOffset, true);
+	while (nextOffset < nextReadSlot*SLOT_SIZE
+			&& nextOffset != -1) {
+		numAdvancePackets++;
+		mostFutureOffset = nextOffset;
+		nextOffset = FollowingPacket(mostFutureOffset, false);
+	}
+}
+
+/**
+ * Called from the foreground when starting playback of a movie.
+ */
+static void InitialiseBMV(void) {
+	if (!stream.open(szMovieFile))
+		error(CANNOT_FIND_FILE, szMovieFile);
+
+	// Grab the data buffer
+	bigBuffer = (byte *)malloc(NUM_SLOTS * SLOT_SIZE);
+	if (bigBuffer == NULL)
+		error(NO_MEM, "FMV data buffer\n");
+
+	// Screen buffer (2 lines more than screen
+	screenBuffer = (byte *)malloc(SCREEN_WIDTH * (SCREEN_HIGH + 2));
+	if (screenBuffer == NULL)
+		error(NO_MEM, "FMV screen buffer\n");
+
+	// Pass the sceen buffer to the decompresser
+	InitBMV(screenBuffer);
+
+	// Initialise some stuff
+	nextUseOffset = 0;
+	nextSoundOffset = 0;
+	wrapUseOffset = -1;
+	mostFutureOffset = 0;
+	currentFrame = 0;
+	currentSoundFrame = 0;
+	numAdvancePackets = 0;
+	nextReadSlot = 0;
+	bFileEnd = false;
+	blobsInBuffer = 0;
+	memset(texts, 0, sizeof(texts));
+	talkColour = 0;
+	bigProblemCount = 0;
+
+	movieTick = 0;
+
+	bIsText = false;
+
+//	memset(&rr, 0, sizeof(rr));
+
+	// Prefetch data
+	LoadSlots(PREFETCH);
+
+	while (numAdvancePackets < ADVANCE_SOUND)
+		LoadSlots(1);
+
+	// Initialise the sound channel
+	InitialiseMovieSound();
+}
+
+/**
+ * Called from the foreground when ending playback of a movie.
+ */
+void FinishBMV(void) {
+	int	i;
+
+	// Notify the sound channel
+	FinishMovieSound();
+
+	// Close the file stream
+	if (stream.isOpen())
+		stream.close();
+
+	// Release the data buffer
+	if (bigBuffer != NULL) {
+		free(bigBuffer);
+		bigBuffer = NULL;
+	}
+
+	// Release the screen buffer
+	if (screenBuffer != NULL) {
+		free(screenBuffer);
+		screenBuffer = NULL;
+	}
+
+	// Ditch any text objects
+	for (i = 0; i < 2; i++) {
+		if (texts[i].pText) {
+			MultiDeleteObject(GetPlayfieldList(FIELD_STATUS), texts[i].pText);
+			texts[i].pText = NULL;
+		}
+	}
+	bMovieOn = false;
+
+	nextMovieTime = 0;
+
+	// Test for 'twixt-movie glitch
+	ClearScreen();
+}
+
+/**
+ * MaintainBuffer()
+ */
+static bool MaintainBuffer(void) {
+	int nextOffset;
+
+	// No action if the file is all read
+	if (bFileEnd == true)
+		return false;
+
+	// See if next complete packet exists
+	// and if so, if it will fit in the top of the buffer
+	nextOffset = FollowingPacket(mostFutureOffset, false);
+	if (nextOffset == -1) {
+		// No following packets
+		return false;
+	} else if (nextOffset > NUM_SLOTS * SLOT_SIZE) {
+		// The current unfinished packet will not fit
+		// Copy this slot to slot 0
+
+		// Not if we're still using it!!!
+		// Or, indeed, if the player is still lagging
+		if (nextUseOffset < SLOT_SIZE || nextUseOffset > mostFutureOffset) {
+			// Slot 0 is still in use, buffer is full!
+			return false;
+		}
+
+		// Tell data player where to make the jump
+		wrapUseOffset = mostFutureOffset;
+
+		// mostFuture Offset is now in slot 0
+		mostFutureOffset %= SLOT_SIZE;
+
+		// Copy the data we already have for unfinished packet
+		memcpy(bigBuffer + mostFutureOffset,
+			bigBuffer + wrapUseOffset,
+			SLOT_SIZE - mostFutureOffset);
+
+		// Next read is into slot 1
+		nextReadSlot = 1;
+	}
+
+	if (nextReadSlot == NUM_SLOTS) {
+		// Want to go to slot zero, wait if still in use
+		if (nextUseOffset < SLOT_SIZE) {
+			// Slot 0 is still in use, buffer is full!
+			return false;
+		}
+
+		// nextOffset must be the buffer size
+		assert(nextOffset == NUM_SLOTS*SLOT_SIZE);
+
+		// wrapUseOffset must not be set
+		assert(wrapUseOffset == -1);
+		wrapUseOffset = nextOffset;
+
+		nextReadSlot = 0;
+		mostFutureOffset = 0;
+	}
+
+	// Don't overwrite unused data
+	if (nextUseOffset / SLOT_SIZE == nextReadSlot) {
+		// Buffer is full!
+		return false;
+	}
+
+	if (stream.read(bigBuffer + nextReadSlot * SLOT_SIZE, SLOT_SIZE) != SLOT_SIZE) {
+		bFileEnd = true;
+	}
+
+	// Read next slot next time
+	nextReadSlot++;
+
+	// Find new mostFutureOffset
+	nextOffset = FollowingPacket(mostFutureOffset, false);
+	while (nextOffset < nextReadSlot*SLOT_SIZE
+			&& nextOffset != -1) {
+		numAdvancePackets++;
+		mostFutureOffset = nextOffset;
+		nextOffset = FollowingPacket(mostFutureOffset, false);
+	}
+
+	// New test feature for e.g. short files
+	if (bFileEnd && *(bigBuffer+mostFutureOffset) != CD_LE_FIN)
+		bAbort = true;
+
+	return true;
+}
+
+/**
+ * DoBMVFrame
+ */
+static bool DoBMVFrame(void) {
+	unsigned char *data;
+	int	graphOffset, length;
+	signed short	xscr;
+
+	if (nextUseOffset == wrapUseOffset) {
+		nextUseOffset %= SLOT_SIZE;
+	}
+
+	while (nextUseOffset == mostFutureOffset) {
+		data = bigBuffer + nextUseOffset;
+		if (*data != CD_LE_FIN) {
+			// Don't get stuck in an infinite loop
+			if (!MaintainBuffer()) {
+				FinishBMV();
+				return false;
+			}
+
+			if (nextUseOffset == wrapUseOffset) {
+				nextUseOffset %= SLOT_SIZE;
+			}
+		} else
+			break;
+	}
+
+	// Set pointer to data
+	data = bigBuffer + nextUseOffset;
+
+	// If still at most Future, it must be last
+	if (nextUseOffset == mostFutureOffset) {
+		assert(*data == CD_LE_FIN);
+	}
+
+	switch (*data) {
+	case CD_SLOT_NOP:
+		nextUseOffset = FollowingPacket(nextUseOffset, true);
+		if (nextUseOffset == wrapUseOffset) {
+			nextUseOffset %= SLOT_SIZE;
+			wrapUseOffset = -1;
+		}
+		numAdvancePackets--;
+		return false;
+
+	case CD_LE_FIN:
+		FinishBMV();
+		numAdvancePackets--;
+		return true;
+
+	default:
+		length = *(int *)(data + 1);
+		length &= 0x00ffffff;
+
+		graphOffset = nextUseOffset + 4;	// Skip command byte and length
+
+		if (*data & CD_AUDIO) {
+			if (bOldAudio) {
+				graphOffset += sz_AUDIO_pkt;	// Skip audio data
+				length -= sz_AUDIO_pkt;
+			} else {
+				int blobs;
+
+				blobs = *(bigBuffer + graphOffset);
+				blobs *= SZ_C_BLOB;
+				graphOffset += (blobs + 1);
+				length -= (blobs + 1);
+			}
+		}
+
+		if (*data & CD_CMND) {
+			int cmdLen;
+
+			// Process command and skip data
+			cmdLen = MovieCommand(*data, graphOffset);
+
+			graphOffset += cmdLen;
+			length -= cmdLen;
+		}
+
+		if (*data & CD_CMAP) {
+			MoviePalette(graphOffset);
+			graphOffset += sz_CMAP_pkt;	// Skip palette data
+			length -= sz_CMAP_pkt;
+		}
+
+		if (*data & CD_XSCR) {
+			xscr = *(signed short *)(bigBuffer + graphOffset);
+			graphOffset += sz_XSCR_pkt;	// Skip scroll offset
+			length -= sz_XSCR_pkt;
+		} else if (*data & BIT0)
+			xscr = -640;
+		else
+			xscr = 0;
+			
+		PrepBMV(bigBuffer + graphOffset, length, xscr);
+
+		currentFrame++;
+		numAdvancePackets--;
+
+		nextUseOffset = FollowingPacket(nextUseOffset, true);
+		if (nextUseOffset == wrapUseOffset) {
+			nextUseOffset %= SLOT_SIZE;
+			wrapUseOffset = -1;
+		}
+		return true;
+	}
+}
+
+/**
+ * DoSoundFrame
+ */
+static bool DoSoundFrame(void) {
+	unsigned char *data;
+	int	graphOffset;
+
+	if (nextSoundOffset == wrapUseOffset) {
+		nextSoundOffset %= SLOT_SIZE;
+	}
+
+	// Make sure the full slot is here
+	while (nextSoundOffset == mostFutureOffset) {
+		data = bigBuffer + nextSoundOffset;
+		if (*data != CD_LE_FIN) {
+			// Don't get stuck in an infinite loop
+			if (!MaintainBuffer()) {
+				if (!bOldAudio)
+					MovieAudio(0, 0);
+				currentSoundFrame++;
+				return false;
+			}
+
+			if (nextSoundOffset == wrapUseOffset) {
+				nextSoundOffset %= SLOT_SIZE;
+			}
+		} else
+			break;
+	}
+
+	// Set pointer to data
+	data = bigBuffer + nextSoundOffset;
+
+	// If still at most Future, it must be last
+	if (nextSoundOffset == mostFutureOffset) {
+		assert(*data == CD_LE_FIN);
+	}
+
+	switch (*data) {
+	case CD_SLOT_NOP:
+		nextSoundOffset = FollowingPacket(nextSoundOffset, true);
+		if (nextSoundOffset == wrapUseOffset) {
+			nextSoundOffset %= SLOT_SIZE;
+		}
+		return false;
+
+	case CD_LE_FIN:
+		if (!bOldAudio)
+			MovieAudio(0, 0);
+		currentSoundFrame++;
+		return true;
+
+	default:
+		if (*data & CD_AUDIO) {
+			graphOffset = nextSoundOffset + 4;	// Skip command byte and length
+
+			if (!bOldAudio) {
+				int blobs = *(bigBuffer + graphOffset);
+				MovieAudio(graphOffset+1, blobs);
+			}
+		} else {
+			if (!bOldAudio)
+				MovieAudio(0, 0);
+		}
+
+		nextSoundOffset = FollowingPacket(nextSoundOffset, false);
+		if (nextSoundOffset == wrapUseOffset) {
+			nextSoundOffset %= SLOT_SIZE;
+		}
+		currentSoundFrame++;
+		return true;
+	}
+
+	return true;
+}
+
+/**
+ * CopyMovieToScreen
+ */
+void CopyMovieToScreen(void) {
+	// Not if not up and running yet!
+	if (!screenBuffer || (currentFrame == 0)) {
+		ForceEntireRedraw();
+		DrawBackgnd();
+		return;
+	}
+
+	// The movie surface is slightly less high than the output screen (429 rows versus 432).
+	// Because of this, there's some extra line clearing above and below the displayed area
+	int yStart = (SCREEN_HEIGHT - SCREEN_HIGH) / 2;
+	memset(_vm->screen().getBasePtr(0, 0), 0, yStart * SCREEN_WIDTH);
+	memcpy(_vm->screen().getBasePtr(0, yStart), ScreenBeg, SCREEN_WIDTH * SCREEN_HIGH);
+	memset(_vm->screen().getBasePtr(0, yStart + SCREEN_HIGH), 0, 
+		(SCREEN_HEIGHT - SCREEN_HIGH - yStart) * SCREEN_WIDTH);
+
+	BmvDrawText(true);
+	PalettesToVideoDAC();			// Keep palette up-to-date
+	UpdateScreenRect(Common::Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT));
+	BmvDrawText(false);
+}
+
+/**
+ * LookAtBuffers
+ */
+static void LookAtBuffers(void) {
+	static int junk;
+	int i;
+
+	if (bigBuffer) {
+		for (i = 0; i < NUM_SLOTS; i++)
+			junk += bigBuffer[i*SLOT_SIZE];
+	}
+}
+
+/**
+ * Handles playback of any active movie. Called from the foreground 24 times a second.
+ */
+void FettleBMV(void) {
+	static int nextMaintain = 0;
+
+	int refFrame;
+	// Tick counter needs to be incrementing at a 24Hz rate
+	int tick = movieTick++;
+
+	if (!bMovieOn)
+		return;
+
+	// Escape the rest if appropriate
+	if (bAbort || (bmvEscape && bmvEscape != GetEscEvents())) {
+		FinishBMV();
+		return;
+	}
+
+	LookAtBuffers();
+
+	if (!stream.isOpen()) {
+		int i;
+
+		// First time in with this movie
+
+		InitialiseBMV();
+
+		for (i = 0; i < ADVANCE_SOUND;) {
+			if (DoSoundFrame())
+				i++;
+		}
+		startTick = -ONE_SECOND / 4;	// 1/4 second
+		return;
+	}
+
+	if (startTick < 0) {
+		startTick++;
+		return;
+	}
+	if (startTick == 0) {
+		startTick = tick;
+		nextMaintain = startTick + 1;
+		StartMovieSound();
+	}
+
+	nextMovieTime = g_system->getMillis() + 41;
+
+	FettleMovieText();
+
+	if (bigProblemCount < PT_A) {
+		refFrame = currentSoundFrame;
+
+		while (currentSoundFrame < ((tick+1-startTick)/2 + ADVANCE_SOUND) && bMovieOn) {
+			if (currentSoundFrame == refFrame+PT_B)
+				break;
+
+			DoSoundFrame();
+		}
+	}
+
+	// Time to process a frame (or maybe more)
+	if (bigProblemCount < PT_A) {
+		refFrame = currentFrame;
+
+		while ((currentFrame < (tick-startTick)/2) && bMovieOn) {
+			DoBMVFrame();
+
+			if (currentFrame == refFrame+PT_B) {
+				bigProblemCount++;
+
+				if (bigProblemCount == PT_A) {
+					startTick = tick-(2*currentFrame);
+					bigProblemCount = 0;
+				}
+				break;
+			}
+		}
+		if (currentFrame == refFrame || currentFrame <= refFrame+3) {
+			bigProblemCount = 0;
+		}
+	} else {
+		while (currentFrame < (tick-startTick)/2 && bMovieOn) {
+			DoBMVFrame();
+		}
+	}
+
+	if (tick >= nextMaintain || numAdvancePackets < SUBSEQUENT_SOUND) {
+		MaintainBuffer();
+		nextMaintain = tick + 2;
+	}
+}
+
+/**
+ * Returns true if a movie is playing.
+ */
+bool MoviePlaying(void) {
+	return bMovieOn;
+}
+
+/**
+ * Returns the audio lag in ms
+ */
+int32 MovieAudioLag(void) {
+	if (!bMovieOn || !audioStream)
+		return 0;
+
+	// Calculate lag
+	int32 playLength = (movieTick - startTick - 1) * ((((uint32) 1000) << 10) / 24);
+	return (playLength - (audioStream->getTotalPlayTime() << 10)) >> 10;
+}
+
+uint32 NextMovieTime(void) {
+	return nextMovieTime;
+}
+
+void AbortMovie(void) {
+	bAbort = true;
+}
+
+void SlowMovieDown(void) {
+	bigProblemCount = 0;
+
+	if (currentFrame < (nowTick-startTick)/2 && bMovieOn) {
+		startTick = nowTick - 2*currentFrame;
+	} else
+		startTick += 2;
+}
+
+void SpeedMovieUp(void) {
+	if (!bigProblemCount) {
+		startTick -= 2;
+	}
+}
+
+} // end of namespace Tinsel


Property changes on: scummvm/trunk/engines/tinsel/bmv.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Modified: scummvm/trunk/engines/tinsel/config.cpp
===================================================================
--- scummvm/trunk/engines/tinsel/config.cpp	2008-11-30 21:17:58 UTC (rev 35195)
+++ scummvm/trunk/engines/tinsel/config.cpp	2008-12-01 20:35:36 UTC (rev 35196)
@@ -24,6 +24,8 @@
  * This file contains configuration functionality
  */
 
+//#define USE_3FLAGS 1
+
 #include "tinsel/config.h"
 #include "tinsel/dw.h"
 #include "tinsel/sound.h"
@@ -39,7 +41,7 @@
 //----------------- GLOBAL GLOBAL DATA --------------------
 
 int dclickSpeed = DOUBLE_CLICK_TIME;
-int volMidi = Audio::Mixer::kMaxChannelVolume;
+int volMusic = Audio::Mixer::kMaxChannelVolume;
 int volSound = Audio::Mixer::kMaxChannelVolume;
 int volVoice = Audio::Mixer::kMaxChannelVolume;
 int speedText = DEFTEXTSPEED;
@@ -49,21 +51,18 @@
 int bAmerica = 0;
 
 
-// Shouldn't really be here, but time is short...
-bool bNoBlocking;
 
 /**
  * Write settings to config manager and flush the config file to disk.
  */
 void WriteConfig(void) {
 	ConfMan.setInt("dclick_speed", dclickSpeed);
-	ConfMan.setInt("music_volume", volMidi);
+	ConfMan.setInt("music_volume", volMusic);
 	ConfMan.setInt("sfx_volume", volSound);
 	ConfMan.setInt("speech_volume", volVoice);
 	ConfMan.setInt("talkspeed", (speedText * 255) / 100);
 	ConfMan.setBool("subtitles", bSubtitles);
 	//ConfMan.setBool("swap_buttons", bSwapButtons ? 1 : 0);
-	//ConfigData.bAmerica = bAmerica;		// EN_USA / EN_GRB
 
 	// Store language for multilingual versions
 	if ((_vm->getFeatures() & GF_USE_3FLAGS) || (_vm->getFeatures() & GF_USE_4FLAGS) || (_vm->getFeatures() & GF_USE_5FLAGS)) {
@@ -92,16 +91,14 @@
 	ConfMan.flushToDisk();
 }
 
-/*---------------------------------------------------------------------*\
-|	ReadConfig()							|
-|-----------------------------------------------------------------------|
-|
-\*---------------------------------------------------------------------*/
+/**
+ * Read configuration settings from the config file into memory
+ */
 void ReadConfig(void) {
 	if (ConfMan.hasKey("dclick_speed"))
 		dclickSpeed = ConfMan.getInt("dclick_speed");
 
-	volMidi = ConfMan.getInt("music_volume");
+	volMusic = ConfMan.getInt("music_volume");
 	volSound = ConfMan.getInt("sfx_volume");
 	volVoice = ConfMan.getInt("speech_volume");
 

Modified: scummvm/trunk/engines/tinsel/config.h
===================================================================
--- scummvm/trunk/engines/tinsel/config.h	2008-11-30 21:17:58 UTC (rev 35195)
+++ scummvm/trunk/engines/tinsel/config.h	2008-12-01 20:35:36 UTC (rev 35196)
@@ -37,7 +37,7 @@
 };
 
 extern int dclickSpeed;
-extern int volMidi;
+extern int volMusic;
 extern int volSound;
 extern int volVoice;
 extern int speedText;
@@ -51,10 +51,6 @@
 
 extern bool isJapanMode();
 
-
-// Shouldn't really be here, but time is short...
-extern bool bNoBlocking;
-
 } // end of namespace Tinsel
 
 #endif

Modified: scummvm/trunk/engines/tinsel/coroutine.h
===================================================================
--- scummvm/trunk/engines/tinsel/coroutine.h	2008-11-30 21:17:58 UTC (rev 35195)
+++ scummvm/trunk/engines/tinsel/coroutine.h	2008-12-01 20:35:36 UTC (rev 35196)
@@ -70,6 +70,7 @@
 
 typedef CoroBaseContext *CoroContext;
 
+extern CoroContext nullContext;
 
 /**
  * Wrapper class which holds a pointer to a pointer to a CoroBaseContext.
@@ -101,6 +102,7 @@
 #define CORO_END_CONTEXT(x)    } *x = (CoroContextTag *)coroParam
 
 #define CORO_BEGIN_CODE(x) \
+		if (&coroParam == &nullContext) assert(!nullContext);\
 		if (!x) {coroParam = x = new CoroContextTag();}\
 		assert(coroParam);\
 		assert(coroParam->_sleep >= 0);\
@@ -109,17 +111,22 @@
 		switch(coroParam->_line) { case 0:;
 
 #define CORO_END_CODE \
+			if (&coroParam == &nullContext) nullContext = NULL; \
 		}
 
-#define CORO_SLEEP(delay) \
-		do {\
+#define CORO_SLEEP(delay) do {\
 			coroParam->_line = __LINE__;\
 			coroParam->_sleep = delay;\
+			assert(&coroParam != &nullContext);\
 			return; case __LINE__:;\
 		} while (0)
 
+#define CORO_GIVE_WAY do { g_scheduler->giveWay(); CORO_SLEEP(1); } while (0)
+#define CORO_RESCHEDULE do { g_scheduler->reschedule(); CORO_SLEEP(1); } while (0)
+
 /** Stop the currently running coroutine */
-#define CORO_KILL_SELF()         do { coroParam->_sleep = -1; return; } while(0)
+#define CORO_KILL_SELF() \
+		do { if (&coroParam != &nullContext) { coroParam->_sleep = -1; } return; } while(0)
 
 /** Invoke another coroutine */
 #define CORO_INVOKE_ARGS(subCoro, ARGS)  \
@@ -130,9 +137,22 @@
 				subCoro ARGS;\
 				if (!coroParam->_subctx) break;\
 				coroParam->_sleep = coroParam->_subctx->_sleep;\
+				assert(&coroParam != &nullContext);\
 				return; case __LINE__:;\
 			} while(1);\
 		} while (0)
+#define CORO_INVOKE_ARGS_V(subCoro, RESULT, ARGS)  \
+		do {\
+			coroParam->_line = __LINE__;\
+			coroParam->_subctx = 0;\
+			do {\
+				subCoro ARGS;\
+				if (!coroParam->_subctx) break;\
+				coroParam->_sleep = coroParam->_subctx->_sleep;\
+				assert(&coroParam != &nullContext);\
+				return RESULT; case __LINE__:;\
+			} while(1);\
+		} while (0)
 
 #define CORO_INVOKE_0(subCoroutine) \
 			CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX))
@@ -140,6 +160,8 @@
 			CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0))
 #define CORO_INVOKE_2(subCoroutine, a0,a1) \
 			CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0,a1))
+#define CORO_INVOKE_3(subCoroutine, a0,a1,a2) \
+			CORO_INVOKE_ARGS(subCoroutine,(CORO_SUBCTX,a0,a1,a2))
 
 
 } // end of namespace Tinsel

Modified: scummvm/trunk/engines/tinsel/cursor.cpp
===================================================================
--- scummvm/trunk/engines/tinsel/cursor.cpp	2008-11-30 21:17:58 UTC (rev 35195)
+++ scummvm/trunk/engines/tinsel/cursor.cpp	2008-12-01 20:35:36 UTC (rev 35196)
@@ -34,11 +34,14 @@
 #include "tinsel/film.h"
 #include "tinsel/graphics.h"
 #include "tinsel/handle.h"
-#include "tinsel/inventory.h"
+#include "tinsel/dialogs.h"
 #include "tinsel/multiobj.h"	// multi-part object defintions etc.
 #include "tinsel/object.h"
 #include "tinsel/pid.h"
+#include "tinsel/play.h"
 #include "tinsel/sched.h"
+#include "tinsel/sysvar.h"
+#include "tinsel/text.h"
 #include "tinsel/timers.h"		// For ONE_SECOND constant
 #include "tinsel/tinlib.h"		// resetidletime()
 #include "tinsel/tinsel.h"		// For engine access
@@ -54,20 +57,21 @@
 
 //----------------- LOCAL GLOBAL DATA --------------------
 
-static OBJECT *McurObj = 0;		// Main cursor object
-static OBJECT *AcurObj = 0;		// Auxiliary cursor object
+static OBJECT *McurObj = NULL;		// Main cursor object
+static OBJECT *AcurObj = NULL;		// Auxiliary cursor object
 
 static ANIM McurAnim = {0,0,0,0,0};		// Main cursor animation structure
 static ANIM AcurAnim = {0,0,0,0,0};		// Auxiliary cursor animation structure
 
-static bool bHiddenCursor = false;	// Set when cursor is hidden
+static bool bHiddenCursor = false;		// Set when cursor is hidden
 static bool bTempNoTrailers = false;	// Set when cursor trails are hidden
+static bool bTempHide = false;			// Set when cursor is hidden
 
 static bool bFrozenCursor = false;	// Set when cursor position is frozen
 
 static frac_t IterationSize = 0;
 
-static SCNHANDLE CursorHandle = 0;	// Handle to cursor reel data
+static SCNHANDLE hCursorFilm = 0;	// Handle to cursor reel data
 
 static int numTrails = 0;
 static int nextTrail = 0;
@@ -76,8 +80,11 @@
 				// - causes cursor processes to do nothing
 				// Reset when main cursor has re-initialised
 
-static bool restart = false;		// When main cursor has been bWhoa-ed, it waits
-				// for this to be set to true.
+static uint16 restart = 0;	// When main cursor has been bWhoa-ed, it waits
+							// for this to be set to 0x8000.
+							// Main cursor sets all the bits after a re-start
+							// - each cursor trail examines it's own bit

@@ Diff output truncated at 100000 characters. @@

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