Index: engines/mohawk/video/cinepak.h
===================================================================
--- engines/mohawk/video/cinepak.h	(revision 48158)
+++ engines/mohawk/video/cinepak.h	(working copy)
@@ -65,6 +65,7 @@
 	~CinepakDecoder();
 
 	Graphics::Surface *decodeImage(Common::SeekableReadStream *stream);
+	Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; }
 
 private:
 	CinepakFrame _curFrame;
Index: engines/mohawk/video/rpza.h
===================================================================
--- engines/mohawk/video/rpza.h	(revision 48158)
+++ engines/mohawk/video/rpza.h	(working copy)
@@ -37,6 +37,7 @@
 	~RPZADecoder() { delete _surface; }
 
 	Graphics::Surface *decodeImage(Common::SeekableReadStream *stream);
+	Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; }
 
 private:
 	Graphics::Surface *_surface;
Index: engines/mohawk/video/smc.h
===================================================================
--- engines/mohawk/video/smc.h	(revision 48158)
+++ engines/mohawk/video/smc.h	(working copy)
@@ -43,6 +43,7 @@
 	~SMCDecoder() { delete _surface; }
 
 	Graphics::Surface *decodeImage(Common::SeekableReadStream *stream);
+	Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); }
 
 private:
 	Graphics::Surface *_surface;
Index: engines/mohawk/video/qtrle.h
===================================================================
--- engines/mohawk/video/qtrle.h	(revision 48158)
+++ engines/mohawk/video/qtrle.h	(working copy)
@@ -37,6 +37,7 @@
 	~QTRLEDecoder();
 
 	Graphics::Surface *decodeImage(Common::SeekableReadStream *stream);
+	Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; }
 
 private:
 	byte _bitsPerPixel;
Index: engines/mohawk/jpeg.h
===================================================================
--- engines/mohawk/jpeg.h	(revision 48158)
+++ engines/mohawk/jpeg.h	(working copy)
@@ -45,6 +45,7 @@
 	~JPEGDecoder();
 
 	Graphics::Surface *decodeImage(Common::SeekableReadStream *stream);
+	Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; }
 
 private:
 	Graphics::PixelFormat _pixelFormat;
Index: engines/tucker/sequences.cpp
===================================================================
--- engines/tucker/sequences.cpp	(revision 48158)
+++ engines/tucker/sequences.cpp	(working copy)
@@ -760,19 +760,20 @@
 }
 
 bool AnimationSequencePlayer::decodeNextAnimationFrame(int index) {
-	bool framesLeft = _flicPlayer[index].decodeNextFrame();
+	::Graphics::Surface *surface = _flicPlayer[index].decodeNextFrame();
+
 	if (_seqNum == 19) {
-		_flicPlayer[index].copyFrameToBuffer(_offscreenBuffer, 0, 0, kScreenWidth);
+		for (uint16 y = 0; (y < surface->h) && (y < kScreenHeight); y++)
+			memcpy(_offscreenBuffer + y * kScreenWidth, (byte *)surface->pixels + y * surface->pitch, surface->w);
 	} else {
 		_flicPlayer[index].copyDirtyRectsToBuffer(_offscreenBuffer, kScreenWidth);
 		++_frameCounter;
 	}
-	if (index == 0) {
-		if (_flicPlayer[index].paletteChanged()) {
-			getRGBPalette(index);
-		}
-	}
-	return framesLeft;
+
+	if (index == 0 && _flicPlayer[index].hasDirtyPalette())
+		getRGBPalette(index);
+
+	return !_flicPlayer[index].endOfVideo();
 }
 
 void AnimationSequencePlayer::loadIntroSeq17_18() {
@@ -797,20 +798,23 @@
 	// The intro credits animation. This uses 2 animations: the foreground one, which
 	// is the actual intro credits, and the background one, which is an animation of
 	// cogs, and is being replayed when an intro credit appears
-	if (_flicPlayer[0].getCurFrame() >= 116) {
-		if (!_flicPlayer[1].decodeNextFrame()) {
+	::Graphics::Surface *surface = 0;
+
+	if (_flicPlayer[0].getCurFrame() >= 117) {
+		surface = _flicPlayer[1].decodeNextFrame();
+		if (_flicPlayer[1].endOfVideo())
 			_flicPlayer[1].reset();
-		}
 	}
+
 	bool framesLeft = decodeNextAnimationFrame(0);
-	for (int i = 0; i < kScreenWidth * kScreenHeight; ++i) {
-		if (_offscreenBuffer[i] == 0) {
-			_offscreenBuffer[i] = _flicPlayer[1].getPixel(i);
-		}
-	}
-	if (!framesLeft) {
+
+	if (surface)
+		for (int i = 0; i < kScreenWidth * kScreenHeight; ++i)
+			if (_offscreenBuffer[i] == 0)
+				_offscreenBuffer[i] = *((byte *)surface->pixels + i);
+
+	if (!framesLeft)
 		_changeToNextSequence = true;
-	}
 }
 
 void AnimationSequencePlayer::displayLoadingScreen() {
@@ -864,7 +868,7 @@
 void AnimationSequencePlayer::playIntroSeq3_4() {
 	if (!_updateScreenPicture) {
 		bool framesLeft = decodeNextAnimationFrame(0);
-		if (_flicPlayer[0].getCurFrame() == 706) {
+		if (_flicPlayer[0].getCurFrame() == 707) {
 			initPicPart4();
 		}
 		if (!framesLeft) {
@@ -903,13 +907,21 @@
 }
 
 void AnimationSequencePlayer::drawPic1Part10() {
+	::Graphics::Surface *surface = _flicPlayer[0].decodeNextFrame();
+	_flicPlayer[0].copyDirtyRectsToBuffer(_offscreenBuffer, kScreenWidth);
+	++_frameCounter;
+
+	if (_flicPlayer[0].hasDirtyPalette())
+		getRGBPalette(0);
+
 	int offset = 0;
 	for (int y = 0; y < kScreenHeight; ++y) {
 		for (int x = 0; x < kScreenWidth; ++x) {
-			byte color = _flicPlayer[0].getPixel(offset);
-			if (color == 0) {
+			byte color = *((byte *)surface->pixels + offset);
+
+			if (color == 0)
 				color = _picBufPtr[800 + y * 640 + _updateScreenWidth + x];
-			}
+
 			_offscreenBuffer[offset++] = color;
 		}
 	}
@@ -924,22 +936,22 @@
 }
 
 void AnimationSequencePlayer::playIntroSeq9_10() {
-	bool framesLeft = decodeNextAnimationFrame(0);
-	if (_flicPlayer[0].getCurFrame() >= 264 && _flicPlayer[0].getCurFrame() <= 295) {
+	if (_flicPlayer[0].getCurFrame() >= 265 && _flicPlayer[0].getCurFrame() <= 296) {
 		drawPic1Part10();
 		_updateScreenWidth += 6;
-	} else if (_flicPlayer[0].getCurFrame() == 984) {
+	} else if (_flicPlayer[0].getCurFrame() == 985) {
+		decodeNextAnimationFrame(0);
 		drawPic2Part10();
-	} else if (_flicPlayer[0].getCurFrame() >= 988 && _flicPlayer[0].getCurFrame() <= 996) {
+	} else if (_flicPlayer[0].getCurFrame() >= 989 && _flicPlayer[0].getCurFrame() <= 997) {
 		drawPic1Part10();
 		_updateScreenWidth -= 25;
 		if (_updateScreenWidth < 0) {
 			_updateScreenWidth = 0;
 		}
 	}
-	if (!framesLeft) {
+
+	if (_flicPlayer[0].endOfVideo())
 		_changeToNextSequence = true;
-	}
 }
 
 void AnimationSequencePlayer::loadIntroSeq21_22() {
Index: engines/saga/scene.h
===================================================================
--- engines/saga/scene.h	(revision 48158)
+++ engines/saga/scene.h	(working copy)
@@ -424,6 +424,7 @@
 	int DinoStartProc();
 	int FTA2StartProc();
 	int FTA2EndProc(FTA2Endings whichEnding);
+	void playMovie(const char *filename);
 
 	void IHNMLoadCutaways();
 	bool checkKey();
Index: engines/saga/introproc_saga2.cpp
===================================================================
--- engines/saga/introproc_saga2.cpp	(revision 48158)
+++ engines/saga/introproc_saga2.cpp	(working copy)
@@ -40,13 +40,7 @@
 int Scene::DinoStartProc() {
 	_vm->_gfx->showCursor(false);
 
-	Graphics::SmackerDecoder *smkDecoder = new Graphics::SmackerDecoder(_vm->_mixer);
-	Graphics::VideoPlayer *player = new Graphics::VideoPlayer(smkDecoder);
-	if (smkDecoder->loadFile("testvid.smk"))
-		player->playVideo();        // Play introduction
-	smkDecoder->closeFile();
-	delete player;
-	delete smkDecoder;
+	playMovie("testvid.smk");
 
 	// HACK: Forcibly quit here
 	_vm->quitGame();
@@ -57,16 +51,8 @@
 int Scene::FTA2StartProc() {
 	_vm->_gfx->showCursor(false);
 
-	Graphics::SmackerDecoder *smkDecoder = new Graphics::SmackerDecoder(_vm->_mixer);
-	Graphics::VideoPlayer *player = new Graphics::VideoPlayer(smkDecoder);
-	if (smkDecoder->loadFile("trimark.smk"))
-		player->playVideo();      // Show Ignite logo
-	smkDecoder->closeFile();
-	if (smkDecoder->loadFile("intro.smk"))
-		player->playVideo();        // Play introduction
-	smkDecoder->closeFile();
-	delete player;
-	delete smkDecoder;
+	playMovie("trimark.smk");
+	playMovie("intro.smk");
 
 	// HACK: Forcibly quit here
 	_vm->quitGame();
@@ -100,18 +86,41 @@
 	_vm->_gfx->showCursor(false);
 
 	// Play ending
-	Graphics::SmackerDecoder *smkDecoder = new Graphics::SmackerDecoder(_vm->_mixer);
-	Graphics::VideoPlayer *player = new Graphics::VideoPlayer(smkDecoder);
-	if (smkDecoder->loadFile(videoName)) {
-		player->playVideo();
-		smkDecoder->closeFile();
-	}
-	delete player;
-	delete smkDecoder;
+	playMovie(videoName);
 
 	return SUCCESS;
 }
 
+void Scene::playMovie(const char *filename) {
+	Graphics::SmackerDecoder *smkDecoder = new Graphics::SmackerDecoder(_vm->_mixer);
+
+	if (!smkDecoder->loadFile(filename))
+		return;
+
+	uint16 x = (g_system->getWidth() - smkDecoder->getWidth()) / 2;
+	uint16 y = (g_system->getHeight() - smkDecoder->getHeight()) / 2;
+
+	while (!_vm->shouldQuit() && !smkDecoder->endOfVideo()) {
+		if (smkDecoder->needsUpdate()) {
+			Graphics::Surface *frame = smkDecoder->decodeNextFrame();
+			if (frame) {
+				_vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+
+				if (smkDecoder->hasDirtyPalette())
+					smkDecoder->setSystemPalette();
+
+				_vm->_system->updateScreen();
+			}
+		}
+	
+		Common::Event event;
+		while (_vm->_system->getEventManager()->pollEvent(event))
+			;
+
+		_vm->_system->delayMillis(10);
+	}
+}
+
 } // End of namespace Saga
 
 #endif
Index: engines/sci/console.cpp
===================================================================
--- engines/sci/console.cpp	(revision 48158)
+++ engines/sci/console.cpp	(working copy)
@@ -223,42 +223,48 @@
 	if (!_videoFile.empty()) {
 		_engine->_gfxCursor->kernelHide();
 
+		Graphics::VideoDecoder *videoDecoder = 0;
+
 		if (_videoFile.hasSuffix(".seq")) {
-			SeqDecoder *seqDecoder = new SeqDecoder();
-			Graphics::VideoPlayer *player = new Graphics::VideoPlayer(seqDecoder);
-			if (seqDecoder->loadFile(_videoFile.c_str(), _videoFrameDelay))
-				player->playVideo();
-			else
-				DebugPrintf("Failed to open movie file %s\n", _videoFile.c_str());
-			seqDecoder->closeFile();
-			delete player;
-			delete seqDecoder;
-		} else if (_videoFile.hasSuffix(".avi")) {
-			Graphics::AviDecoder *aviDecoder = new Graphics::AviDecoder(g_system->getMixer());
-			Graphics::VideoPlayer *player = new Graphics::VideoPlayer(aviDecoder);
-			if (aviDecoder->loadFile(_videoFile.c_str()))
-				player->playVideo();
-			else
-				DebugPrintf("Failed to open movie file %s\n", _videoFile.c_str());
-			aviDecoder->closeFile();
-			delete player;
-			delete aviDecoder;
+			videoDecoder = new SeqDecoder();
+			((SeqDecoder *)videoDecoder)->setFrameDelay(_videoFrameDelay);
+#ifdef ENABLE_SCI32
 		} else if (_videoFile.hasSuffix(".vmd")) {
-#ifdef ENABLE_SCI32
-			VMDDecoder *vmdDecoder = new VMDDecoder(g_system->getMixer());
-			Graphics::VideoPlayer *player = new Graphics::VideoPlayer(vmdDecoder);
-			if (vmdDecoder->loadFile(_videoFile.c_str()))
-				player->playVideo();
-			else
-				DebugPrintf("Failed to open movie file %s\n", _videoFile.c_str());
-			vmdDecoder->closeFile();
-			delete player;
-			delete vmdDecoder;
+			videoDecoder = new VMDDecoder(g_system->getMixer());
 #endif
+		} else if (_videoFile.hasSuffix(".avi")) {
+			videoDecoder = new Graphics::AviDecoder(g_system->getMixer());
 		}
 
+		if (videoDecoder && videoDecoder->loadFile(_videoFile)) {
+			uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2;
+			uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2;
+
+			while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) {			
+				if (videoDecoder->needsUpdate()) {
+					Graphics::Surface *frame = videoDecoder->decodeNextFrame();
+					if (frame) {
+						g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+
+						if (videoDecoder->hasDirtyPalette())
+							videoDecoder->setSystemPalette();
+
+						g_system->updateScreen();
+					}
+				}
+
+				Common::Event event;
+				while (g_system->getEventManager()->pollEvent(event))
+					;
+
+				g_system->delayMillis(10);
+			}
+		
+			delete videoDecoder;
+		} else
+			warning("Could not play video %s\n", _videoFile.c_str());
+
 		_engine->_gfxCursor->kernelShow();
-
 		_videoFile.clear();
 		_videoFrameDelay = 0;
 	}
Index: engines/sci/engine/kgraphics.cpp
===================================================================
--- engines/sci/engine/kgraphics.cpp	(revision 48158)
+++ engines/sci/engine/kgraphics.cpp	(working copy)
@@ -1076,8 +1076,6 @@
 }
 
 reg_t kShowMovie(EngineState *s, int argc, reg_t *argv) {
-	bool playedVideo = false;
-
 	// Hide the cursor if it's showing and then show it again if it was
 	// previously visible.
 	bool reshowCursor;
@@ -1085,24 +1083,23 @@
 	reshowCursor = g_sci->_gfxCursor->isVisible();
 	if (reshowCursor)
 		g_sci->_gfxCursor->kernelHide();
+		
+	Graphics::VideoDecoder *videoDecoder = 0;
 
 	if (argv[0].segment != 0) {
 		// DOS SEQ
 		// SEQ's are called with no subops, just the string and delay
 		Common::String filename = s->_segMan->getString(argv[0]);
-		int delay = argv[1].toUint16(); // Time between frames in ticks
 
 		SeqDecoder *seqDecoder = new SeqDecoder();
-		Graphics::VideoPlayer *player = new Graphics::VideoPlayer(seqDecoder);
-		if (seqDecoder->loadFile(filename.c_str(), delay)) {
-			player->playVideo();
-			playedVideo = true;
-		} else {
+		seqDecoder->setFrameDelay(argv[1].toUint16());
+		videoDecoder = seqDecoder;
+		
+		if (!videoDecoder->loadFile(filename)) {
 			warning("Failed to open movie file %s", filename.c_str());
+			delete videoDecoder;
+			videoDecoder = 0;
 		}
-		seqDecoder->closeFile();
-		delete player;
-		delete seqDecoder;
 	} else {
 		// Windows AVI (Macintosh QuickTime? Need to check KQ6 Macintosh)
 		// TODO: This appears to be some sort of subop. case 0 contains the string
@@ -1121,17 +1118,13 @@
 		switch (argv[0].toUint16()) {
 		case 0: {
 			Common::String filename = s->_segMan->getString(argv[1]);
-			Graphics::AviDecoder *aviDecoder = new Graphics::AviDecoder(g_system->getMixer());
-			Graphics::VideoPlayer *player = new Graphics::VideoPlayer(aviDecoder);
-			if (aviDecoder->loadFile(filename.c_str())) {
-				player->playVideo();
-				playedVideo = true;
-			} else {
+			videoDecoder = new Graphics::AviDecoder(g_system->getMixer());
+
+			if (!videoDecoder->loadFile(filename.c_str())) {
 				warning("Failed to open movie file %s", filename.c_str());
+				delete videoDecoder;
+				videoDecoder = 0;
 			}
-			aviDecoder->closeFile();
-			delete player;
-			delete aviDecoder;
 			break;
 		}
 		default:
@@ -1139,8 +1132,33 @@
 		}
 	}
 
-	if (playedVideo)
+	if (videoDecoder) {
+		uint16 x = (g_system->getWidth() - videoDecoder->getWidth()) / 2;
+		uint16 y = (g_system->getHeight() - videoDecoder->getHeight()) / 2;
+
+		while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo()) {			
+			if (videoDecoder->needsUpdate()) {
+				Graphics::Surface *frame = videoDecoder->decodeNextFrame();
+				if (frame) {
+					g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+
+					if (videoDecoder->hasDirtyPalette())
+						videoDecoder->setSystemPalette();
+
+					g_system->updateScreen();
+				}
+			}
+
+			Common::Event event;
+			while (g_system->getEventManager()->pollEvent(event))
+				;
+
+			g_system->delayMillis(10);
+		}
+		
+		delete videoDecoder;
 		g_sci->_gfxScreen->kernelSyncWithFramebuffer();
+	}
 
 	if (reshowCursor)
 		g_sci->_gfxCursor->kernelShow();
Index: engines/sci/video/vmd_decoder.h
===================================================================
--- engines/sci/video/vmd_decoder.h	(revision 48158)
+++ engines/sci/video/vmd_decoder.h	(working copy)
@@ -29,7 +29,7 @@
 #define GRAPHICS_VIDEO_VMD_DECODER_H
 
 #include "graphics/video/coktelvideo/coktelvideo.h"
-#include "graphics/video/video_player.h"
+#include "graphics/video/video_decoder.h"
 #include "sound/mixer.h"
 
 namespace Sci {
@@ -49,32 +49,37 @@
  * - Shivers 2: Harvest of Souls
  * - Torin's Passage
  */
-class VMDDecoder : public Graphics::VideoDecoder {
+class VMDDecoder : public Graphics::FixedRateVideoDecoder {
 public:
 	VMDDecoder(Audio::Mixer *mixer);
 	virtual ~VMDDecoder();
 
 	uint32 getFrameWaitTime();
 
-	/**
-	 * Load a VMD encoded video file
-	 * @param filename	the filename to load
-	 */
-	bool loadFile(const char *filename);
+	bool load(Common::SeekableReadStream &stream);
+	void close();
 
-	/**
-	 * Close a VMD encoded video file
-	 */
-	void closeFile();
+	bool isVideoLoaded() const { return _fileStream != 0; }
+	uint16 getWidth() const { return _surface->w; }
+	uint16 getHeight() const { return _surface->h; }
+	uint32 getFrameCount() const { return _vmdDecoder->getFramesCount(); }
+	Graphics::Surface *decodeNextFrame();
+	Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); }
+	byte *getPalette() { _dirtyPalette = false; return _palette; }
+	bool hasDirtyPalette() const { return _dirtyPalette; }
 
-	bool decodeNextFrame();
+protected:
+	double getFrameRate() const { return _vmdDecoder->getFrameRate(); }
 
 private:
 	Graphics::Vmd *_vmdDecoder;
 	Audio::Mixer *_mixer;
+	Graphics::Surface *_surface;
+	Common::SeekableReadStream *_fileStream;
 	byte _palette[256 * 3];
+	bool _dirtyPalette;
 
-	void getPalette();
+	void loadPaletteFromVMD();
 };
 
 } // End of namespace Graphics
Index: engines/sci/video/seq_decoder.h
===================================================================
--- engines/sci/video/seq_decoder.h	(revision 48158)
+++ engines/sci/video/seq_decoder.h	(working copy)
@@ -26,40 +26,44 @@
 #ifndef SEQ_DECODER_H
 #define SEQ_DECODER_H
 
-#include "graphics/video/video_player.h"
+#include "graphics/video/video_decoder.h"
 
 namespace Sci {
 
 /**
  * Implementation of the Sierra SEQ decoder, used in KQ6 DOS floppy/CD and GK1 DOS
  */
-class SeqDecoder : public Graphics::VideoDecoder {
+class SeqDecoder : public Graphics::FixedRateVideoDecoder {
 public:
-	SeqDecoder() {}
+	SeqDecoder();
 	virtual ~SeqDecoder();
 
-	/**
-	 * Load a SEQ encoded video file
-	 * @param filename	the filename to load
-	 */
-	bool loadFile(const char *fileName) { return loadFile(fileName, 10); }
+	bool load(Common::SeekableReadStream &stream);
+	void close();
+	
+	void setFrameDelay(int frameDelay) { _frameDelay = frameDelay; }
 
-	/**
-	 * Load a SEQ encoded video file
-	 * @param filename	the filename to load
-	 * @param frameDelay the delay between frames, in ticks
-	 */
-	bool loadFile(const char *fileName, int frameDelay);
+	bool isVideoLoaded() const { return _fileStream != 0; }
+	uint16 getWidth() const { return 320; }
+	uint16 getHeight() const { return 200; }
+	uint32 getFrameCount() const { return _frameCount; }
+	Graphics::Surface *decodeNextFrame();
+	Graphics::PixelFormat getPixelFormat() const { return Graphics::PixelFormat::createFormatCLUT8(); }
+	byte *getPalette() { _dirtyPalette = false; return _palette; }
+	bool hasDirtyPalette() const { return _dirtyPalette; }
 
-	/**
-	 * Close a SEQ encoded video file
-	 */
-	void closeFile();
+protected:
+	double getFrameRate() const { assert(_frameDelay); return 60.0 / _frameDelay; }
 
-	bool decodeNextFrame();
-
 private:
 	bool decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey);
+	uint16 _width, _height;
+	uint16 _frameDelay;
+	Common::SeekableReadStream *_fileStream;
+	byte _palette[256 * 3];
+	bool _dirtyPalette;
+	uint32 _frameCount;
+	Graphics::Surface *_surface;
 };
 
 } // End of namespace Sci
Index: engines/sci/video/vmd_decoder.cpp
===================================================================
--- engines/sci/video/vmd_decoder.cpp	(revision 48158)
+++ engines/sci/video/vmd_decoder.cpp	(working copy)
@@ -27,7 +27,6 @@
 
 #include "sci/video/vmd_decoder.h"
 
-#include "common/archive.h"
 #include "common/endian.h"
 #include "common/util.h"
 #include "common/stream.h"
@@ -42,54 +41,39 @@
 
 VMDDecoder::VMDDecoder(Audio::Mixer *mixer) : _mixer(mixer) {
 	_vmdDecoder = new Graphics::Vmd(new Graphics::PaletteLUT(5, Graphics::PaletteLUT::kPaletteYUV));
+	_surface = 0;
+	_dirtyPalette = false;
+	_fileStream = 0;
 }
 
 VMDDecoder::~VMDDecoder() {
-	closeFile();
+	close();
 }
 
-uint32 VMDDecoder::getFrameWaitTime() {
-	return _vmdDecoder->getFrameWaitTime();
-}
+bool VMDDecoder::load(Common::SeekableReadStream &stream) {
+	close();
 
-bool VMDDecoder::loadFile(const char *fileName) {
-	closeFile();
-
-	_fileStream = SearchMan.createReadStreamForMember(fileName);
-	if (!_fileStream)
+	if (!_vmdDecoder->load(stream))
 		return false;
 
-	if (!_vmdDecoder->load(*_fileStream))
-		return false;
+	_fileStream = &stream;
 
-	if (_vmdDecoder->getFeatures() & Graphics::CoktelVideo::kFeaturesPalette) {
-		getPalette();
-		setPalette(_palette);
-	}
+	if (_vmdDecoder->getFeatures() & Graphics::CoktelVideo::kFeaturesPalette)
+		loadPaletteFromVMD();
 
 	if (_vmdDecoder->getFeatures() & Graphics::CoktelVideo::kFeaturesSound)
 		_vmdDecoder->enableSound(*_mixer);
 
-	_videoInfo.width = _vmdDecoder->getWidth();
-	_videoInfo.height = _vmdDecoder->getHeight();
-	_videoInfo.frameCount = _vmdDecoder->getFramesCount();
-	_videoInfo.frameRate = _vmdDecoder->getFrameRate();
-	_videoInfo.frameDelay = _videoInfo.frameRate * 100;
-	_videoInfo.currentFrame = 0;
-	_videoInfo.firstframeOffset = 0;	// not really necessary for VMDs
-
 	if (_vmdDecoder->hasExtraData())
 		warning("This VMD video has extra embedded data, which is currently not handled");
 
-	_videoFrameBuffer = new byte[_videoInfo.width * _videoInfo.height];
-	memset(_videoFrameBuffer, 0, _videoInfo.width * _videoInfo.height);
-
-	_vmdDecoder->setVideoMemory(_videoFrameBuffer, _videoInfo.width, _videoInfo.height);
-
+	_surface = new Graphics::Surface();
+	_surface->create(_vmdDecoder->getWidth(), _vmdDecoder->getHeight(), 1);
+	_vmdDecoder->setVideoMemory((byte *)_surface->pixels, _surface->w, _surface->h);
 	return true;
 }
 
-void VMDDecoder::closeFile() {
+void VMDDecoder::close() {
 	if (!_fileStream)
 		return;
 
@@ -98,25 +82,27 @@
 	delete _fileStream;
 	_fileStream = 0;
 
-	delete[] _videoFrameBuffer;
-	_videoFrameBuffer = 0;
+	_surface->free();
+	delete _surface;
+	_surface = 0;
+
+	reset();
 }
 
-bool VMDDecoder::decodeNextFrame() {
-	if (_videoInfo.currentFrame == 0)
-		_videoInfo.startTime = g_system->getMillis();
-
+Graphics::Surface *VMDDecoder::decodeNextFrame() {
 	Graphics::CoktelVideo::State state = _vmdDecoder->nextFrame();
 
-	if (state.flags & Graphics::CoktelVideo::kStatePalette) {
-		getPalette();
-		setPalette(_palette);
-	}
+	if (state.flags & Graphics::CoktelVideo::kStatePalette)
+		loadPaletteFromVMD();
 
-	return ++_videoInfo.currentFrame < _videoInfo.frameCount;
+	if (_curFrame == -1)
+		_startTime = g_system->getMillis();
+
+	_curFrame++;
+	return _surface;
 }
 
-void VMDDecoder::getPalette() {
+void VMDDecoder::loadPaletteFromVMD() {
 	const byte *pal = _vmdDecoder->getPalette();
 
 	for (int i = 0; i < 256; i++) {
@@ -124,6 +110,8 @@
 		_palette[i * 3 + 1] = pal[i * 3 + 1] << 2;
 		_palette[i * 3 + 2] = pal[i * 3 + 2] << 2;
 	}
+
+	_dirtyPalette = true;
 }
 
 } // End of namespace Graphics
Index: engines/sci/video/seq_decoder.cpp
===================================================================
--- engines/sci/video/seq_decoder.cpp	(revision 48158)
+++ engines/sci/video/seq_decoder.cpp	(working copy)
@@ -35,10 +35,6 @@
 
 namespace Sci {
 
-// SEQ videos always run at 320x200
-#define SCREEN_WIDTH 320
-#define SCREEN_HEIGHT 200
-
 enum seqPalTypes {
 	kSeqPalVariable = 0,
 	kSeqPalConstant = 1
@@ -49,27 +45,25 @@
 	kSeqFrameDiff = 1
 };
 
+SeqDecoder::SeqDecoder() {
+	_fileStream = 0;
+	_surface = 0;
+	_dirtyPalette = false;
+}
+
 SeqDecoder::~SeqDecoder() {
-	closeFile();
+	close();
 }
 
-bool SeqDecoder::loadFile(const char *fileName, int frameDelay) {
-	closeFile();
+bool SeqDecoder::load(Common::SeekableReadStream &stream) {
+	close();
 
-	_fileStream = SearchMan.createReadStreamForMember(fileName);
-	if (!_fileStream)
-		return false;
+	_fileStream = &stream;
+	_surface = new Graphics::Surface();
+	_surface->create(getWidth(), getHeight(), 1);
 
-	// Seek to the first frame
-	_videoInfo.currentFrame = 0;
+	_frameCount = _fileStream->readUint16LE();
 
-	_videoInfo.width = SCREEN_WIDTH;
-	_videoInfo.height = SCREEN_HEIGHT;
-	_videoInfo.frameCount = _fileStream->readUint16LE();
-	// Our frameDelay is calculated in 1/100 ms, so we convert it here
-	_videoInfo.frameDelay = 100 * frameDelay * 1000 / 60;
-	_videoFrameBuffer = new byte[_videoInfo.width * _videoInfo.height];
-
 	// Set palette
 	int paletteSize = _fileStream->readUint32LE();
 
@@ -81,39 +75,38 @@
 	uint16 palColorStart = READ_LE_UINT16(paletteData + 25);
 	uint16 palColorCount = READ_LE_UINT16(paletteData + 29);
 
-	byte palette[256 * 4];
 	int palOffset = 37;
 
 	for (uint16 colorNo = palColorStart; colorNo < palColorStart + palColorCount; colorNo++) {
 		if (palFormat == kSeqPalVariable)
 			palOffset++;
-		palette[colorNo * 4 + 0] = paletteData[palOffset++];
-		palette[colorNo * 4 + 1] = paletteData[palOffset++];
-		palette[colorNo * 4 + 2] = paletteData[palOffset++];
-		palette[colorNo * 4 + 3] = 0;
+		_palette[colorNo * 3 + 0] = paletteData[palOffset++];
+		_palette[colorNo * 3 + 1] = paletteData[palOffset++];
+		_palette[colorNo * 3 + 2] = paletteData[palOffset++];
 	}
 
-	g_system->setPalette(palette, 0, 256);
-
+	_dirtyPalette = true;
 	delete[] paletteData;
-
-	_videoInfo.firstframeOffset = _fileStream->pos();
-
 	return true;
 }
 
-void SeqDecoder::closeFile() {
+void SeqDecoder::close() {
 	if (!_fileStream)
 		return;
 
+	_frameDelay = 0;
+
 	delete _fileStream;
 	_fileStream = 0;
 
-	delete[] _videoFrameBuffer;
-	_videoFrameBuffer = 0;
+	_surface->free();
+	delete _surface;
+	_surface = 0;
+
+	reset();
 }
 
-bool SeqDecoder::decodeNextFrame() {
+Graphics::Surface *SeqDecoder::decodeNextFrame() {
 	int16 frameWidth = _fileStream->readUint16LE();
 	int16 frameHeight = _fileStream->readUint16LE();
 	int16 frameLeft = _fileStream->readUint16LE();
@@ -129,40 +122,41 @@
 
 	_fileStream->seek(offset);
 
-	if (_videoInfo.currentFrame == 0)
-		_videoInfo.startTime = g_system->getMillis();
-
 	if (frameType == kSeqFrameFull) {
-		byte *dst = _videoFrameBuffer + frameTop * SCREEN_WIDTH + frameLeft;
+		byte *dst = (byte *)_surface->pixels + frameTop * getWidth() + frameLeft;
 
 		byte *linebuf = new byte[frameWidth];
 
 		do {
 			_fileStream->read(linebuf, frameWidth);
 			memcpy(dst, linebuf, frameWidth);
-			dst += SCREEN_WIDTH;
+			dst += getWidth();
 		} while (--frameHeight);
 
 		delete[] linebuf;
 	} else {
 		byte *buf = new byte[frameSize];
 		_fileStream->read(buf, frameSize);
-		decodeFrame(buf, rleSize, buf + rleSize, frameSize - rleSize, _videoFrameBuffer + SCREEN_WIDTH * frameTop, frameLeft, frameWidth, frameHeight, colorKey);
+		decodeFrame(buf, rleSize, buf + rleSize, frameSize - rleSize, (byte *)_surface->pixels + getWidth() * frameTop, frameLeft, frameWidth, frameHeight, colorKey);
 		delete[] buf;
 	}
 
-	return ++_videoInfo.currentFrame < _videoInfo.frameCount;
+	if (_curFrame == -1)
+		_startTime = g_system->getMillis();
+
+	_curFrame++;
+	return _surface;
 }
 
 #define WRITE_TO_BUFFER(n) \
-	if (writeRow * SCREEN_WIDTH + writeCol + (n) > SCREEN_WIDTH * height) { \
+	if (writeRow * getWidth() + writeCol + (n) > getWidth() * height) { \
 		warning("SEQ player: writing out of bounds, aborting"); \
 		return false; \
 	} \
 	if (litPos + (n) > litSize) { \
 		warning("SEQ player: reading out of bounds, aborting"); \
 	} \
-	memcpy(dest + writeRow * SCREEN_WIDTH + writeCol, litData + litPos, n);
+	memcpy(dest + writeRow * getWidth() + writeCol, litData + litPos, n);
 
 bool SeqDecoder::decodeFrame(byte *rleData, int rleSize, byte *litData, int litSize, byte *dest, int left, int width, int height, int colorKey) {
 	int writeRow = 0;
Index: engines/sword1/animation.h
===================================================================
--- engines/sword1/animation.h	(revision 48158)
+++ engines/sword1/animation.h	(working copy)
@@ -28,7 +28,7 @@
 
 #include "graphics/video/dxa_decoder.h"
 #include "graphics/video/smk_decoder.h"
-#include "graphics/video/video_player.h"
+#include "graphics/video/video_decoder.h"
 
 #include "sword1/screen.h"
 #include "sword1/sound.h"
@@ -61,18 +61,20 @@
 	DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle);
 	~DXADecoderWithSound() {}
 
-	int32 getAudioLag();
+	uint32 getElapsedTime() const;
+
 private:
 	Audio::Mixer *_mixer;
 	Audio::SoundHandle *_bgSoundHandle;
 };
 
-class MoviePlayer : public Graphics::VideoPlayer {
+class MoviePlayer {
 public:
 	MoviePlayer(SwordEngine *vm, Text *textMan, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Graphics::VideoDecoder *decoder, DecoderType decoderType);
 	virtual ~MoviePlayer();
 	bool load(uint32 id);
 	void play();
+
 protected:
 	SwordEngine *_vm;
 	Text *_textMan;
@@ -82,10 +84,15 @@
 	int _textX, _textY, _textWidth, _textHeight;
 	DecoderType _decoderType;
 
+	Graphics::VideoDecoder *_decoder;
 	Audio::SoundHandle *_bgSoundHandle;
 	Audio::AudioStream *_bgSoundStream;
 
+	bool playVideo();
 	void performPostProcessing(byte *screen);
+
+	byte findBlackPalIndex();
+	byte findWhitePalIndex();
 };
 
 MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, Audio::Mixer *snd, OSystem *system);
Index: engines/sword1/animation.cpp
===================================================================
--- engines/sword1/animation.cpp	(revision 48158)
+++ engines/sword1/animation.cpp	(working copy)
@@ -68,9 +68,10 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Graphics::VideoDecoder *decoder, DecoderType decoderType)
-	: _vm(vm), _textMan(textMan), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system), VideoPlayer(decoder) {
+	: _vm(vm), _textMan(textMan), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system) {
 	_bgSoundStream = NULL;
 	_decoderType = decoderType;
+	_decoder = decoder;
 }
 
 MoviePlayer::~MoviePlayer() {
@@ -86,11 +87,10 @@
 	Common::File f;
 	char filename[20];
 
-	if (_decoderType == kVideoDecoderDXA) {
+	if (_decoderType == kVideoDecoderDXA)
 		_bgSoundStream = Audio::SeekableAudioStream::openStreamFile(sequenceList[id]);
-	} else {
+	else
 		_bgSoundStream = NULL;
-	}
 
 	if (SwordEngine::_systemVars.showText) {
 		sprintf(filename, "%s.txt", sequenceList[id]);
@@ -146,9 +146,9 @@
 }
 
 void MoviePlayer::play() {
-	if (_bgSoundStream) {
+	if (_bgSoundStream)
 		_snd->playInputStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream);
-	}
+
 	bool terminated = false;
 
 	_textX = 0;
@@ -205,10 +205,10 @@
 			for (x = 0; x < _textWidth; x++) {
 				switch (src[x]) {
 				case BORDER_COL:
-					dst[x] = _decoder->getBlack();
+					dst[x] = findBlackPalIndex();
 					break;
 				case LETTER_COL:
-					dst[x] = _decoder->getWhite();
+					dst[x] = findWhitePalIndex();
 					break;
 				}
 			}
@@ -228,12 +228,12 @@
 
 		for (y = 0; y < _textHeight; y++) {
 			if (_textY + y < frameY || _textY + y >= frameY + frameHeight) {
-				memset(dst + _textX, _decoder->getBlack(), _textWidth);
+				memset(dst + _textX, findBlackPalIndex(), _textWidth);
 			} else {
 				if (frameX > _textX)
-					memset(dst + _textX, _decoder->getBlack(), frameX - _textX);
+					memset(dst + _textX, findBlackPalIndex(), frameX - _textX);
 				if (frameX + frameWidth < _textX + _textWidth)
-					memset(dst + frameX + frameWidth, _decoder->getBlack(), _textX + _textWidth - (frameX + frameWidth));
+					memset(dst + frameX + frameWidth, findBlackPalIndex(), _textX + _textWidth - (frameX + frameWidth));
 			}
 
 			dst += _system->getWidth();
@@ -244,25 +244,51 @@
 	}
 }
 
-DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle)
-	: _mixer(mixer), _bgSoundHandle(bgSoundHandle)  {
+bool MoviePlayer::playVideo() {
+	uint16 x = (g_system->getWidth() - _decoder->getWidth()) / 2;
+	uint16 y = (g_system->getHeight() - _decoder->getHeight()) / 2;
+
+	while (!_vm->shouldQuit() && !_decoder->endOfVideo()) {
+		if (_decoder->needsUpdate()) {
+			Graphics::Surface *frame = _decoder->decodeNextFrame();
+			if (frame)
+				_vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+
+			if (_decoder->hasDirtyPalette())
+				_decoder->setSystemPalette();
+
+			Graphics::Surface *screen = _vm->_system->lockScreen();
+			performPostProcessing((byte *)screen->pixels);
+			_vm->_system->unlockScreen();
+			_vm->_system->updateScreen();
+		}
+
+		Common::Event event;
+		while (_vm->_system->getEventManager()->pollEvent(event))
+			if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
+				return false;
+	}
+
+	return true;
 }
 
-int32 DXADecoderWithSound::getAudioLag() {
-	if (!_fileStream)
-		return 0;
+byte MoviePlayer::findBlackPalIndex() {
+	return 0;
+}
 
-	if (!_mixer->isSoundHandleActive(*_bgSoundHandle))
-		return 0;
+byte MoviePlayer::findWhitePalIndex() {
+	return 0xff;
+}
 
-	int32 frameDelay = getFrameDelay();
-	int32 videoTime = _videoInfo.currentFrame * frameDelay;
-	int32 audioTime;
+DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle)
+	: _mixer(mixer), _bgSoundHandle(bgSoundHandle)  {
+}
 
-	const Audio::Timestamp ts = _mixer->getElapsedTime(*_bgSoundHandle);
-	audioTime = ts.convertToFramerate(100000).totalNumberOfFrames();
+uint32 DXADecoderWithSound::getElapsedTime() const {
+	if (_mixer->isSoundHandleActive(*_bgSoundHandle))
+		return _mixer->getSoundElapsedTime(*_bgSoundHandle);
 
-	return videoTime - audioTime;
+	return VideoDecoder::getElapsedTime();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
Index: engines/sword2/animation.h
===================================================================
--- engines/sword2/animation.h	(revision 48158)
+++ engines/sword2/animation.h	(working copy)
@@ -30,7 +30,7 @@
 
 #include "graphics/video/dxa_decoder.h"
 #include "graphics/video/smk_decoder.h"
-#include "graphics/video/video_player.h"
+#include "graphics/video/video_decoder.h"
 #include "sound/mixer.h"
 
 #include "sword2/screen.h"
@@ -63,13 +63,13 @@
 	DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle);
 	~DXADecoderWithSound() {}
 
-	int32 getAudioLag();
+	uint32 getElapsedTime() const;
 private:
 	Audio::Mixer *_mixer;
 	Audio::SoundHandle *_bgSoundHandle;
 };
 
-class MoviePlayer : public Graphics::VideoPlayer {
+class MoviePlayer {
 public:
 	MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Graphics::VideoDecoder *decoder, DecoderType decoderType);
 	virtual ~MoviePlayer();
@@ -89,6 +89,7 @@
 	int _textX, _textY;
 	DecoderType _decoderType;
 
+	Graphics::VideoDecoder *_decoder;
 	Audio::SoundHandle *_bgSoundHandle;
 	Audio::AudioStream *_bgSoundStream;
 
@@ -96,10 +97,14 @@
 	int _leadOutFrame;
 
 	void performPostProcessing(byte *screen);
+	bool playVideo();
 
 	void openTextObject(uint32 index);
 	void closeTextObject(uint32 index, byte *screen);
 	void drawTextObject(uint32 index, byte *screen);
+
+	byte findBlackPalIndex();
+	byte findWhitePalIndex();
 };
 
 MoviePlayer *makeMoviePlayer(const char *name, Sword2Engine *vm, Audio::Mixer *snd, OSystem *system);
Index: engines/sword2/animation.cpp
===================================================================
--- engines/sword2/animation.cpp	(revision 48158)
+++ engines/sword2/animation.cpp	(working copy)
@@ -47,9 +47,10 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 MoviePlayer::MoviePlayer(Sword2Engine *vm, Audio::Mixer *snd, OSystem *system, Audio::SoundHandle *bgSoundHandle, Graphics::VideoDecoder *decoder, DecoderType decoderType)
-	: _vm(vm), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system), VideoPlayer(decoder) {
+	: _vm(vm), _snd(snd), _bgSoundHandle(bgSoundHandle), _system(system) {
 	_bgSoundStream = NULL;
 	_decoderType = decoderType;
+	_decoder = decoder;
 }
 
 MoviePlayer:: ~MoviePlayer() {
@@ -62,11 +63,10 @@
  * @param id the id of the file
  */
 bool MoviePlayer::load(const char *name) {
-	if (_decoderType == kVideoDecoderDXA) {
+	if (_decoderType == kVideoDecoderDXA)
 		_bgSoundStream = Audio::SeekableAudioStream::openStreamFile(name);
-	} else {
+	else
 		_bgSoundStream = NULL;
-	}
 
 	_textSurface = NULL;
 
@@ -97,13 +97,11 @@
 	_currentMovieText = 0;
 	_leadOut = leadOut;
 
-	if (leadIn) {
+	if (leadIn)
 		_vm->_sound->playMovieSound(leadIn, kLeadInSound);
-	}
 
-	if (_bgSoundStream) {
+	if (_bgSoundStream)
 		_snd->playInputStream(Audio::Mixer::kSFXSoundType, _bgSoundHandle, _bgSoundStream);
-	}
 
 	bool terminated = false;
 
@@ -186,12 +184,12 @@
 
 				for (int y = 0; y < text->_textSprite.h; y++) {
 					if (_textY + y < frameY || _textY + y >= frameY + frameHeight) {
-						memset(dst + _textX, _decoder->getBlack(), text->_textSprite.w);
+						memset(dst + _textX, findBlackPalIndex(), text->_textSprite.w);
 					} else {
 						if (frameX > _textX)
-							memset(dst + _textX, _decoder->getBlack(), frameX - _textX);
+							memset(dst + _textX, findBlackPalIndex(), frameX - _textX);
 						if (frameX + frameWidth < _textX + text->_textSprite.w)
-							memset(dst + frameX + frameWidth, _decoder->getBlack(), _textX + text->_textSprite.w - (frameX + frameWidth));
+							memset(dst + frameX + frameWidth, findBlackPalIndex(), _textX + text->_textSprite.w - (frameX + frameWidth));
 					}
 
 					dst += _system->getWidth();
@@ -207,8 +205,8 @@
 void MoviePlayer::drawTextObject(uint32 index, byte *screen) {
 	MovieText *text = &_movieTexts[index];
 
-	byte white = _decoder->getWhite();
-	byte black = _decoder->getBlack();
+	byte white = findWhitePalIndex();
+	byte black = findBlackPalIndex();
 
 	if (text->_textMem && _textSurface) {
 		byte *src = text->_textSprite.data;
@@ -272,25 +270,51 @@
 	}
 }
 
-DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle)
-	: _mixer(mixer), _bgSoundHandle(bgSoundHandle)  {
+bool MoviePlayer::playVideo() {
+	uint16 x = (g_system->getWidth() - _decoder->getWidth()) / 2;
+	uint16 y = (g_system->getHeight() - _decoder->getHeight()) / 2;
+
+	while (!_vm->shouldQuit() && !_decoder->endOfVideo()) {
+		if (_decoder->needsUpdate()) {
+			Graphics::Surface *frame = _decoder->decodeNextFrame();
+			if (frame)
+				_vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h);
+
+			if (_decoder->hasDirtyPalette())
+				_decoder->setSystemPalette();
+
+			Graphics::Surface *screen = _vm->_system->lockScreen();
+			performPostProcessing((byte *)screen->pixels);
+			_vm->_system->unlockScreen();
+			_vm->_system->updateScreen();
+		}
+
+		Common::Event event;
+		while (_vm->_system->getEventManager()->pollEvent(event))
+			if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
+				return false;
+	}
+
+	return true;
 }
 
-int32 DXADecoderWithSound::getAudioLag() {
-	if (!_fileStream)
-		return 0;
+byte MoviePlayer::findBlackPalIndex() {
+	return 0;
+}
 
-	if (!_mixer->isSoundHandleActive(*_bgSoundHandle))
-		return 0;
+byte MoviePlayer::findWhitePalIndex() {
+	return 0xff;
+}
 
-	int32 frameDelay = getFrameDelay();
-	int32 videoTime = _videoInfo.currentFrame * frameDelay;
-	int32 audioTime;
+DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle)
+	: _mixer(mixer), _bgSoundHandle(bgSoundHandle)  {
+}
 
-	const Audio::Timestamp ts = _mixer->getElapsedTime(*_bgSoundHandle);
-	audioTime = ts.convertToFramerate(100000).totalNumberOfFrames();
+uint32 DXADecoderWithSound::getElapsedTime() const {
+	if (_mixer->isSoundHandleActive(*_bgSoundHandle))
+		return _mixer->getSoundElapsedTime(*_bgSoundHandle);
 
-	return videoTime - audioTime;
+	return VideoDecoder::getElapsedTime();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
Index: engines/scumm/he/animation_he.h
===================================================================
--- engines/scumm/he/animation_he.h	(revision 48158)
+++ engines/scumm/he/animation_he.h	(working copy)
@@ -56,9 +56,6 @@
 
 	void copyFrameToBuffer(byte *dst, int dstType, uint x, uint y, uint pitch);
 	void handleNextFrame();
-
-protected:
-	virtual void setPalette(byte *pal);
 };
 
 } // End of namespace Scumm
Index: engines/scumm/he/script_v90he.cpp
===================================================================
--- engines/scumm/he/script_v90he.cpp	(revision 48158)
+++ engines/scumm/he/script_v90he.cpp	(working copy)
@@ -1437,7 +1437,7 @@
 			}
 		} else if (_videoParams.status == 165) {
 			// Stop video
-			_moviePlay->closeFile();
+			_moviePlay->close();
 		}
 		break;
 	default:
@@ -1464,7 +1464,7 @@
 		break;
 	case 52:	// Get current frame
 		pop();
-		push(_moviePlay->getCurFrame());
+		push(_moviePlay->endOfVideo() ? -1 : (_moviePlay->getCurFrame() + 1));
 		break;
 	case 63:	// Get image number
 		pop();
Index: engines/scumm/he/script_v100he.cpp
===================================================================
--- engines/scumm/he/script_v100he.cpp	(revision 48158)
+++ engines/scumm/he/script_v100he.cpp	(working copy)
@@ -2254,7 +2254,7 @@
 			}
 		} else if (_videoParams.status == 19) {
 			// Stop video
-			_moviePlay->closeFile();
+			_moviePlay->close();
 		}
 		break;
 	default:
@@ -2937,7 +2937,7 @@
 		break;
 	case 73:
 		pop();
-		push(_moviePlay->getCurFrame());
+		push(_moviePlay->endOfVideo() ? -1 : (_moviePlay->getCurFrame() + 1));
 		break;
 	case 84:
 		pop();
Index: engines/scumm/he/animation_he.cpp
===================================================================
--- engines/scumm/he/animation_he.cpp	(revision 48158)
+++ engines/scumm/he/animation_he.cpp	(working copy)
@@ -46,23 +46,21 @@
 }
 
 int MoviePlayer::load(const char *filename, int flags, int image) {
-	if (isVideoLoaded()) {
-		closeFile();
-	}
+	if (isVideoLoaded())
+		close();
 
-	if (!loadFile(filename)) {
+	if (!loadFile(Common::String(filename))) {
 		warning("Failed to load video file %s", filename);
 		return -1;
 	}
+
 	debug(1, "Playing video %s", filename);
 
-	if (flags & 2) {
+	if (flags & 2)
 		_vm->_wiz->createWizEmptyImage(image, 0, 0, getWidth(), getHeight());
-	}
 
 	_flags = flags;
 	_wizResNum = image;
-
 	return 0;
 }
 
@@ -70,8 +68,12 @@
 	uint h = getHeight();
 	uint w = getWidth();
 
-	byte *src = _videoFrameBuffer;
+	Graphics::Surface *surface = decodeNextFrame();
+	byte *src = (byte *)surface->pixels;
 
+	if (hasDirtyPalette())
+		_vm->setPaletteFromPtr(getPalette(), 256);
+
 	if (_vm->_game.features & GF_16BIT_COLOR) {
 		dst += y * pitch + x * 2;
 		do {
@@ -102,14 +104,11 @@
 }
 
 void MoviePlayer::handleNextFrame() {
-	if (!isVideoLoaded()) {
+	if (!isVideoLoaded())
 		return;
-	}
 
 	VirtScreen *pvs = &_vm->_virtscr[kMainVirtScreen];
 
-	decodeNextFrame();
-
 	if (_flags & 2) {
 		uint8 *dstPtr = _vm->getResourceAddress(rtImage, _wizResNum);
 		assert(dstPtr);
@@ -128,15 +127,10 @@
 		_vm->markRectAsDirty(kMainVirtScreen, imageRect);
 	}
 
-	if (getCurFrame() == getFrameCount()) {
-		closeFile();
-	}
+	if (endOfVideo())
+		close();
 }
 
-void MoviePlayer::setPalette(byte *pal) {
-	_vm->setPaletteFromPtr(pal, 256);
-}
-
 } // End of namespace Scumm
 
 #endif // ENABLE_HE
Index: engines/agos/animation.h
===================================================================
--- engines/agos/animation.h	(revision 48158)
+++ engines/agos/animation.h	(working copy)
@@ -72,6 +72,9 @@
 	virtual void handleNextFrame();
 	virtual bool processFrame() = 0;
 	virtual void startSound() {};
+
+protected:
+	uint32 _firstFrameOffset;
 };
 
 class MoviePlayerDXA : public MoviePlayer, ::Graphics::DXADecoder {
@@ -84,13 +87,12 @@
 	void playVideo();
 	void nextFrame();
 	virtual void stopVideo();
-protected:
-	void setPalette(byte *pal);
 
 private:
 	void handleNextFrame();
 	bool processFrame();
 	void startSound();
+	void copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch);
 };
 
 class MoviePlayerSMK : public MoviePlayer, ::Graphics::SmackerDecoder {
@@ -101,12 +103,12 @@
 	void playVideo();
 	void nextFrame();
 	virtual void stopVideo();
-protected:
-	void setPalette(byte *pal);
+
 private:
 	void handleNextFrame();
 	bool processFrame();
 	void startSound();
+	void copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch);
 };
 
 MoviePlayer *makeMoviePlayer(AGOSEngine_Feeble *vm, const char *name);
Index: engines/agos/animation.cpp
===================================================================
--- engines/agos/animation.cpp	(revision 48158)
+++ engines/agos/animation.cpp	(working copy)
@@ -241,29 +241,46 @@
 }
 
 bool MoviePlayerDXA::load() {
-	char videoName[20];
-	uint i;
-
 	if ((_vm->getPlatform() == Common::kPlatformAmiga || _vm->getPlatform() == Common::kPlatformMacintosh) &&
 		_vm->_language != Common::EN_ANY) {
 		_sequenceNum = 0;
-		for (i = 0; i < 90; i++) {
+		for (uint i = 0; i < 90; i++) {
 			if (!scumm_stricmp(baseName, _sequenceList[i]))
 				_sequenceNum = i;
 		}
 	}
 
-	sprintf(videoName, "%s.dxa", baseName);
+	Common::String videoName = Common::String::printf("%s.dxa", baseName);
 	if (!loadFile(videoName))
-		error("Failed to load video file %s", videoName);
+		error("Failed to load video file %s", videoName.c_str());
 
-	debug(0, "Playing video %s", videoName);
+	debug(0, "Playing video %s", videoName.c_str());
 
 	CursorMan.showMouse(false);
 
+	_firstFrameOffset = _fileStream->pos();
+
 	return true;
 }
 
+void MoviePlayerDXA::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) {
+	uint h = getHeight();
+	uint w = getWidth();
+
+	Graphics::Surface *surface = decodeNextFrame();
+	byte *src = (byte *)surface->pixels;
+	dst += y * pitch + x;
+
+	do {
+		memcpy(dst, src, w);
+		dst += pitch;
+		src += w;
+	} while (--h);
+
+	if (hasDirtyPalette())
+		setSystemPalette();
+}
+
 void MoviePlayerDXA::playVideo() {
 	// Most of the videos included in the Amiga version, reduced the
 	// resoluton to 384 x 280, so require the screen to be cleared,
@@ -272,12 +289,12 @@
 		_vm->clearSurfaces();
 	}
 
-	while (getCurFrame() < getFrameCount() && !_skipMovie && !_vm->shouldQuit())
+	while (!endOfVideo() && !_skipMovie && !_vm->shouldQuit())
 		handleNextFrame();
 }
 
 void MoviePlayerDXA::stopVideo() {
-	closeFile();
+	close();
 	_mixer->stopHandle(_bgSound);
 }
 
@@ -318,53 +335,36 @@
 }
 
 void MoviePlayerDXA::nextFrame() {
-	if (_bgSoundStream && _vm->_mixer->isSoundHandleActive(_bgSound) && (_vm->_mixer->getSoundElapsedTime(_bgSound) * getFrameRate()) / 1000 < (uint32)getCurFrame()) {
+	if (_bgSoundStream && _vm->_mixer->isSoundHandleActive(_bgSound) && needsUpdate()) {
 		copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth);
 		return;
 	}
 
-	if (_vm->_interactiveVideo == TYPE_LOOPING && getCurFrame() == getFrameCount()) {
-		_fileStream->seek(_videoInfo.firstframeOffset);
-		_videoInfo.currentFrame = 0;
+	if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) {
+		_fileStream->seek(_firstFrameOffset);
+		_curFrame = -1;
 		startSound();
 	}
 
-	if (getCurFrame() < getFrameCount()) {
-		decodeNextFrame();
-		if (_vm->_interactiveVideo == TYPE_OMNITV) {
+	if (!endOfVideo()) {
+		if (_vm->_interactiveVideo == TYPE_OMNITV)
 			copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth);
-		} else if (_vm->_interactiveVideo == TYPE_LOOPING) {
+		else if (_vm->_interactiveVideo == TYPE_LOOPING)
 			copyFrameToBuffer(_vm->getBackBuf(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth);
-		}
 	} else if (_vm->_interactiveVideo == TYPE_OMNITV) {
-		closeFile();
+		close();
 		_vm->_interactiveVideo = 0;
 		_vm->_variableArray[254] = 6747;
 	}
 }
 
 void MoviePlayerDXA::handleNextFrame() {
-	decodeNextFrame();
 	if (processFrame())
 		_vm->_system->updateScreen();
 
 	MoviePlayer::handleNextFrame();
 }
 
-void MoviePlayerDXA::setPalette(byte *pal) {
-	byte palette[1024];
-	byte *p = palette;
-
-	for (int i = 0; i < 256; i++) {
-		*p++ = *pal++;
-		*p++ = *pal++;
-		*p++ = *pal++;
-		*p++ = 0;
-	}
-
-	_vm->_system->setPalette(palette, 0, 256);
-}
-
 bool MoviePlayerDXA::processFrame() {
 	Graphics::Surface *screen = _vm->_system->lockScreen();
 	copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth);
@@ -381,7 +381,7 @@
 			// sync case for the subsequent frames.
 			_ticks = _vm->_system->getMillis();
 		} else {
-			_ticks += getFrameWaitTime();
+			_ticks += getTimeToNextFrame();
 			while (_vm->_system->getMillis() < _ticks)
 				_vm->_system->delayMillis(10);
 		}
@@ -407,84 +407,82 @@
 }
 
 bool MoviePlayerSMK::load() {
-	char videoName[20];
+	Common::String videoName = Common::String::printf("%s.smk", baseName);
 
-	sprintf(videoName, "%s.smk", baseName);
 	if (!loadFile(videoName))
-		error("Failed to load video file %s", videoName);
+		error("Failed to load video file %s", videoName.c_str());
 
-	debug(0, "Playing video %s", videoName);
+	debug(0, "Playing video %s", videoName.c_str());
 
 	CursorMan.showMouse(false);
 
+	_firstFrameOffset = _fileStream->pos();
+
 	return true;
 }
 
+void MoviePlayerSMK::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) {
+	uint h = getHeight();
+	uint w = getWidth();
+
+	Graphics::Surface *surface = decodeNextFrame();
+	byte *src = (byte *)surface->pixels;
+	dst += y * pitch + x;
+
+	do {
+		memcpy(dst, src, w);
+		dst += pitch;
+		src += w;
+	} while (--h);
+
+	if (hasDirtyPalette())
+		setSystemPalette();
+}
+
 void MoviePlayerSMK::playVideo() {
-	while (getCurFrame() < getFrameCount() && !_skipMovie && !_vm->shouldQuit())
+	while (!endOfVideo() && !_skipMovie && !_vm->shouldQuit())
 		handleNextFrame();
 }
 
 void MoviePlayerSMK::stopVideo() {
-	closeFile();
+	close();
 }
 
 void MoviePlayerSMK::startSound() {
 }
 
 void MoviePlayerSMK::handleNextFrame() {
-	decodeNextFrame();
 	processFrame();
 
 	MoviePlayer::handleNextFrame();
 }
 
 void MoviePlayerSMK::nextFrame() {
-	if (_vm->_interactiveVideo == TYPE_LOOPING && getCurFrame() == getFrameCount()) {
-		_fileStream->seek(_videoInfo.firstframeOffset);
-		_videoInfo.currentFrame = 0;
+	if (_vm->_interactiveVideo == TYPE_LOOPING && endOfVideo()) {
+		_fileStream->seek(_firstFrameOffset);
+		_curFrame = -1;
 	}
 
-	if (getCurFrame() < getFrameCount()) {
-		decodeNextFrame();
+	if (!endOfVideo()) {
 		if (_vm->_interactiveVideo == TYPE_OMNITV) {
 			copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth);
 		} else if (_vm->_interactiveVideo == TYPE_LOOPING) {
 			copyFrameToBuffer(_vm->getBackBuf(), (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth);
 		}
 	} else if (_vm->_interactiveVideo == TYPE_OMNITV) {
-		closeFile();
+		close();
 		_vm->_interactiveVideo = 0;
 		_vm->_variableArray[254] = 6747;
 	}
 }
 
-void MoviePlayerSMK::setPalette(byte *pal) {
-	byte palette[1024];
-	byte *p = palette;
-
-	for (int i = 0; i < 256; i++) {
-		*p++ = *pal++;
-		*p++ = *pal++;
-		*p++ = *pal++;
-		*p++ = 0;
-	}
-
-	_vm->_system->setPalette(palette, 0, 256);
-}
-
 bool MoviePlayerSMK::processFrame() {
 	Graphics::Surface *screen = _vm->_system->lockScreen();
 	copyFrameToBuffer((byte *)screen->pixels, (_vm->_screenWidth - getWidth()) / 2, (_vm->_screenHeight - getHeight()) / 2, _vm->_screenWidth);
 	_vm->_system->unlockScreen();
 
-	uint32 waitTime = getFrameWaitTime();
+	uint32 waitTime = getTimeToNextFrame();
 
-	if (!waitTime) {
-		warning("dropped frame %i", getCurFrame());
-		return false;
-	}
-
 	_vm->_system->updateScreen();
 
 	// Wait before showing the next frame
Index: graphics/module.mk
===================================================================
--- graphics/module.mk	(revision 48158)
+++ graphics/module.mk	(working copy)
@@ -26,7 +26,7 @@
 	video/flic_decoder.o \
 	video/mpeg_player.o \
 	video/smk_decoder.o \
-	video/video_player.o \
+	video/video_decoder.o \
 	video/codecs/msrle.o \
 	video/codecs/msvideo1.o \
 	video/coktelvideo/indeo3.o \
Index: graphics/video/video_decoder.h
===================================================================
--- graphics/video/video_decoder.h	(revision 48157)
+++ graphics/video/video_decoder.h	(working copy)
@@ -30,6 +30,9 @@
 #include "common/list.h"
 #include "common/stream.h"
 
+#include "graphics/surface.h"
+#include "graphics/pixelformat.h"
+
 namespace Common {
 	class SeekableReadStream;
 }
@@ -42,173 +45,137 @@
 class VideoDecoder {
 public:
 	VideoDecoder();
-	virtual ~VideoDecoder();
+	virtual ~VideoDecoder() {}
 
 	/**
 	 * Returns the width of the video
 	 * @return the width of the video
 	 */
-	virtual int getWidth();
+	virtual uint16 getWidth() const = 0;
 
 	/**
 	 * Returns the height of the video
 	 * @return the height of the video
 	 */
-	virtual int getHeight();
+	virtual uint16 getHeight() const = 0;
 
 	/**
 	 * Returns the current frame number of the video
-	 * @return the current frame number of the video
+	 * @return the last frame decoded by the video
 	 */
-	virtual int32 getCurFrame();
+	virtual int32 getCurFrame() const { return _curFrame; }
 
 	/**
 	 * Returns the amount of frames in the video
 	 * @return the amount of frames in the video
 	 */
-	virtual int32 getFrameCount();
+	virtual uint32 getFrameCount() const = 0;
 
 	/**
-	 * Returns the frame rate of the video
-	 * @return the frame rate of the video
+	 * Returns the time (in ms) that the video has been running
 	 */
-	virtual int32 getFrameRate();
+	virtual uint32 getElapsedTime() const;
 
 	/**
-	 * Returns the time to wait for each frame in 1/100 ms (to avoid rounding errors)
-	 * @return the time to wait for each frame in 1/100 ms (to avoid rounding errors)
+	 * Returns whether a frame should be decoded or not
+	 * @return whether a frame should be decoded or not
 	 */
-	virtual int32 getFrameDelay();
+	virtual bool needsUpdate() const;
 
 	/**
-	 * Returns the current A/V lag in 1/100 ms (to avoid rounding errors)
-	 * If > 0, audio lags behind
-	 * If < 0, video lags behind
-	 * @return the current A/V lag in 1/100 ms (to avoid rounding errors)
+	 * Load a video file
+	 * @param filename	the filename to load
 	 */
-	virtual int32 getAudioLag();
+	virtual bool loadFile(const Common::String &filename);
 
 	/**
-	 * Returns the time to wait until the next frame in ms, minding any lag
-	 * @return the time to wait until the next frame in ms
-	 */
-	virtual uint32 getFrameWaitTime();
-
-	/**
 	 * Load a video file
-	 * @param filename	the filename to load
+	 * @param stream  the stream to load
 	 */
-	virtual bool loadFile(const char *filename) = 0;
+	virtual bool load(Common::SeekableReadStream &stream) = 0;
 
 	/**
 	 * Close a video file
 	 */
-	virtual void closeFile() = 0;
+	virtual void close() = 0;
 
 	/**
 	 * Returns if a video file is loaded or not
 	 */
-	bool isVideoLoaded() { return (_fileStream != NULL); }
+	virtual bool isVideoLoaded() const = 0;
 
 	/**
-	 * Set RGB palette, based on current frame
-	 * @param pal		the RGB palette data
+	 * Decode the next frame and return the frame's surface
+	 * @note the return surface should *not* be freed
+	 * @note this may return 0, in which case the last frame should be kept on screen
 	 */
-	virtual void setPalette(byte *pal);
+	virtual Surface *decodeNextFrame() = 0;
 
 	/**
-	 * Gets the value of the pixel at the specified x and y coordinates
-	 * Note: This method assumes that the video's pitch equals its width, and that
-	 * the video has an 8bpp palette
-	 * @param x	the x coordinate of the pixel
-	 * @param y	the y coordinate of the pixel
+	 * Get the pixel format of the video
 	 */
-	byte getPixel(int x, int y) {
-		return *(_videoFrameBuffer + y * _videoInfo.width + x * 1);
-	}
+	virtual PixelFormat getPixelFormat() const = 0;
 
 	/**
-	 * Gets the value of the pixel at the specified offset
-	 * @param offset	the offset of the pixel in the video buffer
+	 * Get the palette for the video in RGB format (if 8bpp or less)
 	 */
-	byte getPixel(int offset) { return getPixel(offset, 0); }
+	virtual byte *getPalette() { return 0; }
 
 	/**
-	 * Return the black palette color for the current frame
+	 * Returns if the palette is dirty or not
 	 */
-	byte getBlack() { return _curFrameBlack; }
+	virtual bool hasDirtyPalette() const { return false; }
 
 	/**
-	 * Return the white palette color for the current frame
+	 * Add the time the video has been paused to maintain sync
 	 */
-	byte getWhite() { return _curFrameWhite; }
+	virtual void addPauseTime(uint32 ms) { _startTime += ms; }
 
 	/**
-	 * Copy current frame into the specified position of the destination
-	 * buffer.
-	 * @param dst		the buffer
-	 * @param x		the x position of the buffer
-	 * @param y		the y position of the buffer
-	 * @param pitch		the pitch of buffer
+	 * Returns if the video is finished or not
 	 */
-	void copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch);
+	virtual bool endOfVideo() const;
 
 	/**
-	 * Decode the next frame to _videoFrameBuffer
+	 * Set the current palette to the system palette
 	 */
-	virtual bool decodeNextFrame() = 0;
+	void setSystemPalette();
 
+	/**
+	 * Return the time until the next frame (in ms)
+	 */
+	virtual uint32 getTimeToNextFrame() const = 0;
+
 protected:
-	struct {
-		uint32 width;
-		uint32 height;
-		uint32 frameCount;
-		int32 frameRate;
-		int32 frameDelay;		// 1/100 ms (to avoid rounding errors)
-		uint32 firstframeOffset;
-		uint32 currentFrame;
-		uint32 startTime;
-	} _videoInfo;
+	/**
+	 * Resets _curFrame and _startTime. Should be called from every close() function.
+	 */
+	void reset();
 
-	byte _curFrameBlack, _curFrameWhite;
-
-	Common::SeekableReadStream *_fileStream;
-	byte *_videoFrameBuffer;
+	int32 _curFrame;
+	uint32 _startTime;
 };
 
-class VideoPlayer {
+/**
+ * A VideoDecoder wrapper that implements getTimeToNextFrame() based on getFrameRate().
+ */
+class FixedRateVideoDecoder : public VideoDecoder {
 public:
-	VideoPlayer(VideoDecoder* decoder) : _skipVideo(false), _decoder(decoder)
-		{ }
-	virtual ~VideoPlayer() { }
-	/**
-	 * A default implementation of a video player
-	 * Plays a non-interactive full screen video till it's stopped by a
-	 * specific event
-	 * @param filename		the name of the file to play
-	 * @param stopEvents	a list of events that can stop the video
-	 *
-	 * Returns true if the video was played to the end, false if skipped
-	 */
-	bool playVideo(Common::List<Common::Event> &stopEvents);
+	FixedRateVideoDecoder() {}
+	virtual ~FixedRateVideoDecoder() {}
 
-	/**
-	 * Provides the same functionality as the video player, and it adds the
-	 * event of skipping the video with the escape key by default
-	 */
-	bool playVideo();
+	uint32 getTimeToNextFrame() const;
 
 protected:
 	/**
-	 * Perform postprocessing once the frame data is copied to the screen,
-	 * right before the frame is drawn. Called by playVideo()
+	 * Return the frame rate in frames per second
+	 * This returns a double because videos can have rates that are not integers and
+	 * there are some videos with frame rates < 1.
 	 */
-	virtual void performPostProcessing(byte *screen);
+	virtual double getFrameRate() const = 0;
 
-	bool _skipVideo;
-	VideoDecoder* _decoder;
-
-	void processVideoEvents(Common::List<Common::Event> &stopEvents);
+private:
+	uint32 getFrameBeginTime(uint32 frame) const;
 };
 
 } // End of namespace Graphics
Index: graphics/video/dxa_decoder.cpp
===================================================================
--- graphics/video/dxa_decoder.cpp	(revision 48158)
+++ graphics/video/dxa_decoder.cpp	(working copy)
@@ -39,11 +39,12 @@
 
 DXADecoder::DXADecoder() {
 	_fileStream = 0;
+	_surface = 0;
+	_dirtyPalette = false;
 
 	_frameBuffer1 = 0;
 	_frameBuffer2 = 0;
 	_scaledBuffer = 0;
-	_videoFrameBuffer = 0;
 
 	_inBuffer = 0;
 	_inBufferSize = 0;
@@ -51,67 +52,59 @@
 	_decompBuffer = 0;
 	_decompBufferSize = 0;
 
-	_videoInfo.width = 0;
-	_videoInfo.height = 0;
+	_width = 0;
+	_height = 0;
 
 	_frameSize = 0;
-	_videoInfo.frameCount = 0;
-	_videoInfo.currentFrame = 0;
-	_videoInfo.frameRate = 0;
-	_videoInfo.frameDelay = 0;
+	_frameCount = 0;
+	_frameRate = 0;
 
 	_scaleMode = S_NONE;
 }
 
 DXADecoder::~DXADecoder() {
-	closeFile();
+	close();
 }
 
-bool DXADecoder::loadFile(const char *fileName) {
-	uint32 tag;
-	int32 frameRate;
+bool DXADecoder::load(Common::SeekableReadStream &stream) {
+	close();
 
-	closeFile();
+	_fileStream = &stream;
 
-	_fileStream = SearchMan.createReadStreamForMember(fileName);
-	if (!_fileStream)
-		return false;
-
-	tag = _fileStream->readUint32BE();
+	uint32 tag = _fileStream->readUint32BE();
 	assert(tag == MKID_BE('DEXA'));
 
 	uint8 flags = _fileStream->readByte();
-	_videoInfo.frameCount = _fileStream->readUint16BE();
-	frameRate = _fileStream->readSint32BE();
+	_frameCount = _fileStream->readUint16BE();
+	int32 frameRate = _fileStream->readSint32BE();
 
-	if (frameRate > 0) {
-		_videoInfo.frameRate = 1000 / frameRate;
-		_videoInfo.frameDelay = frameRate * 100;
-	} else if (frameRate < 0) {
-		_videoInfo.frameRate = 100000 / (-frameRate);
-		_videoInfo.frameDelay = -frameRate;
-	} else {
-		_videoInfo.frameRate = 10;
-		_videoInfo.frameDelay = 10000;
-	}
+	if (frameRate > 0)
+		_frameRate = 1000 / frameRate;
+	else if (frameRate < 0)
+		_frameRate = 100000 / (-frameRate);
+	else
+		_frameRate = 10;
 
-	_videoInfo.width = _fileStream->readUint16BE();
-	_videoInfo.height = _fileStream->readUint16BE();
+	_width = _fileStream->readUint16BE();
+	_height = _fileStream->readUint16BE();
 
 	if (flags & 0x80) {
 		_scaleMode = S_INTERLACED;
-		_curHeight = _videoInfo.height / 2;
+		_curHeight = _height / 2;
 	} else if (flags & 0x40) {
 		_scaleMode = S_DOUBLE;
-		_curHeight = _videoInfo.height / 2;
+		_curHeight = _height / 2;
 	} else {
 		_scaleMode = S_NONE;
-		_curHeight = _videoInfo.height;
+		_curHeight = _height;
 	}
 
-	debug(2, "flags 0x0%x framesCount %d width %d height %d rate %d ticks %d", flags, getFrameCount(), getWidth(), getHeight(), getFrameRate(), getFrameDelay());
+	_surface = new Graphics::Surface();
+	_surface->bytesPerPixel = 1;
 
-	_frameSize = _videoInfo.width * _videoInfo.height;
+	debug(2, "flags 0x0%x framesCount %d width %d height %d rate %d", flags, getFrameCount(), getWidth(), getHeight(), (uint32)getFrameRate());
+
+	_frameSize = _width * _height;
 	_decompBufferSize = _frameSize;
 	_frameBuffer1 = (uint8 *)malloc(_frameSize);
 	memset(_frameBuffer1, 0, _frameSize);
@@ -135,9 +128,10 @@
 
 		do {
 			tag = _fileStream->readUint32BE();
-			if (tag != 0) {
+
+			if (tag != 0)
 				size = _fileStream->readUint32BE();
-			}
+
 			switch (tag) {
 				case 0: // No more tags
 					break;
@@ -159,20 +153,19 @@
 	// Read the sound header
 	_soundTag = _fileStream->readUint32BE();
 
-	_videoInfo.currentFrame = 0;
-
-	_videoInfo.firstframeOffset = _fileStream->pos();
-
 	return true;
 }
 
-void DXADecoder::closeFile() {
+void DXADecoder::close() {
 	if (!_fileStream)
 		return;
 
 	delete _fileStream;
 	_fileStream = 0;
 
+	delete _surface;
+	_surface = 0;
+
 	free(_frameBuffer1);
 	free(_frameBuffer2);
 	free(_scaledBuffer);
@@ -181,6 +174,8 @@
 
 	_inBuffer = 0;
 	_decompBuffer = 0;
+
+	reset();
 }
 
 void DXADecoder::decodeZlib(byte *data, int size, int totalSize) {
@@ -208,10 +203,10 @@
 
 	memcpy(_frameBuffer2, _frameBuffer1, _frameSize);
 
-	for (uint32 by = 0; by < _videoInfo.height; by += BLOCKH) {
-		for (uint32 bx = 0; bx < _videoInfo.width; bx += BLOCKW) {
+	for (uint32 by = 0; by < _height; by += BLOCKH) {
+		for (uint32 bx = 0; bx < _width; bx += BLOCKW) {
 			byte type = *dat++;
-			byte *b2 = _frameBuffer1 + bx + by * _videoInfo.width;
+			byte *b2 = _frameBuffer1 + bx + by * _width;
 
 			switch (type) {
 			case 0:
@@ -243,7 +238,7 @@
 						}
 						diffMap <<= 1;
 					}
-					b2 += _videoInfo.width;
+					b2 += _width;
 				}
 				break;
 			}
@@ -254,7 +249,7 @@
 					for (int xc = 0; xc < BLOCKW; xc++) {
 						b2[xc] = color;
 					}
-					b2 += _videoInfo.width;
+					b2 += _width;
 				}
 				break;
 			}
@@ -263,7 +258,7 @@
 					for (int xc = 0; xc < BLOCKW; xc++) {
 						b2[xc] = *dat++;
 					}
-					b2 += _videoInfo.width;
+					b2 += _width;
 				}
 				break;
 			}
@@ -275,11 +270,11 @@
 				int my = mbyte & 0x07;
 				if (mbyte & 0x08)
 					my = -my;
-				byte *b1 = _frameBuffer2 + (bx+mx) + (by+my) * _videoInfo.width;
+				byte *b1 = _frameBuffer2 + (bx+mx) + (by+my) * _width;
 				for (int yc = 0; yc < BLOCKH; yc++) {
 					memcpy(b2, b1, BLOCKW);
-					b1 += _videoInfo.width;
-					b2 += _videoInfo.width;
+					b1 += _width;
+					b2 += _width;
 				}
 				break;
 			}
@@ -309,7 +304,7 @@
 
 	memcpy(_frameBuffer2, _frameBuffer1, _frameSize);
 
-	int codeSize = _videoInfo.width * _curHeight / 16;
+	int codeSize = _width * _curHeight / 16;
 	int dataSize, motSize, maskSize;
 
 	dataSize = READ_BE_UINT32(&_decompBuffer[0]);
@@ -322,9 +317,9 @@
 	maskBuf = &motBuf[motSize];
 
 	for (uint32 by = 0; by < _curHeight; by += BLOCKH) {
-		for (uint32 bx = 0; bx < _videoInfo.width; bx += BLOCKW) {
+		for (uint32 bx = 0; bx < _width; bx += BLOCKW) {
 			uint8 type = *codeBuf++;
-			uint8 *b2 = (uint8*)_frameBuffer1 + bx + by * _videoInfo.width;
+			uint8 *b2 = (uint8*)_frameBuffer1 + bx + by * _width;
 
 			switch (type) {
 			case 0:
@@ -341,7 +336,7 @@
 						}
 						diffMap <<= 1;
 					}
-					b2 += _videoInfo.width;
+					b2 += _width;
 				}
 				break;
 			}
@@ -352,7 +347,7 @@
 					for (int xc = 0; xc < BLOCKW; xc++) {
 						b2[xc] = color;
 					}
-					b2 += _videoInfo.width;
+					b2 += _width;
 				}
 				break;
 			}
@@ -361,7 +356,7 @@
 					for (int xc = 0; xc < BLOCKW; xc++) {
 						b2[xc] = *dataBuf++;
 					}
-					b2 += _videoInfo.width;
+					b2 += _width;
 				}
 				break;
 			}
@@ -375,11 +370,11 @@
 				if (mbyte & 0x08)
 					my = -my;
 
-				uint8 *b1 = (uint8*)_frameBuffer2 + (bx+mx) + (by+my) * _videoInfo.width;
+				uint8 *b1 = (uint8*)_frameBuffer2 + (bx+mx) + (by+my) * _width;
 				for (int yc = 0; yc < BLOCKH; yc++) {
 					memcpy(b2, b1, BLOCKW);
-					b1 += _videoInfo.width;
-					b2 += _videoInfo.width;
+					b1 += _width;
+					b2 += _width;
 				}
 				break;
 			}
@@ -391,7 +386,7 @@
 
 				for (int subBlock = 0; subBlock < 4; subBlock++) {
 					int sx = bx + subX[subBlock], sy = by + subY[subBlock];
-					b2 = (uint8*)_frameBuffer1 + sx + sy * _videoInfo.width;
+					b2 = (uint8*)_frameBuffer1 + sx + sy * _width;
 					switch (subMask & 0xC0) {
 					// 00: skip
 					case 0x00:
@@ -403,7 +398,7 @@
 							for (int xc = 0; xc < BLOCKW / 2; xc++) {
 								b2[xc] = subColor;
 							}
-							b2 += _videoInfo.width;
+							b2 += _width;
 						}
 						break;
 					}
@@ -419,11 +414,11 @@
 						if (mbyte & 0x08)
 							my = -my;
 
-						uint8 *b1 = (uint8*)_frameBuffer2 + (sx+mx) + (sy+my) * _videoInfo.width;
+						uint8 *b1 = (uint8*)_frameBuffer2 + (sx+mx) + (sy+my) * _width;
 						for (int yc = 0; yc < BLOCKH / 2; yc++) {
 							memcpy(b2, b1, BLOCKW / 2);
-							b1 += _videoInfo.width;
-							b2 += _videoInfo.width;
+							b1 += _width;
+							b2 += _width;
 						}
 						break;
 					}
@@ -433,7 +428,7 @@
 							for (int xc = 0; xc < BLOCKW / 2; xc++) {
 								b2[xc] = *dataBuf++;
 							}
-							b2 += _videoInfo.width;
+							b2 += _width;
 						}
 						break;
 					}
@@ -458,7 +453,7 @@
 							b2[xc] = pixels[code & 1];
 							code >>= 1;
 						}
-						b2 += _videoInfo.width;
+						b2 += _width;
 					}
 				} else {
 					uint32 code = READ_BE_UINT32(maskBuf);
@@ -468,7 +463,7 @@
 							b2[xc] = pixels[code & 3];
 							code >>= 2;
 						}
-						b2 += _videoInfo.width;
+						b2 += _width;
 					}
 				}
 				break;
@@ -481,18 +476,11 @@
 #endif
 }
 
-bool DXADecoder::decodeNextFrame() {
-	uint32 tag;
-
-	if (_videoInfo.currentFrame == 0)
-		_videoInfo.startTime = g_system->getMillis();
-
-	tag = _fileStream->readUint32BE();
+Surface *DXADecoder::decodeNextFrame() {
+	uint32 tag = _fileStream->readUint32BE();
 	if (tag == MKID_BE('CMAP')) {
-		byte rgb[768];
-
-		_fileStream->read(rgb, ARRAYSIZE(rgb));
-		setPalette(rgb);
+		_fileStream->read(_palette, 256 * 3);
+		_dirtyPalette = true;
 	}
 
 	tag = _fileStream->readUint32BE();
@@ -529,8 +517,8 @@
 
 		if (type == 3) {
 			for (uint32 j = 0; j < _curHeight; ++j) {
-				for (uint32 i = 0; i < _videoInfo.width; ++i) {
-					const int offs = j * _videoInfo.width + i;
+				for (uint32 i = 0; i < _width; ++i) {
+					const int offs = j * _width + i;
 					_frameBuffer1[offs] ^= _frameBuffer2[offs];
 				}
 			}
@@ -540,24 +528,34 @@
 	switch (_scaleMode) {
 	case S_INTERLACED:
 		for (int cy = 0; cy < _curHeight; cy++) {
-			memcpy(&_scaledBuffer[2 * cy * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width);
-			memset(&_scaledBuffer[((2 * cy) + 1) * _videoInfo.width], 0, _videoInfo.width);
+			memcpy(&_scaledBuffer[2 * cy * _width], &_frameBuffer1[cy * _width], _width);
+			memset(&_scaledBuffer[((2 * cy) + 1) * _width], 0, _width);
 		}
-		_videoFrameBuffer = _scaledBuffer;
+		_surface->pixels = _scaledBuffer;
 		break;
 	case S_DOUBLE:
 		for (int cy = 0; cy < _curHeight; cy++) {
-			memcpy(&_scaledBuffer[2 * cy * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width);
-			memcpy(&_scaledBuffer[((2 * cy) + 1) * _videoInfo.width], &_frameBuffer1[cy * _videoInfo.width], _videoInfo.width);
+			memcpy(&_scaledBuffer[2 * cy * _width], &_frameBuffer1[cy * _width], _width);
+			memcpy(&_scaledBuffer[((2 * cy) + 1) * _width], &_frameBuffer1[cy * _width], _width);
 		}
-		_videoFrameBuffer = _scaledBuffer;
+		_surface->pixels = _scaledBuffer;
 		break;
 	case S_NONE:
-		_videoFrameBuffer = _frameBuffer1;
+		_surface->pixels = _frameBuffer1;
 		break;
 	}
 
-	return ++_videoInfo.currentFrame < _videoInfo.frameCount;
+	// Copy in the relevant info to the Surface
+	_surface->w = getWidth();
+	_surface->h = getHeight();
+	_surface->pitch = getWidth();
+
+	_curFrame++;
+
+	if (_curFrame == 0)
+		_startTime = g_system->getMillis();
+
+	return _surface;
 }
 
 } // End of namespace Graphics
Index: graphics/video/flic_decoder.cpp
===================================================================
--- graphics/video/flic_decoder.cpp	(revision 48158)
+++ graphics/video/flic_decoder.cpp	(working copy)
@@ -34,20 +34,17 @@
 FlicDecoder::FlicDecoder() {
 	_paletteChanged = false;
 	_fileStream = 0;
-	_videoFrameBuffer = 0;
-	memset(&_videoInfo, 0, sizeof(_videoInfo));
+	_surface = 0;
 }
 
 FlicDecoder::~FlicDecoder() {
-	closeFile();
+	close();
 }
 
-bool FlicDecoder::loadFile(const char *fileName) {
-	closeFile();
+bool FlicDecoder::load(Common::SeekableReadStream &stream) {
+	close();
 
-	_fileStream = SearchMan.createReadStreamForMember(fileName);
-	if (!_fileStream)
-		return false;
+	_fileStream = &stream;
 
 	/* uint32 frameSize = */ _fileStream->readUint32LE();
 	uint16 frameType = _fileStream->readUint16LE();
@@ -60,9 +57,10 @@
 		return false;
 	}
 
-	_videoInfo.frameCount = _fileStream->readUint16LE();
-	_videoInfo.width = _fileStream->readUint16LE();
-	_videoInfo.height = _fileStream->readUint16LE();
+	
+	_frameCount = _fileStream->readUint16LE();
+	uint16 width = _fileStream->readUint16LE();
+	uint16 height = _fileStream->readUint16LE();
 	uint16 colorDepth = _fileStream->readUint16LE();
 	if (colorDepth != 8) {
 		warning("FlicDecoder::FlicDecoder(): attempted to load an FLC with a palette of color depth %d. Only 8-bit color palettes are supported", frameType);
@@ -70,45 +68,48 @@
 		_fileStream = 0;
 		return false;
 	}
+
 	_fileStream->readUint16LE();	// flags
 	// Note: The normal delay is a 32-bit integer (dword), whereas the overriden delay is a 16-bit integer (word)
 	// frameDelay is the FLIC "speed", in milliseconds. Our frameDelay is calculated in 1/100 ms, so we convert it here
-	_videoInfo.frameDelay = 100 * _fileStream->readUint32LE();
-	_videoInfo.frameRate = 100 * 1000 / _videoInfo.frameDelay;
+	uint32 frameDelay = 100 * _fileStream->readUint32LE();
+	_frameRate = 100 * 1000 / frameDelay;
 
 	_fileStream->seek(80);
 	_offsetFrame1 = _fileStream->readUint32LE();
 	_offsetFrame2 = _fileStream->readUint32LE();
 
-	_videoFrameBuffer = new byte[_videoInfo.width * _videoInfo.height];
+	_surface = new Graphics::Surface();
+	_surface->create(width, height, 1);
 	_palette = (byte *)malloc(3 * 256);
 	memset(_palette, 0, 3 * 256);
 	_paletteChanged = false;
 
 	// Seek to the first frame
-	_videoInfo.currentFrame = 0;
 	_fileStream->seek(_offsetFrame1);
 	return true;
 }
 
-void FlicDecoder::closeFile() {
+void FlicDecoder::close() {
 	if (!_fileStream)
 		return;
 
 	delete _fileStream;
 	_fileStream = 0;
 
-	delete[] _videoFrameBuffer;
-	_videoFrameBuffer = 0;
+	_surface->free();
+	delete _surface;
+	_surface = 0;
 
 	free(_palette);
+	_dirtyRects.clear();
 
-	_dirtyRects.clear();
+	reset();
 }
 
 void FlicDecoder::decodeByteRun(uint8 *data) {
-	byte *ptr = (uint8 *)_videoFrameBuffer;
-	while ((uint32)(ptr - _videoFrameBuffer) < (_videoInfo.width * _videoInfo.height)) {
+	byte *ptr = (byte *)_surface->pixels;
+	while ((int32)(ptr - (byte *)_surface->pixels) < (getWidth() * getHeight())) {
 		int chunks = *data++;
 		while (chunks--) {
 			int count = (int8)*data++;
@@ -125,7 +126,7 @@
 
 	// Redraw
 	_dirtyRects.clear();
-	_dirtyRects.push_back(Common::Rect(0, 0, _videoInfo.width, _videoInfo.height));
+	_dirtyRects.push_back(Common::Rect(0, 0, getWidth(), getHeight()));
 }
 
 #define OP_PACKETCOUNT   0
@@ -152,8 +153,8 @@
 			case OP_UNDEFINED:
 				break;
 			case OP_LASTPIXEL:
-				_videoFrameBuffer[currentLine * _videoInfo.width + _videoInfo.width - 1] = (opcode & 0xFF);
-				_dirtyRects.push_back(Common::Rect(_videoInfo.width - 1, currentLine, _videoInfo.width, currentLine + 1));
+				*((byte *)_surface->pixels + currentLine * getWidth() + getWidth() - 1) = (opcode & 0xFF);
+				_dirtyRects.push_back(Common::Rect(getWidth() - 1, currentLine, getWidth(), currentLine + 1));
 				break;
 			case OP_LINESKIPCOUNT:
 				currentLine += -(int16)opcode;
@@ -168,14 +169,14 @@
 			column += *data++;
 			int rleCount = (int8)*data++;
 			if (rleCount > 0) {
-				memcpy(_videoFrameBuffer + (currentLine * _videoInfo.width) + column, data, rleCount * 2);
+				memcpy((byte *)_surface->pixels + (currentLine * getWidth()) + column, data, rleCount * 2);
 				data += rleCount * 2;
 				_dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1));
 			} else if (rleCount < 0) {
 				rleCount = -rleCount;
 				uint16 dataWord = READ_UINT16(data); data += 2;
 				for (int i = 0; i < rleCount; ++i) {
-					WRITE_UINT16(_videoFrameBuffer + currentLine * _videoInfo.width + column + i * 2, dataWord);
+					WRITE_UINT16((byte *)_surface->pixels + currentLine * getWidth() + column + i * 2, dataWord);
 				}
 				_dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1));
 			} else { // End of cutscene ?
@@ -194,34 +195,40 @@
 #define PSTAMP     18
 #define FRAME_TYPE 0xF1FA
 
-bool FlicDecoder::decodeNextFrame() {
-	if (_videoInfo.currentFrame == 0)
-		_videoInfo.startTime = g_system->getMillis();
-
+Surface *FlicDecoder::decodeNextFrame() {
 	// Read chunk
 	uint32 frameSize = _fileStream->readUint32LE();
 	uint16 frameType = _fileStream->readUint16LE();
 	uint16 chunkCount = 0;
 
 	switch (frameType) {
-	case FRAME_TYPE: {
-		chunkCount = _fileStream->readUint16LE();
-		// Note: The overriden delay is a 16-bit integer (word), whereas the normal delay is a 32-bit integer (dword)
-		// frameDelay is the FLIC "speed", in milliseconds. Our frameDelay is calculated in 1/100 ms, so we convert it here
-		uint16 newFrameDelay = _fileStream->readUint16LE();	// "speed", in milliseconds
-		if (newFrameDelay > 0) {
-			_videoInfo.frameDelay = 100 * newFrameDelay;
-			_videoInfo.frameRate = 100 * 1000 / _videoInfo.frameDelay;
-		}
-		_fileStream->readUint16LE();	// reserved, always 0
-		uint16 newWidth = _fileStream->readUint16LE();
-		uint16 newHeight = _fileStream->readUint16LE();
-		if (newWidth > 0)
-			_videoInfo.width = newWidth;
-		if (newHeight > 0)
-			_videoInfo.height = newHeight;
+	case FRAME_TYPE:
+		{
+			// FIXME: FLIC should be switched over to a variable frame rate VideoDecoder to handle
+			// this properly.
 
-		_videoInfo.currentFrame++;
+			chunkCount = _fileStream->readUint16LE();
+			// Note: The overriden delay is a 16-bit integer (word), whereas the normal delay is a 32-bit integer (dword)
+			// frameDelay is the FLIC "speed", in milliseconds. Our frameDelay is calculated in 1/100 ms, so we convert it here
+			uint16 newFrameDelay = _fileStream->readUint16LE();	// "speed", in milliseconds
+			if (newFrameDelay > 0)
+				_frameRate = 1000 / newFrameDelay;
+
+			_fileStream->readUint16LE();	// reserved, always 0
+			uint16 newWidth = _fileStream->readUint16LE();
+			uint16 newHeight = _fileStream->readUint16LE();
+
+			if ((newWidth != 0) && (newHeight != 0)) {
+				if (newWidth == 0)
+					newWidth = _surface->w;
+				if (newHeight == 0)
+					newHeight = _surface->h;
+
+				_surface->free();
+				delete _surface;
+				_surface = new Graphics::Surface();
+				_surface->create(newWidth, newHeight, 1);
+			}
 		}
 		break;
 	default:
@@ -239,7 +246,6 @@
 			switch (frameType) {
 			case FLI_SETPAL:
 				unpackPalette(data);
-				setPalette(_palette);
 				_paletteChanged = true;
 				break;
 			case FLI_SS2:
@@ -259,19 +265,25 @@
 			delete[] data;
 		}
 	}
+	
+	_curFrame++;
 
+	if (_curFrame == 0)
+		_startTime = g_system->getMillis();
+
 	// If we just processed the ring frame, set the next frame
-	if (_videoInfo.currentFrame == _videoInfo.frameCount + 1) {
-		_videoInfo.currentFrame = 1;
+	if (_curFrame == (int32)_frameCount) {
+		_curFrame = 0;
 		_fileStream->seek(_offsetFrame2);
 	}
 
-	return _videoInfo.currentFrame < _videoInfo.frameCount;
+	return _surface;
 }
 
 void FlicDecoder::reset() {
-	_videoInfo.currentFrame = 0;
-	_fileStream->seek(_offsetFrame1);
+	VideoDecoder::reset();
+	if (_fileStream)
+		_fileStream->seek(_offsetFrame1);
 }
 
 void FlicDecoder::unpackPalette(uint8 *data) {
@@ -303,7 +315,7 @@
 	for (Common::List<Common::Rect>::const_iterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) {
 		for (int y = (*it).top; y < (*it).bottom; ++y) {
 			const int x = (*it).left;
-			memcpy(dst + y * pitch + x, _videoFrameBuffer + y * _videoInfo.width + x, (*it).right - x);
+			memcpy(dst + y * pitch + x, (byte *)_surface->pixels + y * getWidth() + x, (*it).right - x);
 		}
 	}
 	_dirtyRects.clear();
Index: graphics/video/avi_decoder.cpp
===================================================================
--- graphics/video/avi_decoder.cpp	(revision 48158)
+++ graphics/video/avi_decoder.cpp	(working copy)
@@ -23,7 +23,6 @@
  *
  */
 
-#include "common/archive.h"
 #include "common/endian.h"
 #include "common/file.h"
 #include "common/stream.h"
@@ -47,8 +46,10 @@
 	_videoCodec = NULL;
 	_decodedHeader = false;
 	_audStream = NULL;
-	_fileStream = NULL;
+	_stream = NULL;
 	_audHandle = new Audio::SoundHandle();
+	_dirtyPalette = false;
+	_stream = NULL;
 	memset(_palette, 0, sizeof(_palette));
 	memset(&_wvInfo, 0, sizeof(PCMWAVEFORMAT));
 	memset(&_bmInfo, 0, sizeof(BITMAPINFOHEADER));
@@ -58,40 +59,40 @@
 }
 
 AviDecoder::~AviDecoder() {
-	closeFile();
+	close();
 	delete _audHandle;
 }
 
 void AviDecoder::runHandle(uint32 tag) {
-	assert (_fileStream);
-	if (_fileStream->eos())
+	assert (_stream);
+	if (_stream->eos())
 		return;
 
 	debug (3, "Decoding tag %s", tag2str(tag));
 
 	switch (tag) {
 		case ID_RIFF:
-			/*_filesize = */_fileStream->readUint32LE();
-			if (_fileStream->readUint32BE() != ID_AVI)
+			/*_filesize = */_stream->readUint32LE();
+			if (_stream->readUint32BE() != ID_AVI)
 				error("RIFF file is not an AVI video");
 			break;
 		case ID_LIST:
 			handleList();
 			break;
 		case ID_AVIH:
-			_header.size = _fileStream->readUint32LE();
-			_header.microSecondsPerFrame = _fileStream->readUint32LE();
-			_header.maxBytesPerSecond = _fileStream->readUint32LE();
-			_header.padding = _fileStream->readUint32LE();
-			_header.flags = _fileStream->readUint32LE();
-			_header.totalFrames = _fileStream->readUint32LE();
-			_header.initialFrames = _fileStream->readUint32LE();
-			_header.streams = _fileStream->readUint32LE();
-			_header.bufferSize = _fileStream->readUint32LE();
-			_header.width = _fileStream->readUint32LE();
-			_header.height = _fileStream->readUint32LE();
+			_header.size = _stream->readUint32LE();
+			_header.microSecondsPerFrame = _stream->readUint32LE();
+			_header.maxBytesPerSecond = _stream->readUint32LE();
+			_header.padding = _stream->readUint32LE();
+			_header.flags = _stream->readUint32LE();
+			_header.totalFrames = _stream->readUint32LE();
+			_header.initialFrames = _stream->readUint32LE();
+			_header.streams = _stream->readUint32LE();
+			_header.bufferSize = _stream->readUint32LE();
+			_header.width = _stream->readUint32LE();
+			_header.height = _stream->readUint32LE();
 			//Ignore 16 bytes of reserved data
-			_fileStream->skip(16);
+			_stream->skip(16);
 			break;
 		case ID_STRH:
 			handleStreamHeader();
@@ -100,18 +101,18 @@
 		case ID_VEDT: // Unknown, safe to ignore
 		case ID_JUNK: // Alignment bytes, should be ignored
 			{
-			uint32 junkSize = _fileStream->readUint32LE();
-			_fileStream->skip(junkSize + (junkSize & 1)); // Alignment
+			uint32 junkSize = _stream->readUint32LE();
+			_stream->skip(junkSize + (junkSize & 1)); // Alignment
 			} break;
 		case ID_IDX1:
-			_ixInfo.size = _fileStream->readUint32LE();
+			_ixInfo.size = _stream->readUint32LE();
 			_ixInfo.indices = new AVIOLDINDEX::Index[_ixInfo.size / 16];
 			debug (0, "%d Indices", (_ixInfo.size / 16));
 			for (uint32 i = 0; i < (_ixInfo.size / 16); i++) {
-				_ixInfo.indices[i].id = _fileStream->readUint32BE();
-				_ixInfo.indices[i].flags = _fileStream->readUint32LE();
-				_ixInfo.indices[i].offset = _fileStream->readUint32LE();
-				_ixInfo.indices[i].size = _fileStream->readUint32LE();
+				_ixInfo.indices[i].id = _stream->readUint32BE();
+				_ixInfo.indices[i].flags = _stream->readUint32LE();
+				_ixInfo.indices[i].offset = _stream->readUint32LE();
+				_ixInfo.indices[i].size = _stream->readUint32LE();
 				debug (0, "Index %d == Tag \'%s\', Offset = %d, Size = %d", i, tag2str(_ixInfo.indices[i].id), _ixInfo.indices[i].offset, _ixInfo.indices[i].size);
 			}
 			break;
@@ -121,14 +122,14 @@
 }
 
 void AviDecoder::handleList() {
-	uint32 listSize = _fileStream->readUint32LE() - 4; // Subtract away listType's 4 bytes
-	uint32 listType = _fileStream->readUint32BE();
-	uint32 curPos = _fileStream->pos();
+	uint32 listSize = _stream->readUint32LE() - 4; // Subtract away listType's 4 bytes
+	uint32 listType = _stream->readUint32BE();
+	uint32 curPos = _stream->pos();
 
 	debug (0, "Found LIST of type %s", tag2str(listType));
 
-	while ((_fileStream->pos() - curPos) < listSize)
-		runHandle(_fileStream->readUint32BE());
+	while ((_stream->pos() - curPos) < listSize)
+		runHandle(_stream->readUint32BE());
 
 	// We now have all the header data
 	if (listType == ID_HDRL)
@@ -137,104 +138,95 @@
 
 void AviDecoder::handleStreamHeader() {
 	AVIStreamHeader sHeader;
-	sHeader.size = _fileStream->readUint32LE();
-	sHeader.streamType = _fileStream->readUint32BE();
+	sHeader.size = _stream->readUint32LE();
+	sHeader.streamType = _stream->readUint32BE();
 	if (sHeader.streamType == ID_MIDS || sHeader.streamType == ID_TXTS)
 		error ("Unhandled MIDI/Text stream");
-	sHeader.streamHandler = _fileStream->readUint32BE();
-	sHeader.flags = _fileStream->readUint32LE();
-	sHeader.priority = _fileStream->readUint16LE();
-	sHeader.language = _fileStream->readUint16LE();
-	sHeader.initialFrames = _fileStream->readUint32LE();
-	sHeader.scale = _fileStream->readUint32LE();
-	sHeader.rate = _fileStream->readUint32LE();
-	sHeader.start = _fileStream->readUint32LE();
-	sHeader.length = _fileStream->readUint32LE();
-	sHeader.bufferSize = _fileStream->readUint32LE();
-	sHeader.quality = _fileStream->readUint32LE();
-	sHeader.sampleSize = _fileStream->readUint32LE();
-	sHeader.frame.left = _fileStream->readSint16LE();
-	sHeader.frame.top = _fileStream->readSint16LE();
-	sHeader.frame.right = _fileStream->readSint16LE();
-	sHeader.frame.bottom = _fileStream->readSint16LE();
+	sHeader.streamHandler = _stream->readUint32BE();
+	sHeader.flags = _stream->readUint32LE();
+	sHeader.priority = _stream->readUint16LE();
+	sHeader.language = _stream->readUint16LE();
+	sHeader.initialFrames = _stream->readUint32LE();
+	sHeader.scale = _stream->readUint32LE();
+	sHeader.rate = _stream->readUint32LE();
+	sHeader.start = _stream->readUint32LE();
+	sHeader.length = _stream->readUint32LE();
+	sHeader.bufferSize = _stream->readUint32LE();
+	sHeader.quality = _stream->readUint32LE();
+	sHeader.sampleSize = _stream->readUint32LE();
+	sHeader.frame.left = _stream->readSint16LE();
+	sHeader.frame.top = _stream->readSint16LE();
+	sHeader.frame.right = _stream->readSint16LE();
+	sHeader.frame.bottom = _stream->readSint16LE();
 
-	if (_fileStream->readUint32BE() != ID_STRF)
+	if (_stream->readUint32BE() != ID_STRF)
 		error("Could not find STRF tag");
-	/* uint32 strfSize = */ _fileStream->readUint32LE();
+	/* uint32 strfSize = */ _stream->readUint32LE();
 
 	if (sHeader.streamType == ID_VIDS) {
 		_vidsHeader = sHeader;
 
-		_bmInfo.size = _fileStream->readUint32LE();
-		_bmInfo.width = _fileStream->readUint32LE();
+		_bmInfo.size = _stream->readUint32LE();
+		_bmInfo.width = _stream->readUint32LE();
 		assert (_header.width == _bmInfo.width);
-		_bmInfo.height = _fileStream->readUint32LE();
+		_bmInfo.height = _stream->readUint32LE();
 		assert (_header.height == _bmInfo.height);
-		_bmInfo.planes = _fileStream->readUint16LE();
-		_bmInfo.bitCount = _fileStream->readUint16LE();
-		_bmInfo.compression = _fileStream->readUint32BE();
-		_bmInfo.sizeImage = _fileStream->readUint32LE();
-		_bmInfo.xPelsPerMeter = _fileStream->readUint32LE();
-		_bmInfo.yPelsPerMeter = _fileStream->readUint32LE();
-		_bmInfo.clrUsed = _fileStream->readUint32LE();
-		_bmInfo.clrImportant = _fileStream->readUint32LE();
+		_bmInfo.planes = _stream->readUint16LE();
+		_bmInfo.bitCount = _stream->readUint16LE();
+		_bmInfo.compression = _stream->readUint32BE();
+		_bmInfo.sizeImage = _stream->readUint32LE();
+		_bmInfo.xPelsPerMeter = _stream->readUint32LE();
+		_bmInfo.yPelsPerMeter = _stream->readUint32LE();
+		_bmInfo.clrUsed = _stream->readUint32LE();
+		_bmInfo.clrImportant = _stream->readUint32LE();
 
 		if (_bmInfo.bitCount == 8) {
 			if (_bmInfo.clrUsed == 0)
 				_bmInfo.clrUsed = 256;
 
 			for (uint32 i = 0; i < _bmInfo.clrUsed; i++) {
-				_palette[i * 3 + 2] = _fileStream->readByte();
-				_palette[i * 3 + 1] = _fileStream->readByte();
-				_palette[i * 3] = _fileStream->readByte();
-				/*_palette[i * 4 + 3] = */_fileStream->readByte();
+				_palette[i * 3 + 2] = _stream->readByte();
+				_palette[i * 3 + 1] = _stream->readByte();
+				_palette[i * 3] = _stream->readByte();
+				/*_palette[i * 4 + 3] = */_stream->readByte();
 			}
 
-			setPalette(_palette);
+			_dirtyPalette = true;
 		}
 	} else if (sHeader.streamType == ID_AUDS) {
 		_audsHeader = sHeader;
 
-		_wvInfo.tag = _fileStream->readUint16LE();
-		_wvInfo.channels = _fileStream->readUint16LE();
-		_wvInfo.samplesPerSec = _fileStream->readUint32LE();
-		_wvInfo.avgBytesPerSec = _fileStream->readUint32LE();
-		_wvInfo.blockAlign = _fileStream->readUint16LE();
-		_wvInfo.size = _fileStream->readUint16LE();
+		_wvInfo.tag = _stream->readUint16LE();
+		_wvInfo.channels = _stream->readUint16LE();
+		_wvInfo.samplesPerSec = _stream->readUint32LE();
+		_wvInfo.avgBytesPerSec = _stream->readUint32LE();
+		_wvInfo.blockAlign = _stream->readUint16LE();
+		_wvInfo.size = _stream->readUint16LE();
 	}
 }
 
-bool AviDecoder::loadFile(const char *fileName) {
-	closeFile();
+bool AviDecoder::load(Common::SeekableReadStream &stream) {
+	close();
 
-	_fileStream = SearchMan.createReadStreamForMember(fileName);
-	if (!_fileStream)
-		return false;
-
+	_stream = &stream;
 	_decodedHeader = false;
-	// Seek to the first frame
-	_videoInfo.currentFrame = 0;
 
-
 	// Read chunks until we have decoded the header
 	while (!_decodedHeader)
-		runHandle(_fileStream->readUint32BE());
+		runHandle(_stream->readUint32BE());
 
-	_videoFrameBuffer = new byte[_header.width * _header.height];
-	memset(_videoFrameBuffer, 0, _header.width * _header.height);
+	uint32 nextTag = _stream->readUint32BE();
 
-	uint32 nextTag = _fileStream->readUint32BE();
-
 	// Throw out any JUNK section
 	if (nextTag == ID_JUNK) {
 		runHandle(ID_JUNK);
-		nextTag = _fileStream->readUint32BE();
+		nextTag = _stream->readUint32BE();
 	}
 
 	// Ignore the 'movi' LIST
 	if (nextTag == ID_LIST) {
-		_fileStream->readUint32BE(); // Skip size
-		if (_fileStream->readUint32BE() != ID_MOVI)
+		_stream->readUint32BE(); // Skip size
+		if (_stream->readUint32BE() != ID_MOVI)
 			error ("Expected 'movi' LIST");
 	} else
 		error ("Expected 'movi' LIST");
@@ -248,34 +240,24 @@
 		_mixer->playInputStream(_soundType, _audHandle, _audStream);
 
 	debug (0, "Frames = %d, Dimensions = %d x %d", _header.totalFrames, _header.width, _header.height);
-	debug (0, "Frame Rate = %d", getFrameRate());
+	debug (0, "Frame Rate = %d", _vidsHeader.rate / _vidsHeader.scale);
 	if ((_audsHeader.scale != 0) && (_header.flags & AVIF_ISINTERLEAVED))
 		debug (0, "Sound Rate = %d", AUDIO_RATE);
 	debug (0, "Video Codec = \'%s\'", tag2str(_vidsHeader.streamHandler));
 
-	_videoInfo.firstframeOffset = _fileStream->pos();
-	_videoInfo.width = _header.width;
-	_videoInfo.height = _header.height;
-	_videoInfo.frameCount = _header.totalFrames;
-	// Our frameDelay is calculated in 1/100 ms, so we convert it here
-	_videoInfo.frameDelay = _header.microSecondsPerFrame / 10;
-
 	if (!_videoCodec)
 		return false;
 
 	return true;
 }
 
-void AviDecoder::closeFile() {
-	if (!_fileStream)
+void AviDecoder::close() {
+	if (!_stream)
 		return;
 
-	delete _fileStream;
-	_fileStream = 0;
+	delete _stream;
+	_stream = 0;
 
-	delete[] _videoFrameBuffer;
-	_videoFrameBuffer = 0;
-
 	// Deinitialize sound
 	_mixer->stopHandle(*_audHandle);
 	_audStream = 0;
@@ -294,26 +276,38 @@
 	memset(&_vidsHeader, 0, sizeof(AVIStreamHeader));
 	memset(&_audsHeader, 0, sizeof(AVIStreamHeader));
 	memset(&_ixInfo, 0, sizeof(AVIOLDINDEX));
+
+	reset();
 }
 
-Surface *AviDecoder::getNextFrame() {
-	uint32 nextTag = _fileStream->readUint32BE();
+uint32 AviDecoder::getElapsedTime() const {
+	if (_audStream)
+		return _mixer->getSoundElapsedTime(*_audHandle);
 
-	if (_fileStream->eos())
+	return VideoDecoder::getElapsedTime();
+}
+
+Surface *AviDecoder::decodeNextFrame() {
+	uint32 nextTag = _stream->readUint32BE();
+
+	if (_stream->eos())
 		return NULL;
 
+	if (_curFrame == -1)
+		_startTime = g_system->getMillis();
+
 	if (nextTag == ID_LIST) {
 		// A list of audio/video chunks
-		uint32 listSize = _fileStream->readUint32LE() - 4;
-		int32 startPos = _fileStream->pos();
+		uint32 listSize = _stream->readUint32LE() - 4;
+		int32 startPos = _stream->pos();
 
-		if (_fileStream->readUint32BE() != ID_REC)
+		if (_stream->readUint32BE() != ID_REC)
 			error ("Expected 'rec ' LIST");
 
 		// Decode chunks in the list and see if we get a frame
 		Surface *frame = NULL;
-		while (_fileStream->pos() < startPos + (int32)listSize) {
-			Surface *temp = getNextFrame();
+		while (_stream->pos() < startPos + (int32)listSize) {
+			Surface *temp = decodeNextFrame();
 			if (temp)
 				frame = temp;
 		}
@@ -321,9 +315,9 @@
 		return frame;
 	} else if (getStreamType(nextTag) == 'wb') {
 		// Audio Chunk
-		uint32 chunkSize = _fileStream->readUint32LE();
+		uint32 chunkSize = _stream->readUint32LE();
 		byte *data = (byte *)malloc(chunkSize);
-		_fileStream->read(data, chunkSize);
+		_stream->read(data, chunkSize);
 
 		byte flags = 0;
 		if (_audsHeader.sampleSize == 2)
@@ -332,40 +326,40 @@
 			flags |= Audio::FLAG_UNSIGNED;
 
 		_audStream->queueBuffer(data, chunkSize, DisposeAfterUse::YES, flags);
-		_fileStream->skip(chunkSize & 1); // Alignment
+		_stream->skip(chunkSize & 1); // Alignment
 	} else if (getStreamType(nextTag) == 'dc' || getStreamType(nextTag) == 'id' ||
 	           getStreamType(nextTag) == 'AM' || getStreamType(nextTag) == '32') {
 		// Compressed Frame
-		_videoInfo.currentFrame++;
-		uint32 chunkSize = _fileStream->readUint32LE();
+		_curFrame++;
+		uint32 chunkSize = _stream->readUint32LE();
 
 		if (chunkSize == 0) // Keep last frame on screen
 			return NULL;
 
-		Common::SeekableReadStream *frameData = _fileStream->readStream(chunkSize);
+		Common::SeekableReadStream *frameData = _stream->readStream(chunkSize);
 		Graphics::Surface *surface = _videoCodec->decodeImage(frameData);
 		delete frameData;
-		_fileStream->skip(chunkSize & 1); // Alignment
+		_stream->skip(chunkSize & 1); // Alignment
 		return surface;
 	} else if (getStreamType(nextTag) == 'pc') {
 		// Palette Change
-		_fileStream->readUint32LE(); // Chunk size, not needed here
-		byte firstEntry = _fileStream->readByte();
-		uint16 numEntries = _fileStream->readByte();
-		_fileStream->readUint16LE(); // Reserved
+		_stream->readUint32LE(); // Chunk size, not needed here
+		byte firstEntry = _stream->readByte();
+		uint16 numEntries = _stream->readByte();
+		_stream->readUint16LE(); // Reserved
 
 		// 0 entries means all colors are going to be changed
 		if (numEntries == 0)
 			numEntries = 256;
 
 		for (uint16 i = firstEntry; i < numEntries + firstEntry; i++) {
-			_palette[i * 3] = _fileStream->readByte();
-			_palette[i * 3 + 1] = _fileStream->readByte();
-			_palette[i * 3 + 2] = _fileStream->readByte();
-			_fileStream->readByte(); // Flags that don't serve us any purpose
+			_palette[i * 3] = _stream->readByte();
+			_palette[i * 3 + 1] = _stream->readByte();
+			_palette[i * 3 + 2] = _stream->readByte();
+			_stream->readByte(); // Flags that don't serve us any purpose
 		}
 
-		setPalette(_palette);
+		_dirtyPalette = true;
 
 		// No alignment necessary. It's always even.
 	} else if (nextTag == ID_JUNK) {
@@ -373,56 +367,11 @@
 	} else if (nextTag == ID_IDX1) {
 		runHandle(ID_IDX1);
 	} else
-		error ("Tag = \'%s\', %d", tag2str(nextTag), _fileStream->pos());
+		error("Tag = \'%s\', %d", tag2str(nextTag), _stream->pos());
 
 	return NULL;
 }
 
-bool AviDecoder::decodeNextFrame() {
-	if (_videoInfo.currentFrame == 0)
-		_videoInfo.startTime = g_system->getMillis();
-
-	Surface *surface = NULL;
-
-	uint32 curFrame = _videoInfo.currentFrame;
-
-	while (!surface && _videoInfo.currentFrame < _videoInfo.frameCount && !_fileStream->eos())
-		surface = getNextFrame();
-
-	if (curFrame == _videoInfo.currentFrame) {
-		warning("No video frame found");
-		_videoInfo.currentFrame++;
-	}
-
-	if (surface)
-		memcpy(_videoFrameBuffer, surface->pixels, _header.width * _header.height);
-
-	return _videoInfo.currentFrame < _videoInfo.frameCount;
-}
-
-int32 AviDecoder::getAudioLag() {
-	if (!_fileStream)
-		return 0;
-
-	int32 frameDelay = getFrameDelay();
-	int32 videoTime = _videoInfo.currentFrame * frameDelay;
-	int32 audioTime;
-
-	if (!_audStream) {
-		/* No audio.
-		   Calculate the lag by how much time has gone by since the first frame
-		   and how much time *should* have passed.
-		*/
-
-		audioTime = (g_system->getMillis() - _videoInfo.startTime) * 100;
-	} else {
-		const Audio::Timestamp ts = _mixer->getElapsedTime(*_audHandle);
-		audioTime = ts.convertToFramerate(100000).totalNumberOfFrames();
-	}
-
-	return videoTime - audioTime;
-}
-
 Codec *AviDecoder::createCodec() {
 	switch (_vidsHeader.streamHandler) {
 		case ID_CRAM:
@@ -438,11 +387,14 @@
 	return NULL;
 }
 
-Audio::QueuingAudioStream *AviDecoder::createAudioStream() {
+PixelFormat AviDecoder::getPixelFormat() const {
+	assert(_videoCodec);
+	return _videoCodec->getPixelFormat();
+}
 
-	if (_wvInfo.tag == AVI_WAVE_FORMAT_PCM) {
+Audio::QueuingAudioStream *AviDecoder::createAudioStream() {
+	if (_wvInfo.tag == AVI_WAVE_FORMAT_PCM)
 		return Audio::makeQueuingAudioStream(AUDIO_RATE, false);
-	}
 
 	if (_wvInfo.tag != 0) // No sound
 		warning ("Unsupported AVI audio format %d", _wvInfo.tag);
Index: graphics/video/dxa_decoder.h
===================================================================
--- graphics/video/dxa_decoder.h	(revision 48158)
+++ graphics/video/dxa_decoder.h	(working copy)
@@ -26,7 +26,7 @@
 #ifndef GRAPHICS_VIDEO_DXA_PLAYER_H
 #define GRAPHICS_VIDEO_DXA_PLAYER_H
 
-#include "graphics/video/video_player.h"
+#include "graphics/video/video_decoder.h"
 
 namespace Graphics {
 
@@ -38,29 +38,33 @@
  *  - sword1
  *  - sword2
  */
-class DXADecoder : public VideoDecoder {
+class DXADecoder : public FixedRateVideoDecoder {
 public:
 	DXADecoder();
 	virtual ~DXADecoder();
 
-	/**
-	 * Load a DXA encoded video file
-	 * @param filename	the filename to load
-	 */
-	bool loadFile(const char *fileName);
+	bool load(Common::SeekableReadStream &stream);
+	void close();
+	
+	bool isVideoLoaded() const { return _fileStream != 0; }
+	uint16 getWidth() const { return _width; }
+	uint16 getHeight() const { return _height; }
+	uint32 getFrameCount() const { return _frameCount; }
+	Surface *decodeNextFrame();
+	PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); }
+	byte *getPalette() { _dirtyPalette = false; return _palette; }
+	bool hasDirtyPalette() const { return _dirtyPalette; }
 
 	/**
-	 * Close a DXA encoded video file
-	 */
-	void closeFile();
-
-	bool decodeNextFrame();
-
-	/**
 	 * Get the sound chunk tag of the loaded DXA file
 	 */
 	uint32 getSoundTag() { return _soundTag; }
 
+protected:
+	double getFrameRate() const { return _frameRate; }
+
+	Common::SeekableReadStream *_fileStream;
+
 private:
 	void decodeZlib(byte *data, int size, int totalSize);
 	void decode12(int size);
@@ -72,6 +76,10 @@
 		S_DOUBLE
 	};
 
+	Graphics::Surface *_surface;
+	byte _palette[256 * 3];
+	bool _dirtyPalette;
+
 	byte *_frameBuffer1;
 	byte *_frameBuffer2;
 	byte *_scaledBuffer;
@@ -83,6 +91,9 @@
 	uint32 _frameSize;
 	ScaleMode _scaleMode;
 	uint32 _soundTag;
+	uint16 _width, _height;
+	uint32 _frameRate;
+	uint32 _frameCount;
 };
 
 } // End of namespace Graphics
Index: graphics/video/flic_decoder.h
===================================================================
--- graphics/video/flic_decoder.h	(revision 48158)
+++ graphics/video/flic_decoder.h	(working copy)
@@ -23,10 +23,10 @@
  *
  */
 
-#ifndef GRAPHICS_VIDEO_FlicDecoder_H
-#define GRAPHICS_VIDEO_FlicDecoder_H
+#ifndef GRAPHICS_VIDEO_FLIC_DECODER_H
+#define GRAPHICS_VIDEO_FLIC_DECODER_H
 
-#include "graphics/video/video_player.h"
+#include "graphics/video/video_decoder.h"
 #include "common/list.h"
 #include "common/rect.h"
 
@@ -42,35 +42,32 @@
  * Video decoder used in engines:
  *  - tucker
  */
-class FlicDecoder : public VideoDecoder {
+class FlicDecoder : public FixedRateVideoDecoder {
 public:
 	FlicDecoder();
 	virtual ~FlicDecoder();
 
-	/**
-	 * Load a FLIC encoded video file
-	 * @param filename	the filename to load
-	 */
-	bool loadFile(const char *fileName);
+	bool load(Common::SeekableReadStream &stream);
+	void close();
 
-	/**
-	 * Close a FLIC encoded video file
-	 */
-	void closeFile();
+	bool isVideoLoaded() const { return _fileStream != 0; }
+	uint16 getWidth() const { return _surface->w; }
+	uint16 getHeight() const { return _surface->h; }
+	uint32 getFrameCount() const { return _frameCount; }
+	Surface *decodeNextFrame();
+	PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); }
 
-	/**
-	 * Decode the next frame
-	 */
-	bool decodeNextFrame();
-
 	const Common::List<Common::Rect> *getDirtyRects() const { return &_dirtyRects; }
 	void clearDirtyRects() { _dirtyRects.clear(); }
 	void copyDirtyRectsToBuffer(uint8 *dst, uint pitch);
 
-	const byte *getPalette() { _paletteChanged = false; return _palette; }
-	bool paletteChanged() { return _paletteChanged; }
+	byte *getPalette() { _paletteChanged = false; return _palette; }
+	bool hasDirtyPalette() { return _paletteChanged; }
 	void reset();
 
+protected:
+	double getFrameRate() const { return _frameRate; }
+
 private:
 	uint16 _offsetFrame1;
 	uint16 _offsetFrame2;
@@ -81,8 +78,12 @@
 	void decodeDeltaFLC(uint8 *data);
 	void unpackPalette(uint8 *mem);
 
+	Common::SeekableReadStream *_fileStream;
+	Surface *_surface;
+	uint32 _frameCount;
+	uint32 _frameRate;
+
 	Common::List<Common::Rect> _dirtyRects;
-
 };
 
 } // End of namespace Graphics
Index: graphics/video/avi_decoder.h
===================================================================
--- graphics/video/avi_decoder.h	(revision 48158)
+++ graphics/video/avi_decoder.h	(working copy)
@@ -26,7 +26,7 @@
 #ifndef GRAPHICS_AVI_PLAYER_H
 #define GRAPHICS_AVI_PLAYER_H
 
-#include "graphics/video/video_player.h"
+#include "graphics/video/video_decoder.h"
 #include "graphics/video/codecs/codec.h"
 #include "sound/audiostream.h"
 #include "sound/mixer.h"
@@ -172,26 +172,27 @@
 	Common::Rect frame;
 };
 
-class AviDecoder : public VideoDecoder {
+class AviDecoder : public FixedRateVideoDecoder {
 public:
 	AviDecoder(Audio::Mixer *mixer,
 			Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType);
 	virtual ~AviDecoder();
 
-	/**
-	 * Load an AVI video file
-	 * @param filename	the filename to load
-	 */
-	bool loadFile(const char *fileName);
+	bool load(Common::SeekableReadStream &stream);
+	void close();
 
-	/**
-	 * Close an AVI video file
-	 */
-	void closeFile();
+	bool isVideoLoaded() const { return _stream != 0; }
+	uint16 getWidth() const { return _header.width; }
+	uint16 getHeight() const { return _header.height; }
+	uint32 getFrameCount() const { return _header.totalFrames; }
+	uint32 getElapsedTime() const;
+	Surface *decodeNextFrame();
+	PixelFormat getPixelFormat() const;
+	byte *getPalette() { _dirtyPalette = false; return _palette; }
+	bool hasDirtyPalette() const { return _dirtyPalette; }
 
-	bool decodeNextFrame();
-	int32 getAudioLag();
-	int32 getFrameRate() { return _vidsHeader.rate / _vidsHeader.scale; }
+protected:
+	double getFrameRate() const { return (double)_vidsHeader.rate / _vidsHeader.scale; }
 
 private:
 	Audio::Mixer *_mixer;
@@ -202,7 +203,9 @@
 	AVIStreamHeader _vidsHeader;
 	AVIStreamHeader _audsHeader;
 	byte _palette[3 * 256];
+	bool _dirtyPalette;
 
+	Common::SeekableReadStream *_stream;
 	bool _decodedHeader;
 
 	Codec *_videoCodec;
@@ -223,8 +226,6 @@
 	static byte char2num(char c);
 	static byte getStreamNum(uint32 tag);
 	static uint16 getStreamType(uint32 tag);
-
-	Surface *getNextFrame();
 };
 
 } // End of namespace Graphics
Index: graphics/video/codecs/codec.h
===================================================================
--- graphics/video/codecs/codec.h	(revision 48158)
+++ graphics/video/codecs/codec.h	(working copy)
@@ -28,6 +28,7 @@
 
 #include "common/stream.h"
 #include "graphics/surface.h"
+#include "graphics/pixelformat.h"
 
 namespace Graphics {
 
@@ -35,7 +36,9 @@
 public:
 	Codec() {}
 	virtual ~Codec() {}
+
 	virtual Surface *decodeImage(Common::SeekableReadStream *stream) = 0;
+	virtual PixelFormat getPixelFormat() const = 0;
 };
 
 } // End of namespace Graphics
Index: graphics/video/codecs/msrle.h
===================================================================
--- graphics/video/codecs/msrle.h	(revision 48158)
+++ graphics/video/codecs/msrle.h	(working copy)
@@ -36,6 +36,7 @@
 	~MSRLEDecoder();
 
 	Surface *decodeImage(Common::SeekableReadStream *stream);
+	PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); }
 
 private:
 	byte _bitsPerPixel;
Index: graphics/video/codecs/msvideo1.cpp
===================================================================
--- graphics/video/codecs/msvideo1.cpp	(revision 48158)
+++ graphics/video/codecs/msvideo1.cpp	(working copy)
@@ -125,7 +125,7 @@
     }
 }
 
-Graphics::Surface *MSVideo1Decoder::decodeImage(Common::SeekableReadStream *stream) {
+Surface *MSVideo1Decoder::decodeImage(Common::SeekableReadStream *stream) {
 	if (_bitsPerPixel == 8)
 		decode8(stream);
 	else {
Index: graphics/video/codecs/msvideo1.h
===================================================================
--- graphics/video/codecs/msvideo1.h	(revision 48158)
+++ graphics/video/codecs/msvideo1.h	(working copy)
@@ -36,6 +36,7 @@
 	~MSVideo1Decoder();
 
 	Surface *decodeImage(Common::SeekableReadStream *stream);
+	PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); }
 
 private:
 	byte _bitsPerPixel;
Index: graphics/video/smk_decoder.cpp
===================================================================
--- graphics/video/smk_decoder.cpp	(revision 48158)
+++ graphics/video/smk_decoder.cpp	(working copy)
@@ -351,52 +351,28 @@
 
 SmackerDecoder::SmackerDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType)
 	: _audioStarted(false), _audioStream(0), _mixer(mixer), _soundType(soundType) {
+	_surface = 0;
+	_fileStream = 0;
+	_dirtyPalette = false;
 }
 
 SmackerDecoder::~SmackerDecoder() {
-	closeFile();
+	close();
 }
 
-int SmackerDecoder::getHeight() {
-	if (!_fileStream)
-		return 0;
-	return (_header.flags ? 2 : 1) * _videoInfo.height;
-}
+uint32 SmackerDecoder::getElapsedTime() const {
+	if (_audioStream)
+		return _mixer->getSoundElapsedTime(_audioHandle);
 
-int32 SmackerDecoder::getAudioLag() {
-	if (!_fileStream)
-		return 0;
-
-	int32 frameDelay = getFrameDelay();
-	int32 videoTime = _videoInfo.currentFrame * frameDelay;
-	int32 audioTime;
-
-	if (!_audioStream) {
-		/* No audio.
-		   Calculate the lag by how much time has gone by since the first frame
-		   and how much time *should* have passed.
-		*/
-
-		audioTime = (g_system->getMillis() - _videoInfo.startTime) * 100;
-	} else {
-		const Audio::Timestamp ts = _mixer->getElapsedTime(_audioHandle);
-		audioTime = ts.convertToFramerate(100000).totalNumberOfFrames();
-	}
-
-	return videoTime - audioTime;
+	return VideoDecoder::getElapsedTime();
 }
 
-bool SmackerDecoder::loadFile(const char *fileName) {
-	int32 frameRate;
+bool SmackerDecoder::load(Common::SeekableReadStream &stream) {
+	close();
 
-	closeFile();
+	_fileStream = &stream;
 
-	_fileStream = SearchMan.createReadStreamForMember(fileName);
-	if (!_fileStream)
-		return false;
-
 	// Seek to the first frame
-	_videoInfo.currentFrame = 0;
 	_header.signature = _fileStream->readUint32BE();
 
 	// No BINK support available
@@ -408,21 +384,17 @@
 
 	assert(_header.signature == MKID_BE('SMK2') || _header.signature == MKID_BE('SMK4'));
 
-	_videoInfo.width = _fileStream->readUint32LE();
-	_videoInfo.height = _fileStream->readUint32LE();
-	_videoInfo.frameCount = _fileStream->readUint32LE();
-	frameRate = _fileStream->readSint32LE();
+	uint32 width = _fileStream->readUint32LE();
+	uint32 height = _fileStream->readUint32LE();
+	_frameCount = _fileStream->readUint32LE();
+	int32 frameRate = _fileStream->readSint32LE();
 
-	if (frameRate > 0) {
-		_videoInfo.frameRate = 1000 / frameRate;
-		_videoInfo.frameDelay = frameRate * 100;
-	} else if (frameRate < 0) {
-		_videoInfo.frameRate = 100000 / (-frameRate);
-		_videoInfo.frameDelay = -frameRate;
-	} else {
-		_videoInfo.frameRate = 10;
-		_videoInfo.frameDelay = 10000;
-	}
+	if (frameRate > 0)
+		_frameRate = 1000 / frameRate;
+	else if (frameRate < 0)
+		_frameRate = 100000 / (-frameRate);
+	else
+		_frameRate = 10;
 
 	// Flags are determined by which bit is set, which can be one of the following:
 	// 0 - set to 1 if file contains a ring frame.
@@ -447,7 +419,6 @@
 	_header.fullSize = _fileStream->readUint32LE();
 	_header.typeSize = _fileStream->readUint32LE();
 
-	uint32 audioInfo;
 	for (i = 0; i < 7; ++i) {
 		// AudioRate - Frequency and format information for each sound track, up to 7 audio tracks.
 		// The 32 constituent bits have the following meaning:
@@ -458,7 +429,7 @@
 		// * bits 27-26 - if both set to zero - use v2 sound decompression
 		// * bits 25-24 - unused
 		// * bits 23-0 - audio sample rate
-		audioInfo = _fileStream->readUint32LE();
+		uint32 audioInfo = _fileStream->readUint32LE();
 		_header.audioInfo[i].isCompressed = audioInfo & 0x80000000;
 		_header.audioInfo[i].hasAudio = audioInfo & 0x40000000;
 		_header.audioInfo[i].is16Bits = audioInfo & 0x20000000;
@@ -467,19 +438,18 @@
 												!(audioInfo & 0x4000000);
 		_header.audioInfo[i].sampleRate = audioInfo & 0xFFFFFF;
 
-		if (_header.audioInfo[i].hasAudio && i == 0) {
+		if (_header.audioInfo[i].hasAudio && i == 0)
 			_audioStream = Audio::makeQueuingAudioStream(_header.audioInfo[0].sampleRate, _header.audioInfo[0].isStereo);
-		}
 	}
 
 	_header.dummy = _fileStream->readUint32LE();
 
-	_frameSizes = (uint32 *)malloc(_videoInfo.frameCount * sizeof(uint32));
-	for (i = 0; i < _videoInfo.frameCount; ++i)
+	_frameSizes = new uint32[_frameCount];
+	for (i = 0; i < _frameCount; ++i)
 		_frameSizes[i] = _fileStream->readUint32LE();
 
-	_frameTypes = (byte *)malloc(_videoInfo.frameCount);
-	for (i = 0; i < _videoInfo.frameCount; ++i)
+	_frameTypes = new byte[_frameCount];
+	for (i = 0; i < _frameCount; ++i)
 		_frameTypes[i] = _fileStream->readByte();
 
 	byte *huffmanTrees = new byte[_header.treesSize];
@@ -494,17 +464,14 @@
 
 	delete[] huffmanTrees;
 
-	_videoFrameBuffer = (byte *)malloc(2 * _videoInfo.width * _videoInfo.height);
-	memset(_videoFrameBuffer, 0, 2 * _videoInfo.width * _videoInfo.height);
+	_surface = new Graphics::Surface();
+	_surface->create(width, height * (_header.flags ? 2 : 1), 1);
 	_palette = (byte *)malloc(3 * 256);
 	memset(_palette, 0, 3 * 256);
-
-	_videoInfo.firstframeOffset = _fileStream->pos();
-
 	return true;
 }
 
-void SmackerDecoder::closeFile() {
+void SmackerDecoder::close() {
 	if (!_fileStream)
 		return;
 
@@ -517,38 +484,42 @@
 	delete _fileStream;
 	_fileStream = 0;
 
+	_surface->free();
+	delete _surface;
+	_surface = 0;
+
 	delete _MMapTree;
 	delete _MClrTree;
 	delete _FullTree;
 	delete _TypeTree;
 
-	free(_frameSizes);
-	free(_frameTypes);
-	free(_videoFrameBuffer);
+	delete[] _frameSizes;
+	delete[] _frameTypes;
 	free(_palette);
+
+	reset();
 }
 
-bool SmackerDecoder::decodeNextFrame() {
+Surface *SmackerDecoder::decodeNextFrame() {
 	uint i;
 	uint32 chunkSize = 0;
 	uint32 dataSizeUnpacked = 0;
 
 	uint32 startPos = _fileStream->pos();
 
-	if (_videoInfo.currentFrame == 0)
-		_videoInfo.startTime = g_system->getMillis();
+	_curFrame++;
 
 	// Check if we got a frame with palette data, and
 	// call back the virtual setPalette function to set
 	// the current palette
-	if (_frameTypes[_videoInfo.currentFrame] & 1) {
+	if (_frameTypes[_curFrame] & 1) {
 		unpackPalette();
-		setPalette(_palette);
+		_dirtyPalette = true;
 	}
 
 	// Load audio tracks
 	for (i = 0; i < 7; ++i) {
-		if (!(_frameTypes[_videoInfo.currentFrame] & (2 << i)))
+		if (!(_frameTypes[_curFrame] & (2 << i)))
 			continue;
 
 		chunkSize = _fileStream->readUint32LE();
@@ -596,10 +567,10 @@
 		}
 	}
 
-	uint32 frameSize = _frameSizes[_videoInfo.currentFrame] & ~3;
+	uint32 frameSize = _frameSizes[_curFrame] & ~3;
 
 	if (_fileStream->pos() - startPos > frameSize)
-		exit(1);
+		error("Smacker actual frame size exceeds recorded frame size");
 
 	uint32 frameDataSize = frameSize - (_fileStream->pos() - startPos);
 
@@ -613,9 +584,9 @@
 	_FullTree->reset();
 	_TypeTree->reset();
 
-	uint bw = _videoInfo.width / 4;
-	uint bh = _videoInfo.height / 4;
-	uint stride = _videoInfo.width;
+	uint bw = getWidth() / 4;
+	uint bh = getHeight() / (_header.flags ? 2 : 1) / 4;
+	uint stride = getWidth();
 	uint block = 0, blocks = bw*bh;
 
 	uint doubleY = _header.flags ? 2 : 1;
@@ -634,7 +605,7 @@
 			while (run-- && block < blocks) {
 				clr = _MClrTree->getCode(bs);
 				map = _MMapTree->getCode(bs);
-				out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
+				out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
 				hi = clr >> 8;
 				lo = clr & 0xff;
 				for (i = 0; i < 4; i++) {
@@ -667,7 +638,7 @@
 			}
 
 			while (run-- && block < blocks) {
-				out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
+				out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
 				switch (mode) {
 					case 0:
 						for (i = 0; i < 4; ++i) {
@@ -733,7 +704,7 @@
 			uint32 col;
 			mode = type >> 8;
 			while (run-- && block < blocks) {
-				out = _videoFrameBuffer + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
+				out = (byte *)_surface->pixels + (block / bw) * (stride * 4 * doubleY) + (block % bw) * 4;
 				col = mode * 0x01010101;
 				for (i = 0; i < 4 * doubleY; ++i) {
 					out[0] = out[1] = out[2] = out[3] = col;
@@ -749,7 +720,10 @@
 
 	free(_frameData);
 
-	return ++_videoInfo.currentFrame < _videoInfo.frameCount;
+	if (_curFrame == 0)
+		_startTime = g_system->getMillis();
+
+	return _surface;
 }
 
 void SmackerDecoder::queueCompressedBuffer(byte *buffer, uint32 bufferSize,
@@ -804,15 +778,12 @@
 		// If the sample is stereo, the data is stored for the left and right channel, respectively
 		// (the exact opposite to the base values)
 		if (!is16Bits) {
-
 			for (int k = 0; k < (isStereo ? 2 : 1); k++) {
 				bases[k] += (int8) ((int16) audioTrees[k]->getCode(audioBS));
 				*curPointer++ = CLIP<int>(bases[k], 0, 255) ^ 0x80;
 				curPos++;
 			}
-
 		} else {
-
 			for (int k = 0; k < (isStereo ? 2 : 1); k++) {
 				bases[k] += (int16) (audioTrees[k * 2]->getCode(audioBS) |
 				                    (audioTrees[k * 2 + 1]->getCode(audioBS) << 8));
@@ -885,7 +856,6 @@
 	}
 
 	_fileStream->seek(startPos + len);
-
 	free(chunk);
 }
 
Index: graphics/video/video_player.cpp
===================================================================
--- graphics/video/video_player.cpp	(revision 48158)
+++ graphics/video/video_player.cpp	(working copy)
@@ -1,242 +0,0 @@
-/* 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$
- *
- */
-
-#include "common/archive.h"
-#include "common/debug.h"
-#include "common/events.h"
-#include "common/system.h"
-#include "common/util.h"
-#include "common/array.h"
-#include "common/endian.h"
-
-#include "graphics/video/video_player.h"
-#include "graphics/surface.h"
-
-namespace Graphics {
-
-VideoDecoder::VideoDecoder() : _fileStream(0) {
-	_curFrameBlack = 0;
-	_curFrameWhite = 255;
-}
-
-VideoDecoder::~VideoDecoder() {
-}
-
-int VideoDecoder::getWidth() {
-	if (!_fileStream)
-		return 0;
-	return _videoInfo.width;
-}
-
-int VideoDecoder::getHeight() {
-	if (!_fileStream)
-		return 0;
-	return _videoInfo.height;
-}
-
-int32 VideoDecoder::getCurFrame() {
-	if (!_fileStream)
-		return -1;
-	return _videoInfo.currentFrame;
-}
-
-int32 VideoDecoder::getFrameCount() {
-	if (!_fileStream)
-		return 0;
-	return _videoInfo.frameCount;
-}
-
-int32 VideoDecoder::getFrameRate() {
-	if (!_fileStream)
-		return 0;
-	return _videoInfo.frameRate;
-}
-
-int32 VideoDecoder::getFrameDelay() {
-	if (!_fileStream)
-		return 0;
-	return _videoInfo.frameDelay;
-}
-
-int32 VideoDecoder::getAudioLag() {
-	if (!_fileStream)
-		return 0;
-
-	/* No audio.
-	   Calculate the lag by how much time has gone by since the first frame
-	   and how much time *should* have passed.
-	*/
-	int32 audioTime = (g_system->getMillis() - _videoInfo.startTime) * 100;
-	int32 videoTime = _videoInfo.currentFrame * getFrameDelay();
-
-	return videoTime - audioTime;
-}
-
-uint32 VideoDecoder::getFrameWaitTime() {
-	int32 waitTime = (getFrameDelay() + getAudioLag()) / 100;
-
-	if (waitTime < 0)
-		return 0;
-
-	return waitTime;
-}
-
-void VideoDecoder::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) {
-	uint h = getHeight();
-	uint w = getWidth();
-
-	byte *src = _videoFrameBuffer;
-	dst += y * pitch + x;
-
-	do {
-		memcpy(dst, src, w);
-		dst += pitch;
-		src += w;
-	} while (--h);
-}
-
-void VideoDecoder::setPalette(byte *pal) {
-	byte videoPalette[256 * 4];
-
-	uint32 maxWeight = 0;
-	uint32 minWeight = 0xFFFFFFFF;
-	uint32 weight = 0;
-	byte r, g, b;
-
-	for (int i = 0; i < 256; i++) {
-		videoPalette[i * 4 + 0] = *pal++;
-		videoPalette[i * 4 + 1] = *pal++;
-		videoPalette[i * 4 + 2] = *pal++;
-		videoPalette[i * 4 + 3] = 0;
-
-		// Try and find the white and black colors for the current palette
-		r = videoPalette[i * 4 + 0];
-		g = videoPalette[i * 4 + 1];
-		b = videoPalette[i * 4 + 2];
-
-		weight = 3 * r * r + 6 * g * g + 2 * b * b;
-
-		if (weight >= maxWeight) {
-			_curFrameWhite = i;
-			maxWeight = weight;
-		}
-
-		if (weight <= minWeight) {
-			_curFrameBlack = i;
-			minWeight = i;
-		}
-	}
-
-	g_system->setPalette(videoPalette, 0, 256);
-}
-
-
-/*
- *  VideoPlayer
- */
-
-void VideoPlayer::processVideoEvents(Common::List<Common::Event> &stopEvents) {
-	Common::Event curEvent;
-	Common::EventManager *eventMan = g_system->getEventManager();
-
-	// Process events, and skip video if esc is pressed
-	while (eventMan->pollEvent(curEvent)) {
-		if (curEvent.type == Common::EVENT_RTL || curEvent.type == Common::EVENT_QUIT) {
-			_skipVideo = true;
-		}
-
-		for (Common::List<Common::Event>::const_iterator iter = stopEvents.begin(); iter != stopEvents.end(); ++iter) {
-			if (curEvent.type == iter->type) {
-				if (iter->type == Common::EVENT_KEYDOWN || iter->type == Common::EVENT_KEYUP) {
-					if (curEvent.kbd.keycode == iter->kbd.keycode) {
-						_skipVideo = true;
-						break;
-					}
-				} else {
-					_skipVideo = true;
-					break;
-				}
-			}
-		}
-	}
-}
-
-bool VideoPlayer::playVideo(Common::List<Common::Event> &stopEvents) {
-	_skipVideo = false;
-	debug(0, "Playing video");
-
-	g_system->fillScreen(0);
-
-	int frameX = (g_system->getWidth() - _decoder->getWidth()) / 2;
-	int frameY = (g_system->getHeight() - _decoder->getHeight()) / 2;
-
-	while (_decoder->getCurFrame() < _decoder->getFrameCount() && !_skipVideo) {
-		processVideoEvents(stopEvents);
-
-		uint32 startTime = 0;
-		_decoder->decodeNextFrame();
-
-		Graphics::Surface *screen = g_system->lockScreen();
-		_decoder->copyFrameToBuffer((byte *)screen->pixels, frameX, frameY, g_system->getWidth());
-		performPostProcessing((byte *)screen->pixels);
-		g_system->unlockScreen();
-
-		uint32 waitTime = _decoder->getFrameWaitTime();
-
-		if (!waitTime) {
-			warning("dropped frame %i", _decoder->getCurFrame());
-			continue;
-		}
-
-		// Update the screen
-		g_system->updateScreen();
-
-		startTime = g_system->getMillis();
-
-		// Wait before showing the next frame
-		while (g_system->getMillis() < startTime + waitTime && !_skipVideo) {
-			processVideoEvents(stopEvents);
-			g_system->delayMillis(10);
-		}
-	}
-
-	return !_skipVideo;
-}
-
-bool VideoPlayer::playVideo() {
-	Common::Event stopEvent;
-	Common::List<Common::Event> stopEvents;
-	stopEvents.clear();
-	stopEvent.type = Common::EVENT_KEYDOWN;
-	stopEvent.kbd = Common::KEYCODE_ESCAPE;
-	stopEvents.push_back(stopEvent);
-
-	return playVideo(stopEvents);
-}
-
-void VideoPlayer::performPostProcessing(byte *screen) {
-}
-
-} // End of namespace Graphics
Index: graphics/video/smk_decoder.h
===================================================================
--- graphics/video/smk_decoder.h	(revision 48158)
+++ graphics/video/smk_decoder.h	(working copy)
@@ -26,7 +26,7 @@
 #ifndef GRAPHICS_VIDEO_SMK_PLAYER_H
 #define GRAPHICS_VIDEO_SMK_PLAYER_H
 
-#include "graphics/video/video_player.h"
+#include "graphics/video/video_decoder.h"
 #include "sound/mixer.h"
 
 namespace Audio {
@@ -51,28 +51,29 @@
  *  - sword1
  *  - sword2
  */
-class SmackerDecoder : public VideoDecoder {
+class SmackerDecoder : public FixedRateVideoDecoder {
 public:
 	SmackerDecoder(Audio::Mixer *mixer,
 			Audio::Mixer::SoundType soundType = Audio::Mixer::kSFXSoundType);
 	virtual ~SmackerDecoder();
 
-	int getHeight();
-	int32 getAudioLag();
+	bool load(Common::SeekableReadStream &stream);
+	void close();
 
-	/**
-	 * Load an SMK encoded video file
-	 * @param filename	the filename to load
-	 */
-	bool loadFile(const char *filename);
+	bool isVideoLoaded() const { return _fileStream != 0; }
+	uint16 getWidth() const { return _surface->w; }
+	uint16 getHeight() const { return _surface->h; }
+	uint32 getFrameCount() const { return _frameCount; }
+	uint32 getElapsedTime() const;
+	Surface *decodeNextFrame();
+	PixelFormat getPixelFormat() const { return PixelFormat::createFormatCLUT8(); }
+	byte *getPalette() { _dirtyPalette = false; return _palette; }
+	bool hasDirtyPalette() const { return _dirtyPalette; }
 
-	/**
-	 * Close an SMK encoded video file
-	 */
-	void closeFile();
+protected:
+	double getFrameRate() const { return _frameRate; }
+	Common::SeekableReadStream *_fileStream;
 
-	bool decodeNextFrame();
-
 private:
 	void unpackPalette();
 	// Possible runs of blocks
@@ -111,7 +112,12 @@
 	byte *_frameData;
 	// The RGB palette
 	byte *_palette;
+	bool _dirtyPalette;
 
+	uint32 _frameRate;
+	uint32 _frameCount;
+	Surface *_surface;
+
 	Audio::Mixer::SoundType _soundType;
 	Audio::Mixer *_mixer;
 	bool _audioStarted;
Index: graphics/video/video_player.h
===================================================================
--- graphics/video/video_player.h	(revision 48158)
+++ graphics/video/video_player.h	(working copy)
@@ -1,216 +0,0 @@
-/* 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$
- *
- */
-
-#ifndef GRAPHICS_VIDEO_PLAYER_H
-#define GRAPHICS_VIDEO_PLAYER_H
-
-#include "common/events.h"
-#include "common/list.h"
-#include "common/stream.h"
-
-namespace Common {
-	class SeekableReadStream;
-}
-
-namespace Graphics {
-
-/**
- * Implementation of a generic video decoder
- */
-class VideoDecoder {
-public:
-	VideoDecoder();
-	virtual ~VideoDecoder();
-
-	/**
-	 * Returns the width of the video
-	 * @return the width of the video
-	 */
-	virtual int getWidth();
-
-	/**
-	 * Returns the height of the video
-	 * @return the height of the video
-	 */
-	virtual int getHeight();
-
-	/**
-	 * Returns the current frame number of the video
-	 * @return the current frame number of the video
-	 */
-	virtual int32 getCurFrame();
-
-	/**
-	 * Returns the amount of frames in the video
-	 * @return the amount of frames in the video
-	 */
-	virtual int32 getFrameCount();
-
-	/**
-	 * Returns the frame rate of the video
-	 * @return the frame rate of the video
-	 */
-	virtual int32 getFrameRate();
-
-	/**
-	 * Returns the time to wait for each frame in 1/100 ms (to avoid rounding errors)
-	 * @return the time to wait for each frame in 1/100 ms (to avoid rounding errors)
-	 */
-	virtual int32 getFrameDelay();
-
-	/**
-	 * Returns the current A/V lag in 1/100 ms (to avoid rounding errors)
-	 * If > 0, audio lags behind
-	 * If < 0, video lags behind
-	 * @return the current A/V lag in 1/100 ms (to avoid rounding errors)
-	 */
-	virtual int32 getAudioLag();
-
-	/**
-	 * Returns the time to wait until the next frame in ms, minding any lag
-	 * @return the time to wait until the next frame in ms
-	 */
-	virtual uint32 getFrameWaitTime();
-
-	/**
-	 * Load a video file
-	 * @param filename	the filename to load
-	 */
-	virtual bool loadFile(const char *filename) = 0;
-
-	/**
-	 * Close a video file
-	 */
-	virtual void closeFile() = 0;
-
-	/**
-	 * Returns if a video file is loaded or not
-	 */
-	bool isVideoLoaded() { return (_fileStream != NULL); }
-
-	/**
-	 * Set RGB palette, based on current frame
-	 * @param pal		the RGB palette data
-	 */
-	virtual void setPalette(byte *pal);
-
-	/**
-	 * Gets the value of the pixel at the specified x and y coordinates
-	 * Note: This method assumes that the video's pitch equals its width, and that
-	 * the video has an 8bpp palette
-	 * @param x	the x coordinate of the pixel
-	 * @param y	the y coordinate of the pixel
-	 */
-	byte getPixel(int x, int y) {
-		return *(_videoFrameBuffer + y * _videoInfo.width + x * 1);
-	}
-
-	/**
-	 * Gets the value of the pixel at the specified offset
-	 * @param offset	the offset of the pixel in the video buffer
-	 */
-	byte getPixel(int offset) { return getPixel(offset, 0); }
-
-	/**
-	 * Return the black palette color for the current frame
-	 */
-	byte getBlack() { return _curFrameBlack; }
-
-	/**
-	 * Return the white palette color for the current frame
-	 */
-	byte getWhite() { return _curFrameWhite; }
-
-	/**
-	 * Copy current frame into the specified position of the destination
-	 * buffer.
-	 * @param dst		the buffer
-	 * @param x		the x position of the buffer
-	 * @param y		the y position of the buffer
-	 * @param pitch		the pitch of buffer
-	 */
-	void copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch);
-
-	/**
-	 * Decode the next frame to _videoFrameBuffer
-	 */
-	virtual bool decodeNextFrame() = 0;
-
-protected:
-	struct {
-		uint32 width;
-		uint32 height;
-		uint32 frameCount;
-		int32 frameRate;
-		int32 frameDelay;		// 1/100 ms (to avoid rounding errors)
-		uint32 firstframeOffset;
-		uint32 currentFrame;
-		uint32 startTime;
-	} _videoInfo;
-
-	byte _curFrameBlack, _curFrameWhite;
-
-	Common::SeekableReadStream *_fileStream;
-	byte *_videoFrameBuffer;
-};
-
-class VideoPlayer {
-public:
-	VideoPlayer(VideoDecoder* decoder) : _skipVideo(false), _decoder(decoder)
-		{ }
-	virtual ~VideoPlayer() { }
-	/**
-	 * A default implementation of a video player
-	 * Plays a non-interactive full screen video till it's stopped by a
-	 * specific event
-	 * @param filename		the name of the file to play
-	 * @param stopEvents	a list of events that can stop the video
-	 *
-	 * Returns true if the video was played to the end, false if skipped
-	 */
-	bool playVideo(Common::List<Common::Event> &stopEvents);
-
-	/**
-	 * Provides the same functionality as the video player, and it adds the
-	 * event of skipping the video with the escape key by default
-	 */
-	bool playVideo();
-
-protected:
-	/**
-	 * Perform postprocessing once the frame data is copied to the screen,
-	 * right before the frame is drawn. Called by playVideo()
-	 */
-	virtual void performPostProcessing(byte *screen);
-
-	bool _skipVideo;
-	VideoDecoder* _decoder;
-
-	void processVideoEvents(Common::List<Common::Event> &stopEvents);
-};
-
-} // End of namespace Graphics
-
-#endif
Index: graphics/video/video_decoder.cpp
===================================================================
--- graphics/video/video_decoder.cpp	(revision 48157)
+++ graphics/video/video_decoder.cpp	(working copy)
@@ -23,220 +23,77 @@
  *
  */
 
-#include "common/archive.h"
-#include "common/debug.h"
-#include "common/events.h"
+#include "graphics/video/video_decoder.h"
+
+#include "common/file.h"
 #include "common/system.h"
-#include "common/util.h"
-#include "common/array.h"
-#include "common/endian.h"
 
-#include "graphics/video/video_player.h"
-#include "graphics/surface.h"
-
 namespace Graphics {
 
-VideoDecoder::VideoDecoder() : _fileStream(0) {
-	_curFrameBlack = 0;
-	_curFrameWhite = 255;
+VideoDecoder::VideoDecoder() {
+	reset();
 }
 
-VideoDecoder::~VideoDecoder() {
-}
+bool VideoDecoder::loadFile(const Common::String &filename) {
+	Common::File *file = new Common::File();
 
-int VideoDecoder::getWidth() {
-	if (!_fileStream)
-		return 0;
-	return _videoInfo.width;
-}
+	if (!file->open(filename)) {
+		delete file;
+		return false;
+	}
 
-int VideoDecoder::getHeight() {
-	if (!_fileStream)
-		return 0;
-	return _videoInfo.height;
+	return load(*file);
 }
 
-int32 VideoDecoder::getCurFrame() {
-	if (!_fileStream)
-		return -1;
-	return _videoInfo.currentFrame;
+uint32 VideoDecoder::getElapsedTime() const {
+	return g_system->getMillis() - _startTime;
 }
 
-int32 VideoDecoder::getFrameCount() {
-	if (!_fileStream)
-		return 0;
-	return _videoInfo.frameCount;
-}
+void VideoDecoder::setSystemPalette() {
+	byte *vidPalette = getPalette();
+	byte *sysPalette = new byte[256 * 4];
 
-int32 VideoDecoder::getFrameRate() {
-	if (!_fileStream)
-		return 0;
-	return _videoInfo.frameRate;
-}
+	for (uint16 i = 0; i < 256; i++) {
+		sysPalette[i * 4] = vidPalette[i * 3];
+		sysPalette[i * 4 + 1] = vidPalette[i * 3 + 1];
+		sysPalette[i * 4 + 2] = vidPalette[i * 3 + 2];
+		sysPalette[i * 4 + 3] = 0;
+	}
 
-int32 VideoDecoder::getFrameDelay() {
-	if (!_fileStream)
-		return 0;
-	return _videoInfo.frameDelay;
+	g_system->setPalette(sysPalette, 0, 256);
+	delete[] sysPalette;
 }
 
-int32 VideoDecoder::getAudioLag() {
-	if (!_fileStream)
-		return 0;
-
-	/* No audio.
-	   Calculate the lag by how much time has gone by since the first frame
-	   and how much time *should* have passed.
-	*/
-	int32 audioTime = (g_system->getMillis() - _videoInfo.startTime) * 100;
-	int32 videoTime = _videoInfo.currentFrame * getFrameDelay();
-
-	return videoTime - audioTime;
+bool VideoDecoder::needsUpdate() const {	
+	return !endOfVideo() && getTimeToNextFrame() == 0;
 }
 
-uint32 VideoDecoder::getFrameWaitTime() {
-	int32 waitTime = (getFrameDelay() + getAudioLag()) / 100;
-
-	if (waitTime < 0)
-		return 0;
-
-	return waitTime;
+void VideoDecoder::reset() {
+	_curFrame = -1;
+	_startTime = 0;
 }
 
-void VideoDecoder::copyFrameToBuffer(byte *dst, uint x, uint y, uint pitch) {
-	uint h = getHeight();
-	uint w = getWidth();
-
-	byte *src = _videoFrameBuffer;
-	dst += y * pitch + x;
-
-	do {
-		memcpy(dst, src, w);
-		dst += pitch;
-		src += w;
-	} while (--h);
+bool VideoDecoder::endOfVideo() const {
+	return !isVideoLoaded() || getCurFrame() >= (int32)getFrameCount() - 1;
 }
 
-void VideoDecoder::setPalette(byte *pal) {
-	byte videoPalette[256 * 4];
+uint32 FixedRateVideoDecoder::getTimeToNextFrame() const {
+	if (endOfVideo() || _curFrame < 0)
+		return 0;
 
-	uint32 maxWeight = 0;
-	uint32 minWeight = 0xFFFFFFFF;
-	uint32 weight = 0;
-	byte r, g, b;
+	uint32 elapsedTime = getElapsedTime();
+	uint32 nextFrameStartTime = getFrameBeginTime(_curFrame + 1);
 
-	for (int i = 0; i < 256; i++) {
-		videoPalette[i * 4 + 0] = *pal++;
-		videoPalette[i * 4 + 1] = *pal++;
-		videoPalette[i * 4 + 2] = *pal++;
-		videoPalette[i * 4 + 3] = 0;
+	// If the time that the next frame should be shown has past
+	// the frame should be shown ASAP.
+	if (nextFrameStartTime <= elapsedTime)
+		return 0;
 
-		// Try and find the white and black colors for the current palette
-		r = videoPalette[i * 4 + 0];
-		g = videoPalette[i * 4 + 1];
-		b = videoPalette[i * 4 + 2];
-
-		weight = 3 * r * r + 6 * g * g + 2 * b * b;
-
-		if (weight >= maxWeight) {
-			_curFrameWhite = i;
-			maxWeight = weight;
-		}
-
-		if (weight <= minWeight) {
-			_curFrameBlack = i;
-			minWeight = i;
-		}
-	}
-
-	g_system->setPalette(videoPalette, 0, 256);
+	return nextFrameStartTime - elapsedTime;
 }
 
-
-/*
- *  VideoPlayer
- */
-
-void VideoPlayer::processVideoEvents(Common::List<Common::Event> &stopEvents) {
-	Common::Event curEvent;
-	Common::EventManager *eventMan = g_system->getEventManager();
-
-	// Process events, and skip video if esc is pressed
-	while (eventMan->pollEvent(curEvent)) {
-		if (curEvent.type == Common::EVENT_RTL || curEvent.type == Common::EVENT_QUIT) {
-			_skipVideo = true;
-		}
-
-		for (Common::List<Common::Event>::const_iterator iter = stopEvents.begin(); iter != stopEvents.end(); ++iter) {
-			if (curEvent.type == iter->type) {
-				if (iter->type == Common::EVENT_KEYDOWN || iter->type == Common::EVENT_KEYUP) {
-					if (curEvent.kbd.keycode == iter->kbd.keycode) {
-						_skipVideo = true;
-						break;
-					}
-				} else {
-					_skipVideo = true;
-					break;
-				}
-			}
-		}
-	}
+uint32 FixedRateVideoDecoder::getFrameBeginTime(uint32 frame) const {
+	return (uint32)(frame * 1000 / getFrameRate());
 }
 
-bool VideoPlayer::playVideo(Common::List<Common::Event> &stopEvents) {
-	_skipVideo = false;
-	debug(0, "Playing video");
-
-	g_system->fillScreen(0);
-
-	int frameX = (g_system->getWidth() - _decoder->getWidth()) / 2;
-	int frameY = (g_system->getHeight() - _decoder->getHeight()) / 2;
-
-	while (_decoder->getCurFrame() < _decoder->getFrameCount() && !_skipVideo) {
-		processVideoEvents(stopEvents);
-
-		uint32 startTime = 0;
-		_decoder->decodeNextFrame();
-
-		Graphics::Surface *screen = g_system->lockScreen();
-		_decoder->copyFrameToBuffer((byte *)screen->pixels, frameX, frameY, g_system->getWidth());
-		performPostProcessing((byte *)screen->pixels);
-		g_system->unlockScreen();
-
-		uint32 waitTime = _decoder->getFrameWaitTime();
-
-		if (!waitTime) {
-			warning("dropped frame %i", _decoder->getCurFrame());
-			continue;
-		}
-
-		// Update the screen
-		g_system->updateScreen();
-
-		startTime = g_system->getMillis();
-
-		// Wait before showing the next frame
-		while (g_system->getMillis() < startTime + waitTime && !_skipVideo) {
-			processVideoEvents(stopEvents);
-			g_system->delayMillis(10);
-		}
-	}
-
-	return !_skipVideo;
-}
-
-bool VideoPlayer::playVideo() {
-	Common::Event stopEvent;
-	Common::List<Common::Event> stopEvents;
-	stopEvents.clear();
-	stopEvent.type = Common::EVENT_KEYDOWN;
-	stopEvent.kbd = Common::KEYCODE_ESCAPE;
-	stopEvents.push_back(stopEvent);
-
-	return playVideo(stopEvents);
-}
-
-void VideoPlayer::performPostProcessing(byte *screen) {
-}
-
 } // End of namespace Graphics
