[Scummvm-cvs-logs] SF.net SVN: scummvm: [20445] scummvm/trunk/sword2

eriktorbjorn at users.sourceforge.net eriktorbjorn at users.sourceforge.net
Thu Feb 9 07:44:01 CET 2006


Revision: 20445
Author:   eriktorbjorn
Date:     2006-02-09 07:41:23 -0800 (Thu, 09 Feb 2006)
ViewCVS:  http://svn.sourceforge.net/scummvm?rev=20445&view=rev

Log Message:
-----------
Finally got rid of the "driver" directory. It made sense in the original code,
because that's where the Windows-specific stuff was, but in ScummVM we let the
backend handle the platform-specific stuff.

Next step will be to rename and restructure some of the files. I'll need to
look a bit more at the BASS and BS1 code bases first, to see if I can get any
sensible ideas from there.

Modified Paths:
--------------
    scummvm/trunk/sword2/anims.cpp
    scummvm/trunk/sword2/function.cpp
    scummvm/trunk/sword2/module.mk

Added Paths:
-----------
    scummvm/trunk/sword2/_mouse.cpp
    scummvm/trunk/sword2/animation.cpp
    scummvm/trunk/sword2/animation.h
    scummvm/trunk/sword2/d_draw.cpp
    scummvm/trunk/sword2/menu.cpp
    scummvm/trunk/sword2/music.cpp
    scummvm/trunk/sword2/palette.cpp
    scummvm/trunk/sword2/rdwin.cpp
    scummvm/trunk/sword2/render.cpp
    scummvm/trunk/sword2/sprite.cpp

Removed Paths:
-------------
    scummvm/trunk/sword2/driver/
Copied: scummvm/trunk/sword2/_mouse.cpp (from rev 20444, scummvm/trunk/sword2/driver/_mouse.cpp)
===================================================================
--- scummvm/trunk/sword2/_mouse.cpp	                        (rev 0)
+++ scummvm/trunk/sword2/_mouse.cpp	2006-02-09 15:41:23 UTC (rev 20445)
@@ -0,0 +1,247 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * 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/stdafx.h"
+#include "common/system.h"
+#include "common/stream.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/mouse.h"
+
+namespace Sword2 {
+
+// This is the maximum mouse cursor size in the SDL backend
+#define MAX_MOUSE_W     80
+#define MAX_MOUSE_H     80
+
+#define MOUSEFLASHFRAME 6
+
+void Mouse::decompressMouse(byte *decomp, byte *comp, uint8 frame, int width, int height, int pitch, int xOff, int yOff) {
+	int32 size = width * height;
+	int32 i = 0;
+	int x = 0;
+	int y = 0;
+
+	comp = comp + READ_LE_UINT32(comp + frame * 4) - MOUSE_ANIM_HEADER_SIZE;
+
+	while (i < size) {
+		if (*comp > 183) {
+			decomp[(y + yOff) * pitch + x + xOff] = *comp++;
+			if (++x >= width) {
+				x = 0;
+				y++;
+			}
+			i++;
+		} else {
+			x += *comp;
+			while (x >= width) {
+				y++;
+				x -= width;
+			}
+			i += *comp++;
+		}
+	}
+}
+
+void Mouse::drawMouse() {
+	byte mouseData[MAX_MOUSE_W * MAX_MOUSE_H];
+
+	if (!_mouseAnim.data && !_luggageAnim.data)
+		return;
+
+	// When an object is used in the game, the mouse cursor should be a
+	// combination of a standard mouse cursor and a luggage cursor.
+	//
+	// However, judging by the original code luggage cursors can also
+	// appear on their own. I have no idea which cases though.
+
+	uint16 mouse_width = 0;
+	uint16 mouse_height = 0;
+	uint16 hotspot_x = 0;
+	uint16 hotspot_y = 0;
+	int deltaX = 0;
+	int deltaY = 0;
+
+	if (_mouseAnim.data) {
+		hotspot_x = _mouseAnim.xHotSpot;
+		hotspot_y = _mouseAnim.yHotSpot;
+		mouse_width = _mouseAnim.mousew;
+		mouse_height = _mouseAnim.mouseh;
+	}
+
+	if (_luggageAnim.data) {
+		if (!_mouseAnim.data) {
+			hotspot_x = _luggageAnim.xHotSpot;
+			hotspot_y = _luggageAnim.yHotSpot;
+		}
+		if (_luggageAnim.mousew > mouse_width)
+			mouse_width = _luggageAnim.mousew;
+		if (_luggageAnim.mouseh > mouse_height)
+			mouse_height = _luggageAnim.mouseh;
+	}
+
+	if (_mouseAnim.data && _luggageAnim.data) {
+		deltaX = _mouseAnim.xHotSpot - _luggageAnim.xHotSpot;
+		deltaY = _mouseAnim.yHotSpot - _luggageAnim.yHotSpot;
+	}
+
+	assert(deltaX >= 0);
+	assert(deltaY >= 0);
+
+	// HACK for maximum cursor size. (The SDL backend imposes this
+	// restriction)
+
+	if (mouse_width + deltaX > MAX_MOUSE_W)
+		deltaX = 80 - mouse_width;
+	if (mouse_height + deltaY > MAX_MOUSE_H)
+		deltaY = 80 - mouse_height;
+
+	mouse_width += deltaX;
+	mouse_height += deltaY;
+
+	if ((uint32)(mouse_width * mouse_height) > sizeof(mouseData)) {
+		warning("Mouse cursor too large");
+		return;
+	}
+
+	memset(mouseData, 0, mouse_width * mouse_height);
+
+	if (_luggageAnim.data)
+		decompressMouse(mouseData, _luggageAnim.data, 0,
+			_luggageAnim.mousew, _luggageAnim.mouseh,
+			mouse_width, deltaX, deltaY);
+
+	if (_mouseAnim.data)
+		decompressMouse(mouseData, _mouseAnim.data, _mouseFrame,
+			_mouseAnim.mousew, _mouseAnim.mouseh, mouse_width);
+
+	_vm->_system->setMouseCursor(mouseData, mouse_width, mouse_height, hotspot_x, hotspot_y, 0);
+}
+
+/**
+ * Animates the current mouse pointer
+ */
+
+int32 Mouse::animateMouse() {
+	uint8 prevMouseFrame = _mouseFrame;
+
+	if (!_mouseAnim.data)
+		return RDERR_UNKNOWN;
+
+	if (++_mouseFrame == _mouseAnim.noAnimFrames)
+		_mouseFrame = MOUSEFLASHFRAME;
+
+	if (_mouseFrame != prevMouseFrame)
+		drawMouse();
+
+	return RD_OK;
+}
+
+/**
+ * Sets the mouse cursor animation.
+ * @param ma a pointer to the animation data, or NULL to clear the current one
+ * @param size the size of the mouse animation data
+ * @param mouseFlash RDMOUSE_FLASH or RDMOUSE_NOFLASH, depending on whether
+ * or not there is a lead-in animation
+ */
+
+int32 Mouse::setMouseAnim(byte *ma, int32 size, int32 mouseFlash) {
+	free(_mouseAnim.data);
+	_mouseAnim.data = NULL;
+
+	if (ma)	{
+		if (mouseFlash == RDMOUSE_FLASH)
+			_mouseFrame = 0;
+		else
+			_mouseFrame = MOUSEFLASHFRAME;
+
+		Common::MemoryReadStream readS(ma, size);
+
+		_mouseAnim.runTimeComp = readS.readByte();
+		_mouseAnim.noAnimFrames = readS.readByte();
+		_mouseAnim.xHotSpot = readS.readSByte();
+		_mouseAnim.yHotSpot = readS.readSByte();
+		_mouseAnim.mousew = readS.readByte();
+		_mouseAnim.mouseh = readS.readByte();
+
+		_mouseAnim.data = (byte *)malloc(size - MOUSE_ANIM_HEADER_SIZE);
+		if (!_mouseAnim.data)
+			return RDERR_OUTOFMEMORY;
+
+		readS.read(_mouseAnim.data, size - MOUSE_ANIM_HEADER_SIZE);
+
+		animateMouse();
+		drawMouse();
+
+		_vm->_system->showMouse(true);
+	} else {
+		if (_luggageAnim.data)
+			drawMouse();
+		else
+			_vm->_system->showMouse(false);
+	}
+
+	return RD_OK;
+}
+
+/**
+ * Sets the "luggage" animation to accompany the mouse animation. Luggage
+ * sprites are of the same format as mouse sprites.
+ * @param ma a pointer to the animation data, or NULL to clear the current one
+ * @param size the size of the animation data
+ */
+
+int32 Mouse::setLuggageAnim(byte *ma, int32 size) {
+	free(_luggageAnim.data);
+	_luggageAnim.data = NULL;
+
+	if (ma)	{
+		Common::MemoryReadStream readS(ma, size);
+
+		_luggageAnim.runTimeComp = readS.readByte();
+		_luggageAnim.noAnimFrames = readS.readByte();
+		_luggageAnim.xHotSpot = readS.readSByte();
+		_luggageAnim.yHotSpot = readS.readSByte();
+		_luggageAnim.mousew = readS.readByte();
+		_luggageAnim.mouseh = readS.readByte();
+
+		_luggageAnim.data = (byte *)malloc(size - MOUSE_ANIM_HEADER_SIZE);
+		if (!_luggageAnim.data)
+			return RDERR_OUTOFMEMORY;
+
+		readS.read(_luggageAnim.data, size - MOUSE_ANIM_HEADER_SIZE);
+
+		animateMouse();
+		drawMouse();
+
+		_vm->_system->showMouse(true);
+	} else {
+		if (_mouseAnim.data)
+			drawMouse();
+		else
+			_vm->_system->showMouse(false);
+	}
+
+	return RD_OK;
+}
+
+} // End of namespace Sword2

Copied: scummvm/trunk/sword2/animation.cpp (from rev 20444, scummvm/trunk/sword2/driver/animation.cpp)
===================================================================
--- scummvm/trunk/sword2/animation.cpp	                        (rev 0)
+++ scummvm/trunk/sword2/animation.cpp	2006-02-09 15:41:23 UTC (rev 20445)
@@ -0,0 +1,535 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * 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/stdafx.h"
+#include "common/file.h"
+#include "common/config-manager.h"
+#include "common/system.h"
+#include "sound/vorbis.h"
+#include "sound/mp3.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/maketext.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
+#include "sword2/animation.h"
+
+namespace Sword2 {
+
+AnimationState::AnimationState(Sword2Engine *vm)
+	: BaseAnimationState(vm->_mixer, vm->_system, 640, 480), _vm(vm) {
+}
+
+AnimationState::~AnimationState() {
+}
+
+#ifdef BACKEND_8BIT
+
+void AnimationState::setPalette(byte *pal) {
+	_vm->_screen->setPalette(0, 256, pal, RDPAL_INSTANT);
+}
+
+#else
+
+void AnimationState::drawTextObject(SpriteInfo *s, byte *src) {
+	OverlayColor *dst = _overlay + RENDERWIDE * s->y + s->x;
+
+	// FIXME: These aren't the "right" colours, but look good to me.
+
+	OverlayColor pen = _sys->RGBToColor(255, 255, 255);
+	OverlayColor border = _sys->RGBToColor(0, 0, 0);
+
+	for (int y = 0; y < s->h; y++) {
+		for (int x = 0; x < s->w; x++) {
+			switch (src[x]) {
+			case 1:
+				dst[x] = border;
+				break;
+			case 255:
+				dst[x] = pen;
+				break;
+			default:
+				break;
+			}
+		}
+		dst += RENDERWIDE;
+		src += s->w;
+	}
+}
+
+#endif
+
+void AnimationState::clearScreen() {
+#ifdef BACKEND_8BIT
+	memset(_vm->_screen->getScreen(), 0, _movieWidth * _movieHeight);
+#else
+	OverlayColor black = _sys->RGBToColor(0, 0, 0);
+
+	for (int i = 0; i < _movieWidth * _movieHeight; i++)
+		_overlay[i] = black;
+#endif
+}
+
+void AnimationState::updateScreen() {
+#ifdef BACKEND_8BIT
+	byte *buf = _vm->_screen->getScreen() + ((480 - _movieHeight) / 2) * RENDERWIDE + (640 - _movieWidth) / 2;
+
+	_vm->_system->copyRectToScreen(buf, _movieWidth, (640 - _movieWidth) / 2, (480 - _movieHeight) / 2, _movieWidth, _movieHeight);
+#else
+	_sys->copyRectToOverlay(_overlay, _movieWidth, 0, 0, _movieWidth, _movieHeight);
+#endif
+	_vm->_system->updateScreen();
+}
+
+void AnimationState::drawYUV(int width, int height, byte *const *dat) {
+#ifdef BACKEND_8BIT
+	_vm->_screen->plotYUV(_lut, width, height, dat);
+#else
+	plotYUV(width, height, dat);
+#endif
+}
+
+MovieInfo MoviePlayer::_movies[] = {
+	{ "carib",    222, false },
+	{ "escape",   187, false },
+	{ "eye",      248, false },
+	{ "finale",  1485, false },
+	{ "guard",     75, false },
+	{ "intro",   1800, false },
+	{ "jungle",   186, false },
+	{ "museum",   167, false },
+	{ "pablo",     75, false },
+	{ "pyramid",   60, false },
+	{ "quaram",   184, false },
+	{ "river",    656, false },
+	{ "sailing",  138, false },
+	{ "shaman",   788, true  },
+	{ "stone1",    34, false },
+	{ "stone2",   282, false },
+	{ "stone3",    65, false },
+	{ "demo",      60, false },
+	{ "enddemo",  110, false }
+};
+
+MoviePlayer::MoviePlayer(Sword2Engine *vm)
+	: _vm(vm), _snd(_vm->_mixer), _sys(_vm->_system), _textSurface(NULL) {
+}
+
+void MoviePlayer::openTextObject(MovieTextObject *obj) {
+	if (obj->textSprite)
+		_vm->_screen->createSurface(obj->textSprite, &_textSurface);
+}
+
+void MoviePlayer::closeTextObject(MovieTextObject *obj) {
+	if (_textSurface) {
+		_vm->_screen->deleteSurface(_textSurface);
+		_textSurface = NULL;
+	}
+}
+
+void MoviePlayer::drawTextObject(AnimationState *anim, MovieTextObject *obj) {
+	if (obj->textSprite && _textSurface) {
+#ifdef BACKEND_8BIT
+		_vm->_screen->drawSurface(obj->textSprite, _textSurface);
+#else
+		if (anim)
+			anim->drawTextObject(obj->textSprite, _textSurface);
+		else
+			_vm->_screen->drawSurface(obj->textSprite, _textSurface);
+#endif
+	}
+}
+
+/**
+ * Plays an animated cutscene.
+ * @param filename the file name of the cutscene file
+ * @param text the subtitles and voiceovers for the cutscene
+ * @param leadInRes lead-in music resource id
+ * @param leadOutRes lead-out music resource id
+ */
+
+int32 MoviePlayer::play(const char *filename, MovieTextObject *text[], int32 leadInRes, int32 leadOutRes) {
+	Audio::SoundHandle leadInHandle;
+
+	// This happens if the user quits during the "eye" smacker
+	if (_vm->_quit)
+		return RD_OK;
+
+	if (leadInRes) {
+		byte *leadIn = _vm->_resman->openResource(leadInRes);
+		uint32 leadInLen = _vm->_resman->fetchLen(leadInRes) - ResHeader::size();
+
+		assert(_vm->_resman->fetchType(leadIn) == WAV_FILE);
+
+		leadIn += ResHeader::size();
+
+		_vm->_sound->playFx(&leadInHandle, leadIn, leadInLen, Audio::Mixer::kMaxChannelVolume, 0, false, Audio::Mixer::kMusicSoundType);
+	}
+
+	byte *leadOut = NULL;
+	uint32 leadOutLen = 0;
+
+	if (leadOutRes) {
+		leadOut = _vm->_resman->openResource(leadOutRes);
+		leadOutLen = _vm->_resman->fetchLen(leadOutRes) - ResHeader::size();
+
+		assert(_vm->_resman->fetchType(leadOut) == WAV_FILE);
+
+		leadOut += ResHeader::size();
+	}
+
+	_leadOutFrame = (uint)-1;
+
+	int i;
+
+	for (i = 0; i < ARRAYSIZE(_movies); i++) {
+		if (scumm_stricmp(filename, _movies[i].name) == 0) {
+			_seamless = _movies[i].seamless;
+			if (_movies[i].frames > 60)
+				_leadOutFrame = _movies[i].frames - 60;
+			break;
+		}
+	}
+
+	if (i == ARRAYSIZE(_movies))
+		warning("Unknown movie, '%s'", filename);
+
+#ifdef USE_MPEG2
+	playMPEG(filename, text, leadOut, leadOutLen);
+#else
+	// No MPEG2? Use the old 'Narration Only' hack
+	playDummy(filename, text, leadOut, leadOutLen);
+#endif
+
+	_snd->stopHandle(leadInHandle);
+
+	// Wait for the lead-out to stop, if there is any. Though don't do it
+	// for seamless movies, since they are meant to blend into the rest of
+	// the game.
+
+	if (!_seamless) {
+		while (_vm->_mixer->isSoundHandleActive(_leadOutHandle)) {
+			_vm->_screen->updateDisplay();
+			_vm->_system->delayMillis(30);
+		}
+	}
+
+	if (leadInRes)
+		_vm->_resman->closeResource(leadInRes);
+
+	if (leadOutRes)
+		_vm->_resman->closeResource(leadOutRes);
+
+	return RD_OK;
+}
+
+void MoviePlayer::playMPEG(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen) {
+	uint frameCounter = 0, textCounter = 0;
+	Audio::SoundHandle handle;
+	bool skipCutscene = false, textVisible = false;
+	uint32 flags = Audio::Mixer::FLAG_16BITS;
+	bool startNextText = false;
+
+	byte oldPal[256 * 4];
+	memcpy(oldPal, _vm->_screen->getPalette(), sizeof(oldPal));
+
+	AnimationState *anim = new AnimationState(_vm);
+
+	if (!anim->init(filename)) {
+		delete anim;
+		// Missing Files? Use the old 'Narration Only' hack
+		playDummy(filename, text, leadOut, leadOutLen);
+		return;
+	}
+
+	// Clear the screen, because whatever is on it will be visible when the
+	// overlay is removed. And if there isn't an overlay, we don't want it
+	// to be visible during the cutscene. (Not all cutscenes cover the
+	// entire screen.)
+	_vm->_screen->clearScene();
+	_vm->_screen->updateDisplay();
+
+#ifndef SCUMM_BIG_ENDIAN
+	flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+
+	while (!skipCutscene && anim->decodeFrame()) {
+		// The frame has been drawn. Now draw the subtitles, if any,
+		// before updating the screen.
+
+		if (text && text[textCounter]) {
+			if (frameCounter == text[textCounter]->startFrame) {
+				openTextObject(text[textCounter]);
+				textVisible = true;
+
+				if (text[textCounter]->speech) {
+					startNextText = true;
+				}
+			}
+
+			if (startNextText && !_snd->isSoundHandleActive(handle)) {
+				_snd->playRaw(&handle, text[textCounter]->speech, text[textCounter]->speechBufferSize, 22050, flags);
+				startNextText = false;
+			}
+
+			if (frameCounter == text[textCounter]->endFrame) {
+				closeTextObject(text[textCounter]);
+				textCounter++;
+				textVisible = false;
+			}
+
+			if (textVisible)
+				drawTextObject(anim, text[textCounter]);
+		}
+
+		anim->updateScreen();
+		frameCounter++;
+
+		if (frameCounter == _leadOutFrame && leadOut)
+			_vm->_sound->playFx(&_leadOutHandle, leadOut, leadOutLen, Audio::Mixer::kMaxChannelVolume, 0, false, Audio::Mixer::kMusicSoundType);
+
+		OSystem::Event event;
+		while (_sys->pollEvent(event)) {
+			switch (event.type) {
+#ifndef BACKEND_8BIT
+			case OSystem::EVENT_SCREEN_CHANGED:
+				anim->buildLookup();
+				break;
+#endif
+			case OSystem::EVENT_KEYDOWN:
+				if (event.kbd.keycode == 27)
+					skipCutscene = true;
+				break;
+			case OSystem::EVENT_QUIT:
+				_vm->closeGame();
+				skipCutscene = true;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	if (!skipCutscene) {
+		// Sleep for one frame so that the last frame is displayed.
+		_sys->delayMillis(1000 / 12);
+	}
+
+	if (!_seamless) {
+		// Most movies fade to black on their own, but not all of them.
+		// Since we may be hanging around in the cutscene player for a
+		// while longer, waiting for the lead-out sound to finish,
+		// paint the overlay black.
+
+		anim->clearScreen();
+	}
+
+	// If the speech is still playing, redraw the subtitles. At least in
+	// the English version this is most noticeable in the "carib" cutscene.
+
+	if (textVisible && _snd->isSoundHandleActive(handle))
+		drawTextObject(anim, text[textCounter]);
+
+	if (text)
+		closeTextObject(text[textCounter]);
+
+	anim->updateScreen();
+
+	// Wait for the voice to stop playing. This is to make sure that we
+	// don't cut off the speech in mid-sentence, and - even more
+	// importantly - that we don't free the sound buffer while it's in use.
+
+	if (skipCutscene)
+		_snd->stopHandle(handle);
+
+	while (_snd->isSoundHandleActive(handle)) {
+		_vm->_screen->updateDisplay(false);
+		_sys->delayMillis(100);
+	}
+
+	if (!_seamless) {
+		// Clear the screen again
+		anim->clearScreen();
+		anim->updateScreen();
+	}
+
+	_vm->_screen->setPalette(0, 256, oldPal, RDPAL_INSTANT);
+
+	delete anim;
+}
+
+/**
+ * This just plays the cutscene with voiceovers / subtitles, in case the files
+ * are missing.
+ */
+
+void MoviePlayer::playDummy(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen) {
+	if (!text)
+		return;
+
+	int frameCounter = 0, textCounter = 0;
+
+	byte oldPal[256 * 4];
+	byte tmpPal[256 * 4];
+
+	_vm->_screen->clearScene();
+
+	// HACK: Draw instructions
+	//
+	// I'm using the the menu area, because that's unlikely to be touched
+	// by anything else during the cutscene.
+
+	memset(_vm->_screen->getScreen(), 0, _vm->_screen->getScreenWide() * MENUDEEP);
+
+	byte *data;
+
+	// Russian version substituted latin characters with cyrillic. That's
+	// why it renders completely unreadable with default message
+	if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) {
+		byte msg[] = "Po\344uk - to\344\345ko pev\345: hagmute k\344abuwy Ucke\343n, u\344u nocetute ca\343t npoekta u ckava\343te budeo po\344uku";
+		data = _vm->_fontRenderer->makeTextSprite(msg, RENDERWIDE, 255, _vm->_speechFontId);
+	} else {
+		// TODO: Translate message to other languages?
+#ifdef USE_MPEG2
+		byte msg[] = "Cutscene - Narration Only: Press ESC to exit, or visit www.scummvm.org to download cutscene videos";
+#else
+		byte msg[] = "Cutscene - Narration Only: Press ESC to exit, or recompile ScummVM with MPEG2 support";
+#endif
+
+		data = _vm->_fontRenderer->makeTextSprite(msg, RENDERWIDE, 255, _vm->_speechFontId);
+	}
+
+	FrameHeader frame_head;
+	SpriteInfo msgSprite;
+	byte *msgSurface;
+
+	frame_head.read(data);
+
+	msgSprite.x = _vm->_screen->getScreenWide() / 2 - frame_head.width / 2;
+	msgSprite.y = MENUDEEP / 2 - frame_head.height / 2;
+	msgSprite.w = frame_head.width;
+	msgSprite.h = frame_head.height;
+	msgSprite.type = RDSPR_NOCOMPRESSION;
+	msgSprite.data = data + FrameHeader::size();
+
+	_vm->_screen->createSurface(&msgSprite, &msgSurface);
+	_vm->_screen->drawSurface(&msgSprite, msgSurface);
+	_vm->_screen->deleteSurface(msgSurface);
+
+	free(data);
+
+	// In case the cutscene has a long lead-in, start just before the first
+	// line of text.
+
+	frameCounter = text[0]->startFrame - 12;
+
+	// Fake a palette that will hopefully make the text visible. In the
+	// opening cutscene it seems to use colours 1 (black) and 255 (white).
+
+	memcpy(oldPal, _vm->_screen->getPalette(), sizeof(oldPal));
+	memset(tmpPal, 0, sizeof(tmpPal));
+	tmpPal[255 * 4 + 0] = 255;
+	tmpPal[255 * 4 + 1] = 255;
+	tmpPal[255 * 4 + 2] = 255;
+	_vm->_screen->setPalette(0, 256, tmpPal, RDPAL_INSTANT);
+
+	Audio::SoundHandle handle;
+
+	bool skipCutscene = false;
+
+	uint32 flags = Audio::Mixer::FLAG_16BITS;
+
+#ifndef SCUMM_BIG_ENDIAN
+	flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+
+	while (1) {
+		if (!text[textCounter])
+			break;
+
+		if (frameCounter == text[textCounter]->startFrame) {
+			_vm->_screen->clearScene();
+			openTextObject(text[textCounter]);
+			drawTextObject(NULL, text[textCounter]);
+			if (text[textCounter]->speech) {
+				_snd->playRaw(&handle, text[textCounter]->speech, text[textCounter]->speechBufferSize, 22050, flags);
+			}
+		}
+
+		if (frameCounter == text[textCounter]->endFrame) {
+			closeTextObject(text[textCounter]);
+			_vm->_screen->clearScene();
+			_vm->_screen->setNeedFullRedraw();
+			textCounter++;
+		}
+
+		frameCounter++;
+		_vm->_screen->updateDisplay();
+
+		KeyboardEvent *ke = _vm->keyboardEvent();
+
+		if ((ke && ke->keycode == 27) || _vm->_quit) {
+			_snd->stopHandle(handle);
+			skipCutscene = true;
+			break;
+		}
+
+		// Simulate ~12 frames per second. I don't know what frame rate
+		// the original movies had, or even if it was constant, but
+		// this seems to work reasonably.
+
+		_sys->delayMillis(90);
+	}
+
+	// Wait for the voice to stop playing. This is to make sure that we
+	// don't cut off the speech in mid-sentence, and - even more
+	// importantly - that we don't free the sound buffer while it's in use.
+
+	while (_snd->isSoundHandleActive(handle)) {
+		_vm->_screen->updateDisplay(false);
+		_sys->delayMillis(100);
+	}
+
+	closeTextObject(text[textCounter]);
+
+	_vm->_screen->clearScene();
+	_vm->_screen->setNeedFullRedraw();
+
+	// HACK: Remove the instructions created above
+	Common::Rect r;
+
+	memset(_vm->_screen->getScreen(), 0, _vm->_screen->getScreenWide() * MENUDEEP);
+	r.left = r.top = 0;
+	r.right = _vm->_screen->getScreenWide();
+	r.bottom = MENUDEEP;
+	_vm->_screen->updateRect(&r);
+
+	// FIXME: For now, only play the lead-out music for cutscenes that have
+	// subtitles.
+
+	if (!skipCutscene && leadOut)
+		_vm->_sound->playFx(&_leadOutHandle, leadOut, leadOutLen, Audio::Mixer::kMaxChannelVolume, 0, false, Audio::Mixer::kMusicSoundType);
+
+	_vm->_screen->setPalette(0, 256, oldPal, RDPAL_INSTANT);
+}
+
+} // End of namespace Sword2

Copied: scummvm/trunk/sword2/animation.h (from rev 20444, scummvm/trunk/sword2/driver/animation.h)
===================================================================
--- scummvm/trunk/sword2/animation.h	                        (rev 0)
+++ scummvm/trunk/sword2/animation.h	2006-02-09 15:41:23 UTC (rev 20445)
@@ -0,0 +1,102 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * 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.
+ *
+ * $Header$
+ *
+ */
+
+#ifndef ANIMATION_H
+#define ANIMATION_H
+
+#include "graphics/animation.h"
+#include "sound/mixer.h"
+
+namespace Sword2 {
+
+struct SpriteInfo;
+
+// This is the structure which is passed to the sequence player. It includes
+// the smack to play, and any text lines which are to be displayed over the top
+// of the sequence.
+
+struct MovieTextObject {
+	uint16 startFrame;
+	uint16 endFrame;
+	SpriteInfo *textSprite;
+	uint32 speechBufferSize;
+	uint16 *speech;
+};
+
+class AnimationState : public ::Graphics::BaseAnimationState {
+private:
+	Sword2Engine *_vm;
+
+public:
+	AnimationState(Sword2Engine *vm);
+	~AnimationState();
+
+#ifndef BACKEND_8BIT
+	void drawTextObject(SpriteInfo *s, byte *src);
+#endif
+
+	void clearScreen();
+	void updateScreen();
+
+private:
+	void drawYUV(int width, int height, byte *const *dat);
+
+#ifdef BACKEND_8BIT
+	void setPalette(byte *pal);
+#endif
+};
+
+struct MovieInfo {
+	char name[9];
+	uint frames;
+	bool seamless;
+};
+
+class MoviePlayer {
+private:
+	Sword2Engine *_vm;
+	Audio::Mixer *_snd;
+	OSystem *_sys;
+
+	byte *_textSurface;
+
+	Audio::SoundHandle _leadOutHandle;
+
+	uint _leadOutFrame;
+	bool _seamless;
+
+	static struct MovieInfo _movies[];
+
+	void openTextObject(MovieTextObject *obj);
+	void closeTextObject(MovieTextObject *obj);
+	void drawTextObject(AnimationState *anim, MovieTextObject *obj);
+
+	void playMPEG(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen);
+	void playDummy(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen);
+
+public:
+	MoviePlayer(Sword2Engine *vm);
+	int32 play(const char *filename, MovieTextObject *text[], int32 leadInRes, int32 leadOutRes);
+};
+
+} // End of namespace Sword2
+
+#endif

Modified: scummvm/trunk/sword2/anims.cpp
===================================================================
--- scummvm/trunk/sword2/anims.cpp	2006-02-09 15:12:44 UTC (rev 20444)
+++ scummvm/trunk/sword2/anims.cpp	2006-02-09 15:41:23 UTC (rev 20445)
@@ -37,7 +37,7 @@
 #include "sword2/resman.h"
 #include "sword2/router.h"
 #include "sword2/sound.h"
-#include "sword2/driver/animation.h"
+#include "sword2/animation.h"
 
 namespace Sword2 {
 

Copied: scummvm/trunk/sword2/d_draw.cpp (from rev 20444, scummvm/trunk/sword2/driver/d_draw.cpp)
===================================================================
--- scummvm/trunk/sword2/d_draw.cpp	                        (rev 0)
+++ scummvm/trunk/sword2/d_draw.cpp	2006-02-09 15:41:23 UTC (rev 20445)
@@ -0,0 +1,71 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * 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/stdafx.h"
+#include "common/system.h"
+#include "sword2/sword2.h"
+#include "sword2/mouse.h"
+
+namespace Sword2 {
+
+/**
+ * @return the graphics detail setting
+ */
+
+int8 Screen::getRenderLevel() {
+	return _renderLevel;
+}
+
+void Screen::setRenderLevel(int8 level) {
+	_renderLevel = level;
+
+	switch (_renderLevel) {
+	case 0:
+		// Lowest setting: no fancy stuff
+		_renderCaps = 0;
+		break;
+	case 1:
+		// Medium-low setting: transparency-blending
+		_renderCaps = RDBLTFX_SPRITEBLEND;
+		break;
+	case 2:
+		// Medium-high setting: transparency-blending + shading
+		_renderCaps = RDBLTFX_SPRITEBLEND | RDBLTFX_SHADOWBLEND;
+		break;
+	case 3:
+		// Highest setting: transparency-blending + shading +
+		// edge-blending + improved stretching
+		_renderCaps = RDBLTFX_SPRITEBLEND | RDBLTFX_SHADOWBLEND | RDBLTFX_EDGEBLEND;
+		break;
+	}
+}
+
+/**
+ * Fill the screen buffer with palette colour zero. Note that it does not
+ * touch the menu areas of the screen.
+ */
+
+void Screen::clearScene() {
+	memset(_buffer + MENUDEEP * _screenWide, 0, _screenWide * RENDERDEEP);
+	_needFullRedraw = true;
+}
+
+} // End of namespace Sword2

Modified: scummvm/trunk/sword2/function.cpp
===================================================================
--- scummvm/trunk/sword2/function.cpp	2006-02-09 15:12:44 UTC (rev 20444)
+++ scummvm/trunk/sword2/function.cpp	2006-02-09 15:41:23 UTC (rev 20445)
@@ -34,7 +34,7 @@
 #include "sword2/resman.h"
 #include "sword2/router.h"
 #include "sword2/sound.h"
-#include "sword2/driver/animation.h"
+#include "sword2/animation.h"
 
 namespace Sword2 {
 

Copied: scummvm/trunk/sword2/menu.cpp (from rev 20444, scummvm/trunk/sword2/driver/menu.cpp)
===================================================================
--- scummvm/trunk/sword2/menu.cpp	                        (rev 0)
+++ scummvm/trunk/sword2/menu.cpp	2006-02-09 15:41:23 UTC (rev 20445)
@@ -0,0 +1,291 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * 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/stdafx.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/mouse.h"
+
+namespace Sword2 {
+
+#define MENUDEEP 40
+#define MAXMENUANIMS 8
+
+void Mouse::clearIconArea(int menu, int pocket, Common::Rect *r) {
+	byte *buf = _vm->_screen->getScreen();
+	int16 screenWide = _vm->_screen->getScreenWide();
+
+	r->top = menu * (RENDERDEEP + MENUDEEP) + (MENUDEEP - RDMENU_ICONDEEP) / 2;
+	r->bottom = r->top + RDMENU_ICONDEEP;
+	r->left = RDMENU_ICONSTART + pocket * (RDMENU_ICONWIDE + RDMENU_ICONSPACING);
+	r->right = r->left + RDMENU_ICONWIDE;
+
+	byte *dst = buf + r->top * screenWide + r->left;
+
+	for (int i = 0; i < RDMENU_ICONDEEP; i++) {
+		memset(dst, 0, RDMENU_ICONWIDE);
+		dst += screenWide;
+	}
+}
+
+/**
+ * This function should be called regularly to process the menubar system. The
+ * rate at which this function is called will dictate how smooth the menu
+ * system is.
+ */
+
+void Mouse::processMenu() {
+	uint8 menu;
+	uint8 i, j;
+	uint8 frameCount;
+	Common::Rect r1, r2;
+	static int32 lastTime = 0;
+
+	byte *buf = _vm->_screen->getScreen();
+	int16 screenWide = _vm->_screen->getScreenWide();
+
+	if (lastTime == 0) {
+		lastTime = _vm->getMillis();
+		frameCount = 1;
+	} else {
+		int32 delta = _vm->getMillis() - lastTime;
+
+		if (delta > 250) {
+			lastTime += delta;
+			delta = 250;
+			frameCount = 1;
+		} else {
+			frameCount = (uint8) ((_iconCount + 8) * delta / 750);
+			lastTime += frameCount * 750 / (_iconCount + 8);
+		}
+	}
+
+	// Note: The "almost hidden" menu state exists only so that the menu
+	// will be redrawn one last time before it's completely hidden. We do
+	// not need a corresponding "almost shown" state because the menu will
+	// always be redrawn while it's shown anyway. (We may want to change
+	// this later.)
+
+	while (frameCount-- > 0) {
+		for (menu = RDMENU_TOP; menu <= RDMENU_BOTTOM; menu++) {
+			if (_menuStatus[menu] == RDMENU_HIDDEN || _menuStatus[menu] == RDMENU_ALMOST_HIDDEN || _menuStatus[menu] == RDMENU_SHOWN)
+				continue;
+
+			int target, direction, nextState;
+
+			if (_menuStatus[menu] == RDMENU_OPENING) {
+				target = MAXMENUANIMS;
+				direction = 1;
+				nextState = RDMENU_SHOWN;
+			} else {
+				target = 0;
+				direction = -1;
+				nextState = RDMENU_ALMOST_HIDDEN;
+			}
+
+			bool complete = true;
+
+			// Propagate animation from the first icon...
+			for (i = RDMENU_MAXPOCKETS - 1; i > 0; i--) {
+				_pocketStatus[menu][i] = _pocketStatus[menu][i - 1];
+
+				if (_pocketStatus[menu][i] != target)
+					complete = false;
+			}
+
+			if (_pocketStatus[menu][i] != target)
+				complete = false;
+
+			// ...and animate the first icon
+			if (_pocketStatus[menu][0] != target)
+				_pocketStatus[menu][0] += direction;
+
+			if (complete)
+				_menuStatus[menu] = nextState;
+		}
+	}
+
+	for (menu = RDMENU_TOP; menu <= RDMENU_BOTTOM; menu++) {
+		if (_menuStatus[menu] == RDMENU_HIDDEN)
+			continue;
+
+		if (_menuStatus[menu] == RDMENU_ALMOST_HIDDEN)
+			_menuStatus[menu] = RDMENU_HIDDEN;
+
+		// Draw the menu here.
+		int32 curx = RDMENU_ICONSTART + RDMENU_ICONWIDE / 2;
+		int32 cury = (MENUDEEP / 2) + (RENDERDEEP + MENUDEEP) * menu;
+
+		for (i = 0; i < RDMENU_MAXPOCKETS; i++) {
+			if (_icons[menu][i]) {
+				int32 xoff, yoff;
+
+				// Since we no longer clear the screen after
+				// each frame we need to clear the icon area.
+
+				clearIconArea(menu, i, &r1);
+
+				if (_pocketStatus[menu][i] == MAXMENUANIMS) {
+					xoff = (RDMENU_ICONWIDE / 2);
+					r2.left = curx - xoff;
+					r2.right = r2.left + RDMENU_ICONWIDE;
+					yoff = (RDMENU_ICONDEEP / 2);
+					r2.top = cury - yoff;
+					r2.bottom = r2.top + RDMENU_ICONDEEP;
+				} else {
+					xoff = (RDMENU_ICONWIDE / 2) * _pocketStatus[menu][i] / MAXMENUANIMS;
+					r2.left = curx - xoff;
+					r2.right = curx + xoff;
+					yoff = (RDMENU_ICONDEEP / 2) * _pocketStatus[menu][i] / MAXMENUANIMS;
+					r2.top = cury - yoff;
+					r2.bottom = cury + yoff;
+				}
+
+				if (xoff != 0 && yoff != 0) {
+					byte *dst = buf + r2.top * screenWide + r2.left;
+					byte *src = _icons[menu][i];
+
+					if (_pocketStatus[menu][i] != MAXMENUANIMS) {
+						_vm->_screen->scaleImageFast(
+							dst, screenWide, r2.right - r2.left, r2.bottom - r2.top,
+							src, RDMENU_ICONWIDE, RDMENU_ICONWIDE, RDMENU_ICONDEEP);
+					} else {
+						for (j = 0; j < RDMENU_ICONDEEP; j++) {
+							memcpy(dst, src, RDMENU_ICONWIDE);
+							src += RDMENU_ICONWIDE;
+							dst += screenWide;
+						}
+					}
+				}
+				_vm->_screen->updateRect(&r1);
+			}
+			curx += (RDMENU_ICONSPACING + RDMENU_ICONWIDE);
+		}
+	}
+}
+
+/**
+ * This function brings a specified menu into view.
+ * @param menu RDMENU_TOP or RDMENU_BOTTOM, depending on which menu to show
+ * @return RD_OK, or an error code
+ */
+
+int32 Mouse::showMenu(uint8 menu) {
+	// Check for invalid menu parameter
+	if (menu > RDMENU_BOTTOM)
+		return RDERR_INVALIDMENU;
+
+	// Check that the menu is not currently shown, or in the process of
+	// being shown.
+	if (_menuStatus[menu] == RDMENU_SHOWN || _menuStatus[menu] == RDMENU_OPENING)
+		return RDERR_INVALIDCOMMAND;
+
+	_menuStatus[menu] = RDMENU_OPENING;
+	return RD_OK;
+}
+
+/**
+ * This function hides a specified menu.
+ * @param menu RDMENU_TOP or RDMENU_BOTTOM depending on which menu to hide
+ * @return RD_OK, or an error code
+ */
+
+int32 Mouse::hideMenu(uint8 menu) {
+	// Check for invalid menu parameter
+	if (menu > RDMENU_BOTTOM)
+		return RDERR_INVALIDMENU;
+
+	// Check that the menu is not currently hidden, or in the process of
+	// being hidden.
+	if (_menuStatus[menu] == RDMENU_HIDDEN || _menuStatus[menu] == RDMENU_CLOSING)
+		return RDERR_INVALIDCOMMAND;
+
+	_menuStatus[menu] = RDMENU_CLOSING;
+	return RD_OK;
+}
+
+/**
+ * This function hides both menus immediately.
+ */
+
+void Mouse::closeMenuImmediately() {
+	Common::Rect r;
+	int i;
+
+	_menuStatus[RDMENU_TOP] = RDMENU_HIDDEN;
+	_menuStatus[RDMENU_BOTTOM] = RDMENU_HIDDEN;
+
+	for (i = 0; i < RDMENU_MAXPOCKETS; i++) {
+		if (_icons[RDMENU_TOP][i]) {
+			clearIconArea(RDMENU_TOP, i, &r);
+			_vm->_screen->updateRect(&r);
+		}
+		if (_icons[RDMENU_BOTTOM][i]) {
+			clearIconArea(RDMENU_BOTTOM, i, &r);
+			_vm->_screen->updateRect(&r);
+		}
+	}
+
+	memset(_pocketStatus, 0, sizeof(uint8) * 2 * RDMENU_MAXPOCKETS);
+}
+
+/**
+ * This function sets a menubar icon.
+ * @param menu RDMENU_TOP or RDMENU_BOTTOM, depending on which menu to change
+ * @param pocket the menu pocket to change
+ * @param icon icon data, or NULL to clear the icon
+ * @return RD_OK, or an error code
+ */
+
+int32 Mouse::setMenuIcon(uint8 menu, uint8 pocket, byte *icon) {
+	Common::Rect r;
+
+	// Check for invalid menu parameter.
+	if (menu > RDMENU_BOTTOM)
+		return RDERR_INVALIDMENU;
+
+	// Check for invalid pocket parameter
+	if (pocket >= RDMENU_MAXPOCKETS)
+		return RDERR_INVALIDPOCKET;
+
+	// If there is an icon in the requested menu/pocket, clear it out.
+	if (_icons[menu][pocket]) {
+		_iconCount--;
+		free(_icons[menu][pocket]);
+		_icons[menu][pocket] = NULL;
+		clearIconArea(menu, pocket, &r);
+		_vm->_screen->updateRect(&r);
+	}
+
+	// Only put the icon in the pocket if it is not NULL
+	if (icon != NULL) {
+		_iconCount++;
+		_icons[menu][pocket] = (byte *)malloc(RDMENU_ICONWIDE * RDMENU_ICONDEEP);
+		if (_icons[menu][pocket] == NULL)
+			return RDERR_OUTOFMEMORY;
+		memcpy(_icons[menu][pocket], icon, RDMENU_ICONWIDE * RDMENU_ICONDEEP);
+	}
+
+	return RD_OK;
+}
+
+} // End of namespace Sword2

Modified: scummvm/trunk/sword2/module.mk
===================================================================
--- scummvm/trunk/sword2/module.mk	2006-02-09 15:12:44 UTC (rev 20444)
+++ scummvm/trunk/sword2/module.mk	2006-02-09 15:41:23 UTC (rev 20445)
@@ -1,10 +1,13 @@
 MODULE := sword2
 
 MODULE_OBJS := \
+	sword2/_mouse.o \
+	sword2/animation.o \
 	sword2/anims.o \
 	sword2/build_display.o \
 	sword2/console.o \
 	sword2/controls.o \
+	sword2/d_draw.o \
 	sword2/debug.o \
 	sword2/events.o \
 	sword2/function.o \
@@ -14,31 +17,27 @@
 	sword2/logic.o \
 	sword2/maketext.o \
 	sword2/memory.o \
+	sword2/menu.o \
 	sword2/mouse.o \
+	sword2/music.o \
+	sword2/palette.o \
 	sword2/protocol.o \
+	sword2/rdwin.o \
+	sword2/render.o \
 	sword2/resman.o \
 	sword2/router.o \
 	sword2/save_rest.o \
 	sword2/scroll.o \
 	sword2/sound.o \
 	sword2/speech.o \
+	sword2/sprite.o \
 	sword2/startup.o \
 	sword2/sword2.o \
 	sword2/sync.o \
-	sword2/walker.o \
-	sword2/driver/d_draw.o \
-	sword2/driver/d_sound.o \
-	sword2/driver/menu.o \
-	sword2/driver/_mouse.o \
-	sword2/driver/palette.o \
-	sword2/driver/rdwin.o \
-	sword2/driver/render.o \
-	sword2/driver/sprite.o \
-	sword2/driver/animation.o
+	sword2/walker.o
 
 MODULE_DIRS += \
-	sword2 \
-	sword2/driver
+	sword2
 
 # This module can be built as a plugin
 ifdef BUILD_PLUGINS

Copied: scummvm/trunk/sword2/music.cpp (from rev 20444, scummvm/trunk/sword2/driver/d_sound.cpp)
===================================================================
--- scummvm/trunk/sword2/music.cpp	                        (rev 0)
+++ scummvm/trunk/sword2/music.cpp	2006-02-09 15:41:23 UTC (rev 20445)
@@ -0,0 +1,856 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * 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$
+ */
+
+// One feature still missing is the original's DipMusic() function which, as
+// far as I can understand, softened the music volume when someone was
+// speaking, but only (?) if the music was playing loudly at the time.
+//
+// All things considered, I think this is more bother than it's worth.
+
+#include "common/stdafx.h"
+#include "common/file.h"
+#include "common/system.h"
+#include "sound/mp3.h"
+#include "sound/vorbis.h"
+#include "sound/flac.h"
+#include "sound/rate.h"
+#include "sound/wave.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
+
+namespace Sword2 {
+
+static AudioStream *makeCLUStream(Common::File *fp, int size);
+
+static AudioStream *getAudioStream(SoundFileHandle *fh, const char *base, int cd, uint32 id, uint32 *numSamples) {
+	debug(3, "Playing %s from CD %d", base, cd);
+
+	if (!fh->file.isOpen()) {
+		struct {
+			const char *ext;
+			int mode;
+		} file_types[] = {
+	#ifdef USE_MAD
+			{ "cl3", kMP3Mode },
+	#endif
+	#ifdef USE_VORBIS
+			{ "clg", kVorbisMode },
+	#endif
+	#ifdef USE_FLAC
+			{ "clf", kFlacMode },
+	#endif
+			{ "clu", kCLUMode }
+		};
+
+		int soundMode = 0;
+		char filename[20];
+
+		for (int i = 0; i < ARRAYSIZE(file_types); i++) {
+			Common::File f;
+
+			sprintf(filename, "%s%d.%s", base, cd, file_types[i].ext);
+			if (f.open(filename)) {
+				soundMode = file_types[i].mode;
+				break;
+			}
+
+			sprintf(filename, "%s.%s", base, file_types[i].ext);
+			if (f.open(filename)) {
+				soundMode = file_types[i].mode;
+				break;
+			}
+		}
+
+		if (soundMode == 0)
+			return NULL;
+
+		fh->file.open(filename);
+		fh->fileType = soundMode;
+		if (!fh->file.isOpen()) {
+			warning("Very strange fopen error");
+			return NULL;
+		}
+		if (fh->fileSize != fh->file.size()) {
+			if (fh->idxTab) {
+				free(fh->idxTab);
+				fh->idxTab = NULL;
+			}
+		}
+	}
+
+	uint32 entrySize = (fh->fileType == kCLUMode) ? 2 : 3;
+
+	if (!fh->idxTab) {
+		fh->file.seek(0);
+		fh->idxLen = fh->file.readUint32LE();
+		fh->file.seek(entrySize * 4);
+
+		fh->idxTab = (uint32*)malloc(fh->idxLen * 3 * sizeof(uint32));
+		for (uint32 cnt = 0; cnt < fh->idxLen; cnt++) {
+			fh->idxTab[cnt * 3 + 0] = fh->file.readUint32LE();
+			fh->idxTab[cnt * 3 + 1] = fh->file.readUint32LE();
+			if (fh->fileType == kCLUMode) {
+				fh->idxTab[cnt * 3 + 2] = fh->idxTab[cnt * 3 + 1];
+				fh->idxTab[cnt * 3 + 1]--;
+			} else
+				fh->idxTab[cnt * 3 + 2] = fh->file.readUint32LE();
+		}
+	}
+
+	uint32 pos = fh->idxTab[id * 3 + 0];
+	uint32 len = fh->idxTab[id * 3 + 1];
+	uint32 enc_len = fh->idxTab[id * 3 + 2];
+
+	if (numSamples)
+		*numSamples = len;
+
+	if (!pos || !len) {
+		fh->file.close();
+		return NULL;
+	}
+
+	fh->file.seek(pos, SEEK_SET);
+
+	switch (fh->fileType) {
+	case kCLUMode:
+		return makeCLUStream(&fh->file, enc_len);
+#ifdef USE_MAD
+	case kMP3Mode:
+		return makeMP3Stream(&fh->file, enc_len);
+#endif
+#ifdef USE_VORBIS
+	case kVorbisMode:
+		return makeVorbisStream(&fh->file, enc_len);
+#endif
+#ifdef USE_FLAC
+	case kFlacMode:
+		return makeFlacStream(&fh->file, enc_len);
+#endif
+	default:
+		return NULL;
+	}
+}
+
+// ----------------------------------------------------------------------------
+// Custom AudioStream class to handle Broken Sword 2's audio compression.
+// ----------------------------------------------------------------------------
+
+#define GetCompressedShift(n)      ((n) >> 4)
+#define GetCompressedSign(n)       (((n) >> 3) & 1)
+#define GetCompressedAmplitude(n)  ((n) & 7)
+
+CLUInputStream::CLUInputStream(Common::File *file, int size)
+	: _file(file), _firstTime(true), _bufferEnd(_outbuf + BUFFER_SIZE) {
+
+	_file->incRef();
+
+	// Determine the end position.
+	_file_pos = _file->pos();
+	_end_pos = _file_pos + size;
+
+	// Read in initial data
+	refill();
+}
+
+CLUInputStream::~CLUInputStream() {
+	_file->decRef();
+}
+
+int CLUInputStream::readBuffer(int16 *buffer, const int numSamples) {
+	int samples = 0;
+	while (samples < numSamples && !eosIntern()) {
+		const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
+		memcpy(buffer, _pos, len * 2);
+		buffer += len;
+		_pos += len;
+		samples += len;
+		if (_pos >= _bufferEnd) {
+			refill();
+		}
+	}
+	return samples;
+}
+
+void CLUInputStream::refill() {
+	byte *in = _inbuf;
+	int16 *out = _outbuf;
+
+	_file->seek(_file_pos, SEEK_SET);
+
+	uint len_left = _file->read(in, MIN((uint32)BUFFER_SIZE, _end_pos - _file->pos()));
+
+	_file_pos = _file->pos();
+
+	while (len_left > 0) {
+		uint16 sample;
+
+		if (_firstTime) {
+			_firstTime = false;
+			_prev = READ_LE_UINT16(in);
+			sample = _prev;
+			len_left -= 2;
+			in += 2;
+		} else {
+			uint16 delta = GetCompressedAmplitude(*in) << GetCompressedShift(*in);
+			if (GetCompressedSign(*in))
+				sample = _prev - delta;
+			else
+				sample = _prev + delta;
+
+			_prev = sample;
+			len_left--;
+			in++;
+		}
+
+		*out++ = sample;
+	}
+
+	_pos = _outbuf;
+	_bufferEnd = out;
+}
+
+AudioStream *makeCLUStream(Common::File *file, int size) {
+	return new CLUInputStream(file, size);
+}
+
+// ----------------------------------------------------------------------------
+// Another custom AudioStream class, to wrap around the various AudioStream
+// classes used for music decompression, and to add looping, fading, etc.
+// ----------------------------------------------------------------------------
+
+// The length of a fade-in/out, in milliseconds.
+#define FADE_LENGTH 3000
+
+MusicInputStream::MusicInputStream(int cd, SoundFileHandle *fh, uint32 musicId, bool looping) {
+	_cd = cd;
+	_fh = fh;
+	_musicId = musicId;
+	_looping = looping;
+
+	_bufferEnd = _buffer + BUFFER_SIZE;
+	_remove = false;
+	_fading = 0;
+
+	_decoder = getAudioStream(_fh, "music", _cd, _musicId, &_numSamples);
+	if (_decoder) {
+		_samplesLeft = _numSamples;
+		_fadeSamples = (getRate() * FADE_LENGTH) / 1000;
+		fadeUp();
+
+		// Read in initial data
+		refill();
+	}
+}
+
+MusicInputStream::~MusicInputStream() {
+	delete _decoder;
+}
+
+int MusicInputStream::readBuffer(int16 *buffer, const int numSamples) {
+	if (!_decoder)
+		return 0;
+
+	int samples = 0;
+	while (samples < numSamples && !eosIntern()) {
+		const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
+		memcpy(buffer, _pos, len * 2);
+		buffer += len;
+		_pos += len;
+		samples += len;
+		if (_pos >= _bufferEnd) {
+			refill();
+		}
+	}
+	return samples;
+}
+
+void MusicInputStream::refill() {
+	int16 *buf = _buffer;
+	uint32 numSamples = 0;
+	uint32 len_left;
+	bool endFade = false;
+
+	len_left = BUFFER_SIZE;
+
+	if (_fading > 0 && (uint32)_fading < len_left)
+		len_left = _fading;
+
+	if (_samplesLeft < len_left)
+		len_left = _samplesLeft;
+
+	if (!_looping) {
+		// Non-looping music is faded out at the end. If this fade
+		// out would have started somewhere within the len_left samples
+		// to read, we only read up to that point. This way, we can
+		// treat this fade as any other.
+
+		if (!_fading) {
+			uint32 currentlyAt = _numSamples - _samplesLeft;
+			uint32 fadeOutAt = _numSamples - _fadeSamples;
+			uint32 readTo = currentlyAt + len_left;
+
+			if (fadeOutAt == currentlyAt)
+				fadeDown();
+			else if (fadeOutAt > currentlyAt && fadeOutAt <= readTo) {
+				len_left = fadeOutAt - currentlyAt;
+				endFade = true;
+			}
+		}
+	}
+
+	int desired = len_left - numSamples;
+	int len = _decoder->readBuffer(buf, desired);
+
+	// Shouldn't happen, but if it does it could cause an infinite loop.
+	// Of course there were bugs that caused it to happen several times
+	// during development. :-)
+
+	if (len < desired) {
+		warning("Expected %d samples, but got %d", desired, len);
+		_samplesLeft = len;
+	}
+
+	buf += len;
+	numSamples += len;
+	len_left -= len;
+	_samplesLeft -= len;
+
+	int16 *ptr;
+
+	if (_fading > 0) {
+		// Fade down
+		for (ptr = _buffer; ptr < buf; ptr++) {
+			if (_fading > 0) {
+				_fading--;
+				*ptr = (*ptr * _fading) / _fadeSamples;
+			}
+			if (_fading == 0) {
+				_looping = false;
+				_remove = true;
+				*ptr = 0;
+			}
+		}
+	} else if (_fading < 0) {
+		// Fade up
+		for (ptr = _buffer; ptr < buf; ptr++) {
+			_fading--;
+			*ptr = -(*ptr * _fading) / _fadeSamples;
+			if (_fading <= -_fadeSamples) {
+				_fading = 0;
+				break;
+			}
+		}
+	}
+
+	if (endFade)
+		fadeDown();
+
+	if (!_samplesLeft) {
+		if (_looping) {
+			delete _decoder;
+			_decoder = getAudioStream(_fh, "music", _cd, _musicId, &_numSamples);
+			_samplesLeft = _numSamples;
+		} else
+			_remove = true;
+	}
+
+	_pos = _buffer;
+	_bufferEnd = buf;
+}
+
+void MusicInputStream::fadeUp() {
+	if (_fading > 0)
+		_fading = -_fading;
+	else if (_fading == 0)
+		_fading = -1;
+}
+
+void MusicInputStream::fadeDown() {
+	if (_fading < 0)
+		_fading = -_fading;
+	else if (_fading == 0)
+		_fading = _fadeSamples;
+}
+
+bool MusicInputStream::readyToRemove() {
+	return _remove;
+}
+
+int32 MusicInputStream::getTimeRemaining() {
+	// This is far from exact, but it doesn't have to be.
+	return (_samplesLeft + BUFFER_SIZE) / getRate();
+}
+
+// ----------------------------------------------------------------------------
+// Main sound class
+// ----------------------------------------------------------------------------
+
+// AudioStream API
+
+int Sound::readBuffer(int16 *buffer, const int numSamples) {
+	Common::StackLock lock(_mutex);
+	int i;
+
+	if (_musicPaused)
+		return 0;
+
+	for (i = 0; i < MAXMUS; i++) {
+		if (_music[i] && _music[i]->readyToRemove()) {
+			delete _music[i];
+			_music[i] = NULL;
+		}
+	}
+
+	memset(buffer, 0, 2 * numSamples);
+
+	if (!_mixBuffer || numSamples > _mixBufferLen) {
+		if (_mixBuffer)
+			_mixBuffer = (int16 *)realloc(_mixBuffer, 2 * numSamples);
+		else
+			_mixBuffer = (int16 *)malloc(2 * numSamples);
+
+		_mixBufferLen = numSamples;
+	}
+
+	if (!_mixBuffer)
+		return 0;
+
+	for (i = 0; i < MAXMUS; i++) {
+		if (!_music[i])
+			continue;
+
+		int len = _music[i]->readBuffer(_mixBuffer, numSamples);
+
+		if (!_musicMuted) {
+			for (int j = 0; j < len; j++) {
+				Audio::clampedAdd(buffer[j], _mixBuffer[j]);
+			}
+		}
+	}
+
+	bool inUse[MAXMUS];
+
+	for (i = 0; i < MAXMUS; i++)
+		inUse[i] = false;
+
+	for (i = 0; i < MAXMUS; i++) {
+		if (_music[i]) {
+			if (_music[i]->getCD() == 1)
+				inUse[0] = true;
+			else
+				inUse[1] = true;
+		}
+	}
+
+	for (i = 0; i < MAXMUS; i++) {
+		if (!inUse[i] && !_musicFile[i].inUse && _musicFile[i].file.isOpen())
+			_musicFile[i].file.close();
+	}
+
+	return numSamples;
+}
+
+bool Sound::endOfData() const {
+	for (int i = 0; i < MAXMUS; i++) {
+		if (_musicFile[i].file.isOpen())
+			return false;
+	}
+
+	return true;
+}
+
+// ----------------------------------------------------------------------------
+// MUSIC
+// ----------------------------------------------------------------------------
+
+/**
+ * Stops the music dead in its tracks. Any music that is currently being
+ * streamed is paused.
+ */
+
+void Sound::pauseMusic() {
+	Common::StackLock lock(_mutex);
+
+	_musicPaused = true;
+}
+
+/**
+ * Restarts the music from where it was stopped.
+ */
+
+void Sound::unpauseMusic() {
+	Common::StackLock lock(_mutex);
+
+	_musicPaused = false;
+}
+
+/**
+ * Fades out and stops the music.
+ */
+
+void Sound::stopMusic(bool immediately) {
+	Common::StackLock lock(_mutex);
+
+	_loopingMusicId = 0;
+
+	for (int i = 0; i < MAXMUS; i++) {
+		if (_music[i]) {
+			if (immediately) {
+				delete _music[i];
+				_music[i] = NULL;
+			} else
+				_music[i]->fadeDown();
+		}
+	}
+}
+
+/**
+ * Streams music from a cluster file.
+ * @param musicId the id of the music to stream
+ * @param loop true if the music is to loop back to the start
+ * @return RD_OK or an error code
+ */
+int32 Sound::streamCompMusic(uint32 musicId, bool loop) {
+	//Common::StackLock lock(_mutex);
+
+	_mutex.lock();
+	int cd = _vm->_resman->getCD();
+
+	if (loop)
+		_loopingMusicId = musicId;
+	else
+		_loopingMusicId = 0;
+
+	int primary = -1;
+	int secondary = -1;
+
+	// If both music streams are active, one of them will have to go.
+
+	if (_music[0] && _music[1]) {
+		int32 fade0 = _music[0]->isFading();
+		int32 fade1 = _music[1]->isFading();
+
+		if (!fade0 && !fade1) {
+			// Neither is fading. This shouldn't happen, so just
+			// pick one and be done with it.
+			primary = 0;
+		} else if (fade0 && !fade1) {
+			// Stream 0 is fading, so pick that one.
+			primary = 0;
+		} else if (!fade0 && fade1) {
+			// Stream 1 is fading, so pick that one.
+			primary = 1;
+		} else {
+			// Both streams are fading. Pick the one that is
+			// closest to silent.
+			if (ABS(fade0) < ABS(fade1))
+				primary = 0;
+			else
+				primary = 1;
+		}
+
+		delete _music[primary];
+		_music[primary] = NULL;
+	}
+
+	// Pick the available music stream. If no music is playing it doesn't
+	// matter which we use.
+
+	if (_music[0] || _music[1]) {
+		if (_music[0]) {
+			primary = 1;
+			secondary = 0;
+		} else {
+			primary = 0;
+			secondary = 1;
+		}
+	} else
+		primary = 0;
+
+	// Don't start streaming if the volume is off.
+	if (isMusicMute()) {
+		_mutex.unlock();
+		return RD_OK;
+	}
+
+	if (secondary != -1)
+		_music[secondary]->fadeDown();
+	SoundFileHandle *fh = (cd == 1) ? &_musicFile[0] : &_musicFile[1];
+	fh->inUse = true;
+	_mutex.unlock();
+
+	MusicInputStream *tmp = new MusicInputStream(cd, fh, musicId, loop);
+
+	if (tmp->isReady()) {
+		_mutex.lock();
+		_music[primary] = tmp;
+		fh->inUse = false;
+		_mutex.unlock();
+		return RD_OK;
+	} else {
+		_mutex.lock();
+		fh->inUse = false;
+		_mutex.unlock();
+		delete tmp;
+		return RDERR_INVALIDFILENAME;
+	}
+}
+
+/**
+ * @return the time left for the current music, in seconds.
+ */
+
+int32 Sound::musicTimeRemaining() {
+	Common::StackLock lock(_mutex);
+
+	for (int i = 0; i < MAXMUS; i++) {
+		if (_music[i] && _music[i]->isFading() <= 0)
+			return _music[i]->getTimeRemaining();
+	}
+
+	return 0;
+}
+
+// ----------------------------------------------------------------------------
+// SPEECH
+// ----------------------------------------------------------------------------
+
+/**
+ * Mutes/Unmutes the speech.
+ * @param mute If mute is false, restore the volume to the last set master
+ * level. Otherwise the speech is muted (volume 0).
+ */
+
+void Sound::muteSpeech(bool mute) {
+	_speechMuted = mute;
+
+	if (_vm->_mixer->isSoundHandleActive(_soundHandleSpeech)) {
+		uint volume = mute ? 0 : Audio::Mixer::kMaxChannelVolume;
+
+		_vm->_mixer->setChannelVolume(_soundHandleSpeech, volume);
+	}
+}
+
+/**
+ * Stops the speech dead in its tracks.
+ */
+
+void Sound::pauseSpeech() {
+	_speechPaused = true;
+	_vm->_mixer->pauseHandle(_soundHandleSpeech, true);
+}
+
+/**
+ * Restarts the speech from where it was stopped.
+ */
+
+void Sound::unpauseSpeech() {
+	_speechPaused = false;
+	_vm->_mixer->pauseHandle(_soundHandleSpeech, false);
+}
+
+/**
+ * Stops the speech from playing.
+ */
+
+int32 Sound::stopSpeech() {
+	if (_vm->_mixer->isSoundHandleActive(_soundHandleSpeech)) {
+		_vm->_mixer->stopHandle(_soundHandleSpeech);
+		return RD_OK;
+	}
+
+	return RDERR_SPEECHNOTPLAYING;
+}
+
+/**
+ * @return Either RDSE_SAMPLEPLAYING or RDSE_SAMPLEFINISHED
+ */
+
+int32 Sound::getSpeechStatus() {
+	return _vm->_mixer->isSoundHandleActive(_soundHandleSpeech) ? RDSE_SAMPLEPLAYING : RDSE_SAMPLEFINISHED;
+}
+
+/**
+ * Returns either RDSE_QUIET or RDSE_SPEAKING
+ */
+
+int32 Sound::amISpeaking() {
+	if (!_speechMuted && !_speechPaused && _vm->_mixer->isSoundHandleActive(_soundHandleSpeech))
+		return RDSE_SPEAKING;
+
+	return RDSE_QUIET;
+}
+
+/**
+ * This function loads and decompresses a list of speech from a cluster, but
+ * does not play it. This is used for cutscene voice-overs, presumably to
+ * avoid having to read from more than one file on the CD during playback.
+ * @param speechId the text line id used to reference the speech
+ * @param buf a pointer to the buffer that will be allocated for the sound
+ */
+
+uint32 Sound::preFetchCompSpeech(uint32 speechId, uint16 **buf) {
+	int cd = _vm->_resman->getCD();
+	uint32 numSamples;
+
+	SoundFileHandle *fh = (cd == 1) ? &_speechFile[0] : &_speechFile[1];
+
+	AudioStream *input = getAudioStream(fh, "speech", cd, speechId, &numSamples);
+
+	if (!input)
+		return 0;
+
+	*buf = NULL;
+
+	// Decompress data into speech buffer.
+
+	uint32 bufferSize = 2 * numSamples;
+
+	*buf = (uint16 *)malloc(bufferSize);
+	if (!*buf) {
+		delete input;
+		fh->file.close();
+		return 0;
+	}
+
+	uint32 readSamples = input->readBuffer((int16 *)*buf, numSamples);
+
+	fh->file.close();
+	delete input;
+
+	return 2 * readSamples;
+}
+
+/**
+ * This function loads, decompresses and plays a line of speech. An error
+ * occurs if speech is already playing.
+ * @param speechId the text line id used to reference the speech
+ * @param vol volume, 0 (minimum) to 16 (maximum)
+ * @param pan panning, -16 (full left) to 16 (full right)
+ */
+
+int32 Sound::playCompSpeech(uint32 speechId, uint8 vol, int8 pan) {
+	if (_speechMuted)
+		return RD_OK;
+
+	if (getSpeechStatus() == RDERR_SPEECHPLAYING)
+		return RDERR_SPEECHPLAYING;
+
+	int cd = _vm->_resman->getCD();
+	SoundFileHandle *fh = (cd == 1) ? &_speechFile[0] : &_speechFile[1];
+
+	AudioStream *input = getAudioStream(fh, "speech", cd, speechId, NULL);
+
+	if (!input)
+		return RDERR_INVALIDID;
+
+	// Modify the volume according to the master volume
+
+	byte volume = _speechMuted ? 0 : vol * Audio::Mixer::kMaxChannelVolume / 16;
+	int8 p = (pan * 127) / 16;
+
+	if (isReverseStereo())
+		p = -p;
+
+	// Start the speech playing
+	_vm->_mixer->playInputStream(Audio::Mixer::kSpeechSoundType, &_soundHandleSpeech, input, -1, volume, p);
+	return RD_OK;
+}
+
+// ----------------------------------------------------------------------------
+// SOUND EFFECTS
+// ----------------------------------------------------------------------------
+
+/**
+ * Mutes/Unmutes the sound effects.
+ * @param mute If mute is false, restore the volume to the last set master
+ * level. Otherwise the sound effects are muted (volume 0).
+ */
+
+void Sound::muteFx(bool mute) {
+	_fxMuted = mute;
+
+	// Now update the volume of any fxs playing
+	for (int i = 0; i < FXQ_LENGTH; i++) {
+		if (_fxQueue[i].resource) {
+			_vm->_mixer->setChannelVolume(_fxQueue[i].handle, mute ? 0 : _fxQueue[i].volume);
+		}
+	}
+}
+
+/**
+ * Sets the volume and pan of the sample which is currently playing
+ * @param id the id of the sample
+ * @param vol volume
+ * @param pan panning
+ */
+
+int32 Sound::setFxIdVolumePan(int32 id, int vol, int pan) {
+	if (!_fxQueue[id].resource)
+		return RDERR_FXNOTOPEN;
+
+	if (vol > 16)
+		vol = 16;
+
+	_fxQueue[id].volume = (vol * Audio::Mixer::kMaxChannelVolume) / 16;
+
+	if (pan != 255) {
+		if (isReverseStereo())
+			pan = -pan;
+		_fxQueue[id].pan = (pan * 127) / 16;
+	}
+
+	if (!_fxMuted && _vm->_mixer->isSoundHandleActive(_fxQueue[id].handle)) {
+		_vm->_mixer->setChannelVolume(_fxQueue[id].handle, _fxQueue[id].volume);
+		if (pan != -1)
+			_vm->_mixer->setChannelBalance(_fxQueue[id].handle, _fxQueue[id].pan);
+	}
+
+	return RD_OK;
+}
+
+void Sound::pauseFx() {
+	if (_fxPaused)
+		return;
+
+	for (int i = 0; i < FXQ_LENGTH; i++) {
+		if (_fxQueue[i].resource)
+			_vm->_mixer->pauseHandle(_fxQueue[i].handle, true);
+	}
+
+	_fxPaused = true;
+}
+
+void Sound::unpauseFx() {
+	if (!_fxPaused)
+		return;
+
+	for (int i = 0; i < FXQ_LENGTH; i++)
+		if (_fxQueue[i].resource)
+			_vm->_mixer->pauseHandle(_fxQueue[i].handle, false);
+
+	_fxPaused = false;
+}
+
+} // End of namespace Sword2

Copied: scummvm/trunk/sword2/palette.cpp (from rev 20444, scummvm/trunk/sword2/driver/palette.cpp)
===================================================================
--- scummvm/trunk/sword2/palette.cpp	                        (rev 0)
+++ scummvm/trunk/sword2/palette.cpp	2006-02-09 15:41:23 UTC (rev 20445)
@@ -0,0 +1,267 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * 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/stdafx.h"
+#include "common/system.h"
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/logic.h"
+#include "sword2/resman.h"
+
+namespace Sword2 {
+
+/**
+ * Start layer palette fading up
+ */
+
+void Screen::startNewPalette() {
+	// If the screen is still fading down then wait for black - could
+	// happen when everythings cached into a large memory model
+	waitForFade();
+
+	byte *screenFile = _vm->_resman->openResource(_thisScreen.background_layer_id);
+
+	memcpy(_paletteMatch, _vm->fetchPaletteMatchTable(screenFile), PALTABLESIZE);
+	setPalette(0, 256, _vm->fetchPalette(screenFile), RDPAL_FADE);
+
+	// Indicating that it's a screen palette
+	_lastPaletteRes = 0;
+
+	_vm->_resman->closeResource(_thisScreen.background_layer_id);
+	fadeUp();
+ 	_thisScreen.new_palette = 0;
+}
+
+void Screen::setFullPalette(int32 palRes) {
+	// fudge for hut interior
+	// - unpausing should restore last palette as normal (could be screen
+	// palette or 'dark_palette_13')
+	// - but restoring the screen palette after 'dark_palette_13' should
+	// now work properly too!
+
+	// "Hut interior" refers to the watchman's hut in Marseille, and this
+	// is apparently needed for the palette to be restored properly when
+	// you turn the light off. (I didn't even notice the light switch!)
+
+	if (_vm->_logic->readVar(LOCATION) == 13) {
+		// unpausing
+		if (palRes == -1) {
+			// restore whatever palette was last set (screen
+			// palette or 'dark_palette_13')
+			palRes = _lastPaletteRes;
+		}
+	} else {
+		// check if we're just restoring the current screen palette
+		// because we might actually need to use a separate palette
+		// file anyway eg. for pausing & unpausing during the eclipse
+
+		// unpausing (fudged for location 13)
+ 		if (palRes == -1) {
+			// we really meant '0'
+			palRes = 0;
+		}
+
+		if (palRes == 0 && _lastPaletteRes)
+			palRes = _lastPaletteRes;
+	}
+
+	// If non-zero, set palette to this separate palette file. Otherwise,
+	// set palette to current screen palette.
+
+	if (palRes) {
+		byte *pal = _vm->_resman->openResource(palRes);
+
+		assert(_vm->_resman->fetchType(pal) == PALETTE_FILE);
+
+		pal += ResHeader::size();
+
+		// always set colour 0 to black because most background screen
+		// palettes have a bright colour 0 although it should come out
+		// as black in the game!
+
+		pal[0] = 0;
+		pal[1] = 0;
+		pal[2] = 0;
+		pal[3] = 0;
+
+		setPalette(0, 256, pal, RDPAL_INSTANT);
+		_vm->_resman->closeResource(palRes);
+	} else {
+		if (_thisScreen.background_layer_id) {
+			byte *data = _vm->_resman->openResource(_thisScreen.background_layer_id);
+			memcpy(_paletteMatch, _vm->fetchPaletteMatchTable(data), PALTABLESIZE);
+			setPalette(0, 256, _vm->fetchPalette(data), RDPAL_INSTANT);
+			_vm->_resman->closeResource(_thisScreen.background_layer_id);
+		} else
+			error("setFullPalette(0) called, but no current screen available!");
+	}
+
+	if (palRes != CONTROL_PANEL_PALETTE)
+		_lastPaletteRes = palRes;
+}
+
+/**
+ * Matches a colour triplet to a palette index.
+ * @param r red colour component
+ * @param g green colour component
+ * @param b blue colour component
+ * @return the palette index of the closest matching colour in the palette
+ */
+
+// FIXME: This used to be inlined - probably a good idea - but the
+// linker complained when I tried to use it in sprite.cpp.
+
+uint8 Screen::quickMatch(uint8 r, uint8 g, uint8 b) {
+	return _paletteMatch[((int32)(r >> 2) << 12) + ((int32)(g >> 2) << 6) + (b >> 2)];
+}
+
+/**
+ * Sets the palette.
+ * @param startEntry the first colour entry to set
+ * @param noEntries the number of colour entries to set
+ * @param colourTable the new colour entries
+ * @param fadeNow whether to perform the change immediately or delayed
+ */
+
+void Screen::setPalette(int16 startEntry, int16 noEntries, byte *colourTable, uint8 fadeNow) {
+	assert(noEntries > 0);
+
+	memcpy(&_palette[4 * startEntry], colourTable, noEntries * 4);
+
+	if (fadeNow == RDPAL_INSTANT) {
+		_vm->_system->setPalette(_palette, startEntry, noEntries);
+		setNeedFullRedraw();
+	}
+}
+
+void Screen::dimPalette() {
+	byte *p = _palette;
+
+	for (int i = 0; i < 256; i++) {
+		p[i * 4 + 0] /= 2;
+		p[i * 4 + 1] /= 2;
+		p[i * 4 + 2] /= 2;
+	}
+
+	_vm->_system->setPalette(p, 0, 256);
+	setNeedFullRedraw();
+}
+
+/**
+ * Fades the palette up from black to the current palette.
+ * @param time the time it will take the palette to fade up
+ */
+
+int32 Screen::fadeUp(float time) {
+	if (getFadeStatus() != RDFADE_BLACK && getFadeStatus() != RDFADE_NONE)
+		return RDERR_FADEINCOMPLETE;
+
+	_fadeTotalTime = (int32)(time * 1000);
+	_fadeStatus = RDFADE_UP;
+	_fadeStartTime = _vm->_system->getMillis();
+
+	return RD_OK;
+}
+
+/**
+ * Fades the palette down to black from the current palette.
+ * @param time the time it will take the palette to fade down
+ */
+
+int32 Screen::fadeDown(float time) {
+	if (getFadeStatus() != RDFADE_BLACK && getFadeStatus() != RDFADE_NONE)
+		return RDERR_FADEINCOMPLETE;
+
+	_fadeTotalTime = (int32)(time * 1000);
+	_fadeStatus = RDFADE_DOWN;
+	_fadeStartTime = _vm->_system->getMillis();
+
+	return RD_OK;
+}
+
+/**
+ * Get the current fade status
+ * @return RDFADE_UP (fading up), RDFADE_DOWN (fading down), RDFADE_NONE
+ * (not faded), or RDFADE_BLACK (completely faded down)
+ */
+
+uint8 Screen::getFadeStatus() {
+	return _fadeStatus;
+}
+
+void Screen::waitForFade() {
+	while (getFadeStatus() != RDFADE_NONE && getFadeStatus() != RDFADE_BLACK && !_vm->_quit) {
+		updateDisplay();
+		_vm->_system->delayMillis(20);
+	}
+}
+
+void Screen::fadeServer() {
+	static int32 previousTime = 0;
+	byte fadePalette[256 * 4];
+	byte *newPalette = fadePalette;
+	int32 currentTime;
+	int16 fadeMultiplier;
+	int16 i;
+
+	// If we're not in the process of fading, do nothing.
+	if (getFadeStatus() != RDFADE_UP && getFadeStatus() != RDFADE_DOWN)
+		return;
+
+	// I don't know if this is necessary, but let's limit how often the
+	// palette is updated, just to be safe.
+	currentTime = _vm->_system->getMillis();
+	if (currentTime - previousTime <= 25)
+		return;
+
+	previousTime = currentTime;
+
+	if (getFadeStatus() == RDFADE_UP) {
+		if (currentTime >= _fadeStartTime + _fadeTotalTime) {
+			_fadeStatus = RDFADE_NONE;
+			newPalette = _palette;
+		} else {
+			fadeMultiplier = (int16)(((int32)(currentTime - _fadeStartTime) * 256) / _fadeTotalTime);
+			for (i = 0; i < 256; i++) {
+				newPalette[i * 4 + 0] = (_palette[i * 4 + 0] * fadeMultiplier) >> 8;
+				newPalette[i * 4 + 1] = (_palette[i * 4 + 1] * fadeMultiplier) >> 8;
+				newPalette[i * 4 + 2] = (_palette[i * 4 + 2] * fadeMultiplier) >> 8;
+			}
+		}
+	} else {
+		if (currentTime >= _fadeStartTime + _fadeTotalTime) {
+			_fadeStatus = RDFADE_BLACK;
+			memset(newPalette, 0, sizeof(fadePalette));
+		} else {
+			fadeMultiplier = (int16)(((int32)(_fadeTotalTime - (currentTime - _fadeStartTime)) * 256) / _fadeTotalTime);
+			for (i = 0; i < 256; i++) {
+				newPalette[i * 4 + 0] = (_palette[i * 4 + 0] * fadeMultiplier) >> 8;
+				newPalette[i * 4 + 1] = (_palette[i * 4 + 1] * fadeMultiplier) >> 8;
+				newPalette[i * 4 + 2] = (_palette[i * 4 + 2] * fadeMultiplier) >> 8;
+			}
+		}
+	}
+
+	_vm->_system->setPalette(newPalette, 0, 256);
+	setNeedFullRedraw();
+}
+
+} // End of namespace Sword2

Copied: scummvm/trunk/sword2/rdwin.cpp (from rev 20444, scummvm/trunk/sword2/driver/rdwin.cpp)
===================================================================
--- scummvm/trunk/sword2/rdwin.cpp	                        (rev 0)
+++ scummvm/trunk/sword2/rdwin.cpp	2006-02-09 15:41:23 UTC (rev 20445)
@@ -0,0 +1,118 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * 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/stdafx.h"
+#include "common/system.h"
+#include "sword2/sword2.h"
+
+namespace Sword2 {
+
+/**
+ * Tell updateDisplay() that the scene needs to be completely updated.
+ */
+
+void Screen::setNeedFullRedraw() {
+	_needFullRedraw = true;
+}
+
+/**
+ * Mark an area of the screen as dirty, first generation.
+ */
+
+void Screen::markAsDirty(int16 x0, int16 y0, int16 x1, int16 y1) {
+	int16 gridX0 = x0 / CELLWIDE;
+	int16 gridY0 = y0 / CELLDEEP;
+	int16 gridX1 = x1 / CELLWIDE;
+	int16 gridY1 = y1 / CELLDEEP;
+
+	for (int16 i = gridY0; i <= gridY1; i++)
+		for (int16 j = gridX0; j <= gridX1; j++)
+			_dirtyGrid[i * _gridWide + j] = 2;
+}
+
+/**
+ * This function has two purposes: It redraws the scene, and it handles input
+ * events, palette fading, etc. It should be called at a high rate (> 20 per
+ * second), but the scene is usually only redrawn about 12 times per second,
+ * except when then screen is scrolling.
+ *
+ * @param redrawScene If true, redraw the scene.
+ */
+
+void Screen::updateDisplay(bool redrawScene) {
+	_vm->parseInputEvents();
+	fadeServer();
+
+	if (redrawScene) {
+		int i;
+
+		// Note that the entire scene is always rendered, which is less
+		// than optimal, but at least we can try to be intelligent
+		// about updating the screen afterwards.
+
+		if (_needFullRedraw) {
+			// Update the entire screen. This is necessary when
+			// scrolling, fading, etc.
+
+			_vm->_system->copyRectToScreen(_buffer + MENUDEEP * _screenWide, _screenWide, 0, MENUDEEP, _screenWide, _screenDeep - 2 * MENUDEEP);
+			_needFullRedraw = false;
+		} else {
+			// Update only the dirty areas of the screen
+
+			int j, x, y;
+			int stripWide;
+
+			for (i = 0; i < _gridDeep; i++) {
+				stripWide = 0;
+
+				for (j = 0; j < _gridWide; j++) {
+					if (_dirtyGrid[i * _gridWide + j]) {
+						stripWide++;
+					} else if (stripWide) {
+						x = CELLWIDE * (j - stripWide);
+						y = CELLDEEP * i;
+						_vm->_system->copyRectToScreen(_buffer + y * _screenWide + x, _screenWide, x, y, stripWide * CELLWIDE, CELLDEEP);
+						stripWide = 0;
+					}
+				}
+
+				if (stripWide) {
+					x = CELLWIDE * (j - stripWide);
+					y = CELLDEEP * i;
+					_vm->_system->copyRectToScreen(_buffer + y * _screenWide + x, _screenWide, x, y, stripWide * CELLWIDE, CELLDEEP);
+					stripWide = 0;
+				}
+			}
+		}
+
+		// Age the dirty cells one generation. This way we keep track
+		// of both the cells that were updated this time, and the ones
+		// that were updated the last time.
+
+		for (i = 0; i < _gridWide * _gridDeep; i++)
+			_dirtyGrid[i] >>= 1;
+	}
+
+	// We always need to update because of fades, menu animations, etc.
+	_vm->_system->updateScreen();
+}
+
+} // End of namespace Sword2

Copied: scummvm/trunk/sword2/render.cpp (from rev 20444, scummvm/trunk/sword2/driver/render.cpp)
===================================================================
--- scummvm/trunk/sword2/render.cpp	                        (rev 0)
+++ scummvm/trunk/sword2/render.cpp	2006-02-09 15:41:23 UTC (rev 20445)
@@ -0,0 +1,584 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * 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/stdafx.h"
+#include "common/system.h"
+
+#include "graphics/primitives.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/build_display.h"
+
+#ifdef BACKEND_8BIT
+#include "sword2/animation.h"
+#endif
+
+namespace Sword2 {
+
+#define MILLISECSPERCYCLE  83
+#define RENDERAVERAGETOTAL 4
+
+void Screen::updateRect(Common::Rect *r) {
+	_vm->_system->copyRectToScreen(_buffer + r->top * _screenWide + r->left,
+		_screenWide, r->left, r->top, r->right - r->left,
+		r->bottom - r->top);
+}
+
+void Screen::blitBlockSurface(BlockSurface *s, Common::Rect *r, Common::Rect *clipRect) {
+	if (!r->intersects(*clipRect))
+		return;
+
+	byte *src = s->data;
+
+	if (r->top < clipRect->top) {
+		src -= BLOCKWIDTH * (r->top - clipRect->top);
+		r->top = clipRect->top;
+	}
+	if (r->left < clipRect->left) {
+		src -= (r->left - clipRect->left);
+		r->left = clipRect->left;
+	}
+	if (r->bottom > clipRect->bottom)
+		r->bottom = clipRect->bottom;
+	if (r->right > clipRect->right)
+		r->right = clipRect->right;
+
+	byte *dst = _buffer + r->top * _screenWide + r->left;
+	int i;
+
+	if (s->transparent) {
+		for (i = 0; i < r->bottom - r->top; i++) {
+			for (int j = 0; j < r->right - r->left; j++) {
+				if (src[j])
+					dst[j] = src[j];
+			}
+			src += BLOCKWIDTH;
+			dst += _screenWide;
+		}
+	} else {
+		for (i = 0; i < r->bottom - r->top; i++) {
+			memcpy(dst, src, r->right - r->left);
+			src += BLOCKWIDTH;
+			dst += _screenWide;
+		}
+	}
+}
+
+// There are two different separate functions for scaling the image - one fast
+// and one good. Or at least that's the theory. I'm sure there are better ways
+// to scale an image than this. The latter is used at the highest graphics
+// quality setting. Note that the "good" scaler takes an extra parameter, a
+// pointer to the area of the screen where the sprite will be drawn.
+//
+// This code isn't quite like the original DrawSprite(), but should be close
+// enough.
+
+void Screen::scaleImageFast(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight) {
+	int x, y;
+
+	for (x = 0; x < dstWidth; x++)
+		_xScale[x] = (x * srcWidth) / dstWidth;
+
+	for (y = 0; y < dstHeight; y++)
+		_yScale[y] = (y * srcHeight) / dstHeight;
+
+	for (y = 0; y < dstHeight; y++) {
+		for (x = 0; x < dstWidth; x++) {
+			dst[x] = src[_yScale[y] * srcPitch + _xScale[x]];
+		}
+		dst += dstPitch;
+	}
+}
+
+void Screen::scaleImageGood(byte *dst, uint16 dstPitch, uint16 dstWidth, uint16 dstHeight, byte *src, uint16 srcPitch, uint16 srcWidth, uint16 srcHeight, byte *backbuf) {
+	for (int y = 0; y < dstHeight; y++) {
+		for (int x = 0; x < dstWidth; x++) {
+			uint8 c1, c2, c3, c4;
+
+			uint32 xPos = (x * srcWidth) / dstWidth;
+			uint32 yPos = (y * srcHeight) / dstHeight;
+			uint32 xFrac = dstWidth - (x * srcWidth) % dstWidth;
+			uint32 yFrac = dstHeight - (y * srcHeight) % dstHeight;
+
+			byte *srcPtr = src + yPos * srcPitch + xPos;
+			byte *backPtr = backbuf + y * _screenWide + x;
+
+			bool transparent = true;
+
+			if (*srcPtr) {
+				c1 = *srcPtr;
+				transparent = false;
+			} else
+				c1 = *backPtr;
+
+			if (x < dstWidth - 1) {
+				if (*(srcPtr + 1)) {
+					c2 = *(srcPtr + 1);
+					transparent = false;
+				} else
+					c2 = *(backPtr + 1);
+			} else
+				c2 = c1;
+
+			if (y < dstHeight - 1) {
+				if (*(srcPtr + srcPitch)) {
+					c3 = *(srcPtr + srcPitch);
+					transparent = false;
+				} else
+					c3 = *(backPtr + _screenWide);
+			} else
+				c3 = c1;
+
+			if (x < dstWidth - 1 && y < dstHeight - 1) {
+				if (*(srcPtr + srcPitch + 1)) {
+					c4 = *(srcPtr + srcPitch + 1);
+					transparent = false;
+				} else
+					c4 = *(backPtr + _screenWide + 1);
+			} else
+				c4 = c3;
+
+			if (!transparent) {
+				uint32 r1 = _palette[c1 * 4 + 0];
+				uint32 g1 = _palette[c1 * 4 + 1];
+				uint32 b1 = _palette[c1 * 4 + 2];
+
+				uint32 r2 = _palette[c2 * 4 + 0];
+				uint32 g2 = _palette[c2 * 4 + 1];
+				uint32 b2 = _palette[c2 * 4 + 2];
+
+				uint32 r3 = _palette[c3 * 4 + 0];
+				uint32 g3 = _palette[c3 * 4 + 1];
+				uint32 b3 = _palette[c3 * 4 + 2];
+
+				uint32 r4 = _palette[c4 * 4 + 0];
+				uint32 g4 = _palette[c4 * 4 + 1];
+				uint32 b4 = _palette[c4 * 4 + 2];
+
+				uint32 r5 = (r1 * xFrac + r2 * (dstWidth - xFrac)) / dstWidth;
+				uint32 g5 = (g1 * xFrac + g2 * (dstWidth - xFrac)) / dstWidth;
+				uint32 b5 = (b1 * xFrac + b2 * (dstWidth - xFrac)) / dstWidth;
+
+				uint32 r6 = (r3 * xFrac + r4 * (dstWidth - xFrac)) / dstWidth;
+				uint32 g6 = (g3 * xFrac + g4 * (dstWidth - xFrac)) / dstWidth;
+				uint32 b6 = (b3 * xFrac + b4 * (dstWidth - xFrac)) / dstWidth;
+
+				uint32 r = (r5 * yFrac + r6 * (dstHeight - yFrac)) / dstHeight;
+				uint32 g = (g5 * yFrac + g6 * (dstHeight - yFrac)) / dstHeight;
+				uint32 b = (b5 * yFrac + b6 * (dstHeight - yFrac)) / dstHeight;
+
+				dst[y * dstWidth + x] = quickMatch(r, g, b);
+			} else
+				dst[y * dstWidth + x] = 0;
+		}
+	}
+}
+
+/**
+ * Plots a point relative to the top left corner of the screen. This is only
+ * used for debugging.
+ * @param x x-coordinate of the point
+ * @param y y-coordinate of the point
+ * @param colour colour of the point
+ */
+
+void Screen::plotPoint(int x, int y, uint8 colour) {
+	byte *buf = _buffer + MENUDEEP * RENDERWIDE;
+
+	x -= _scrollX;
+	y -= _scrollY;
+
+	if (x >= 0 && x < RENDERWIDE && y >= 0 && y < RENDERDEEP) {
+		buf[y * RENDERWIDE + x] = colour;
+		markAsDirty(x, y + MENUDEEP, x, y + MENUDEEP);
+	}
+}
+
+static void plot(int x, int y, int colour, void *data) {
+	Screen *screen = (Screen *)data;
+	screen->plotPoint(x, y, (uint8) colour);
+}
+
+/**
+ * Draws a line from one point to another. This is only used for debugging.
+ * @param x0 x-coordinate of the start point
+ * @param y0 y-coordinate of the start point
+ * @param x1 x-coordinate of the end point
+ * @param y1 y-coordinate of the end point
+ * @param colour colour of the line
+ */
+
+void Screen::drawLine(int x0, int y0, int x1, int y1, uint8 colour) {
+	Graphics::drawLine(x0, y0, x1, y1, colour, &plot, this);
+}
+
+/**
+ * This function tells the driver the size of the background screen for the
+ * current location.
+ * @param w width of the current location
+ * @param h height of the current location
+ */
+
+void Screen::setLocationMetrics(uint16 w, uint16 h) {
+	_locationWide = w;
+	_locationDeep = h;
+	setNeedFullRedraw();
+}
+
+/**
+ * Draws a parallax layer at the current position determined by the scroll. A
+ * parallax can be either foreground, background or the main screen.
+ */
+
+void Screen::renderParallax(byte *ptr, int16 l) {
+	Parallax p;
+	int16 x, y;
+	Common::Rect r;
+
+	p.read(ptr);
+
+	if (_locationWide == _screenWide)
+		x = 0;
+	else
+		x = ((int32)((p.w - _screenWide) * _scrollX) / (int32)(_locationWide - _screenWide));
+
+	if (_locationDeep == _screenDeep - MENUDEEP * 2)
+		y = 0;
+	else
+		y = ((int32)((p.h - (_screenDeep - MENUDEEP * 2)) * _scrollY) / (int32)(_locationDeep - (_screenDeep - MENUDEEP * 2)));
+
+	Common::Rect clipRect;
+
+	// Leave enough space for the top and bottom menues
+
+	clipRect.left = 0;
+	clipRect.right = _screenWide;
+	clipRect.top = MENUDEEP;
+	clipRect.bottom = _screenDeep - MENUDEEP;
+
+	for (int j = 0; j < _yBlocks[l]; j++) {
+		for (int i = 0; i < _xBlocks[l]; i++) {
+			if (_blockSurfaces[l][i + j * _xBlocks[l]]) {
+				r.left = i * BLOCKWIDTH - x;
+				r.right = r.left + BLOCKWIDTH;
+				r.top = j * BLOCKHEIGHT - y + MENUDEEP;
+				r.bottom = r.top + BLOCKHEIGHT;
+				blitBlockSurface(_blockSurfaces[l][i + j * _xBlocks[l]], &r, &clipRect);
+			}
+		}
+	}
+
+	_parallaxScrollX = _scrollX - x;
+	_parallaxScrollY = _scrollY - y;
+}
+
+// Uncomment this when benchmarking the drawing routines.
+#define LIMIT_FRAME_RATE
+
+/**
+ * Initialises the timers before the render loop is entered.
+ */
+
+void Screen::initialiseRenderCycle() {
+	_initialTime = _vm->_system->getMillis();
+	_totalTime = _initialTime + MILLISECSPERCYCLE;
+}
+
+/**
+ * This function should be called when the game engine is ready to start the
+ * render cycle.
+ */
+
+void Screen::startRenderCycle() {
+	_scrollXOld = _scrollX;
+	_scrollYOld = _scrollY;
+
+	_startTime = _vm->_system->getMillis();
+
+	if (_startTime + _renderAverageTime >= _totalTime)	{
+		_scrollX = _scrollXTarget;
+		_scrollY = _scrollYTarget;
+		_renderTooSlow = true;
+	} else {
+		_scrollX = (int16)(_scrollXOld + ((_scrollXTarget - _scrollXOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
+		_scrollY = (int16)(_scrollYOld + ((_scrollYTarget - _scrollYOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
+		_renderTooSlow = false;
+	}
+
+	if (_scrollXOld != _scrollX || _scrollYOld != _scrollY)
+		setNeedFullRedraw();
+
+	_framesPerGameCycle = 0;
+}
+
+/**
+ * This function should be called at the end of the render cycle.
+ * @return true if the render cycle is to be terminated,
+ *         or false if it should continue
+ */
+
+bool Screen::endRenderCycle() {
+	static int32 renderTimeLog[4] = { 60, 60, 60, 60 };
+	static int32 renderCountIndex = 0;
+	int32 time;
+
+	time = _vm->_system->getMillis();
+	renderTimeLog[renderCountIndex] = time - _startTime;
+	_startTime = time;
+	_renderAverageTime = (renderTimeLog[0] + renderTimeLog[1] + renderTimeLog[2] + renderTimeLog[3]) >> 2;
+
+	_framesPerGameCycle++;
+
+	if (++renderCountIndex == RENDERAVERAGETOTAL)
+		renderCountIndex = 0;
+
+	if (_renderTooSlow) {
+		initialiseRenderCycle();
+		return true;
+	}
+
+	if (_startTime + _renderAverageTime >= _totalTime) {
+		_totalTime += MILLISECSPERCYCLE;
+		_initialTime = time;
+		return true;
+	}
+
+#ifdef LIMIT_FRAME_RATE
+	if (_scrollXTarget == _scrollX && _scrollYTarget == _scrollY) {
+		// If we have already reached the scroll target sleep for the
+		// rest of the render cycle.
+		_vm->sleepUntil(_totalTime);
+		_initialTime = _vm->_system->getMillis();
+		_totalTime += MILLISECSPERCYCLE;
+		return true;
+	}
+#endif
+
+	// This is an attempt to ensure that we always reach the scroll target.
+	// Otherwise the game frequently tries to pump out new interpolation
+	// frames without ever getting anywhere.
+
+	if (ABS(_scrollX - _scrollXTarget) <= 1 && ABS(_scrollY - _scrollYTarget) <= 1) {
+		_scrollX = _scrollXTarget;
+		_scrollY = _scrollYTarget;
+	} else {
+		_scrollX = (int16)(_scrollXOld + ((_scrollXTarget - _scrollXOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
+		_scrollY = (int16)(_scrollYOld + ((_scrollYTarget - _scrollYOld) * (_startTime - _initialTime + _renderAverageTime)) / (_totalTime - _initialTime));
+	}
+
+	if (_scrollX != _scrollXOld || _scrollY != _scrollYOld)
+		setNeedFullRedraw();
+
+#ifdef LIMIT_FRAME_RATE
+	// Give the other threads some breathing space. This apparently helps
+	// against bug #875683, though I was never able to reproduce it for
+	// myself.
+	_vm->_system->delayMillis(10);
+#endif
+
+	return false;
+}
+
+/**
+ * Reset scrolling stuff. This function is called from initBackground()
+ */
+
+void Screen::resetRenderEngine() {
+	_parallaxScrollX = 0;
+	_parallaxScrollY = 0;
+	_scrollX = 0;
+	_scrollY = 0;
+}
+
+/**
+ * This function should be called five times with either the parallax layer
+ * or a NULL pointer in order of background parallax to foreground parallax.
+ */
+
+int32 Screen::initialiseBackgroundLayer(byte *parallax) {
+	Parallax p;
+	uint16 i, j, k;
+	byte *data;
+	byte *dst;
+
+	debug(2, "initialiseBackgroundLayer");
+
+	assert(_layer < MAXLAYERS);
+
+	if (!parallax) {
+		_layer++;
+		return RD_OK;
+	}
+
+	p.read(parallax);
+
+	_xBlocks[_layer] = (p.w + BLOCKWIDTH - 1) / BLOCKWIDTH;
+	_yBlocks[_layer] = (p.h + BLOCKHEIGHT - 1) / BLOCKHEIGHT;
+
+	_blockSurfaces[_layer] = (BlockSurface **)calloc(_xBlocks[_layer] * _yBlocks[_layer], sizeof(BlockSurface *));
+	if (!_blockSurfaces[_layer])
+		return RDERR_OUTOFMEMORY;
+
+	// Decode the parallax layer into a large chunk of memory
+
+	byte *memchunk = (byte *)calloc(_xBlocks[_layer] * _yBlocks[_layer], BLOCKWIDTH * BLOCKHEIGHT);
+	if (!memchunk)
+		return RDERR_OUTOFMEMORY;
+
+	for (i = 0; i < p.h; i++) {
+		uint32 p_offset = READ_LE_UINT32(parallax + Parallax::size() + 4 * i);
+
+		if (!p_offset)
+			continue;
+
+		byte *pLine = parallax + p_offset;
+		uint16 packets = READ_LE_UINT16(pLine);
+		uint16 offset = READ_LE_UINT16(pLine + 2);
+
+		data = pLine + 4;
+		dst = memchunk + i * p.w + offset;
+
+		if (!packets) {
+			memcpy(dst, data, p.w);
+			continue;
+		}
+
+		bool zeros = false;
+
+		for (j = 0; j < packets; j++) {
+			if (zeros) {
+				dst += *data;
+				offset += *data;
+				data++;
+				zeros = false;
+			} else if (!*data) {
+				data++;
+				zeros = true;
+			} else {
+				uint16 count = *data++;
+				memcpy(dst, data, count);
+				data += count;
+				dst += count;
+				offset += count;
+				zeros = true;
+			}
+		}
+	}
+
+	// The large memory chunk is now divided into a number of smaller
+	// surfaces. For most parallax layers, we'll end up using less memory
+	// this way, and it will be faster to draw since completely transparent
+	// surfaces are discarded.
+
+	for (i = 0; i < _xBlocks[_layer] * _yBlocks[_layer]; i++) {
+		bool block_has_data = false;
+		bool block_is_transparent = false;
+
+		int x = BLOCKWIDTH * (i % _xBlocks[_layer]);
+		int y = BLOCKHEIGHT * (i / _xBlocks[_layer]);
+
+		data = memchunk + p.w * y + x;
+
+		for (j = 0; j < BLOCKHEIGHT; j++) {
+			for (k = 0; k < BLOCKWIDTH; k++) {
+				if (x + k < p.w && y + j < p.h) {
+					if (data[j * p.w + k])
+						block_has_data = true;
+					else
+						block_is_transparent = true;
+				}
+			}
+		}
+
+		//  Only assign a surface to the block if it contains data.
+
+		if (block_has_data) {
+			_blockSurfaces[_layer][i] = (BlockSurface *)malloc(sizeof(BlockSurface));
+
+			//  Copy the data into the surfaces.
+			dst = _blockSurfaces[_layer][i]->data;
+			for (j = 0; j < BLOCKHEIGHT; j++) {
+				memcpy(dst, data, BLOCKWIDTH);
+				data += p.w;
+				dst += BLOCKWIDTH;
+			}
+
+			_blockSurfaces[_layer][i]->transparent = block_is_transparent;
+
+		} else
+			_blockSurfaces[_layer][i] = NULL;
+	}
+
+	free(memchunk);
+	_layer++;
+
+	return RD_OK;
+}
+
+/**
+ * Should be called once after leaving the room to free up memory.
+ */
+
+void Screen::closeBackgroundLayer() {
+	debug(2, "CloseBackgroundLayer");
+
+	for (int i = 0; i < MAXLAYERS; i++) {
+		if (_blockSurfaces[i]) {
+			for (int j = 0; j < _xBlocks[i] * _yBlocks[i]; j++)
+				if (_blockSurfaces[i][j])
+					free(_blockSurfaces[i][j]);
+			free(_blockSurfaces[i]);
+			_blockSurfaces[i] = NULL;
+		}
+	}
+
+	_layer = 0;
+}
+
+#ifdef BACKEND_8BIT
+void Screen::plotYUV(byte *lut, int width, int height, byte *const *dat) {
+	byte *buf = _buffer + ((480 - height) / 2) * RENDERWIDE + (640 - width) / 2;
+
+	int x, y;
+
+	int ypos = 0;
+	int cpos = 0;
+	int linepos = 0;
+
+	for (y = 0; y < height; y += 2) {
+		for (x = 0; x < width; x += 2) {
+			int i = ((((dat[2][cpos] + ROUNDADD) >> SHIFT) * (BITDEPTH + 1)) + ((dat[1][cpos] + ROUNDADD) >> SHIFT)) * (BITDEPTH + 1);
+			cpos++;
+
+			buf[linepos               ] = lut[i + ((dat[0][        ypos  ] + ROUNDADD) >> SHIFT)];
+			buf[RENDERWIDE + linepos++] = lut[i + ((dat[0][width + ypos++] + ROUNDADD) >> SHIFT)];
+			buf[linepos               ] = lut[i + ((dat[0][        ypos  ] + ROUNDADD) >> SHIFT)];
+			buf[RENDERWIDE + linepos++] = lut[i + ((dat[0][width + ypos++] + ROUNDADD) >> SHIFT)];
+		}
+		linepos += (2 * RENDERWIDE - width);
+		ypos += width;
+	}
+}
+#endif
+
+
+} // End of namespace Sword2

Copied: scummvm/trunk/sword2/sprite.cpp (from rev 20444, scummvm/trunk/sword2/driver/sprite.cpp)
===================================================================
--- scummvm/trunk/sword2/sprite.cpp	                        (rev 0)
+++ scummvm/trunk/sword2/sprite.cpp	2006-02-09 15:41:23 UTC (rev 20445)
@@ -0,0 +1,655 @@
+/* Copyright (C) 1994-1998 Revolution Software Ltd.
+ * Copyright (C) 2003-2006 The ScummVM project
+ *
+ * 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/stdafx.h"
+
+#include "sword2/sword2.h"
+#include "sword2/defs.h"
+#include "sword2/build_display.h"
+
+namespace Sword2 {
+
+/**
+ * This function takes a sprite and creates a mirror image of it.
+ * @param dst destination buffer
+ * @param src source buffer
+ * @param w width of the sprite
+ * @param h height of the sprite
+ */
+
+void Screen::mirrorSprite(byte *dst, byte *src, int16 w, int16 h) {
+	for (int y = 0; y < h; y++) {
+		for (int x = 0; x < w; x++) {
+			*dst++ = *(src + w - x - 1);
+		}
+		src += w;
+	}
+}
+
+/**
+ * This function takes a compressed frame of a sprite with up to 256 colours
+ * and decompresses it.
+ * @param dst destination buffer
+ * @param src source buffer
+ * @param decompSize the expected size of the decompressed sprite
+ */
+
+int32 Screen::decompressRLE256(byte *dst, byte *src, int32 decompSize) {
+	// PARAMETERS:
+	// source	points to the start of the sprite data for input
+	// decompSize	gives size of decompressed data in bytes
+	// dest		points to start of destination buffer for decompressed
+	// 		data
+
+	byte headerByte;			// block header byte
+	byte *endDest = dst + decompSize;	// pointer to byte after end of decomp buffer
+	int32 rv;
+
+	while (1) {
+		// FLAT block
+		// read FLAT block header & increment 'scan' to first pixel
+		// of block
+		headerByte = *src++;
+
+		// if this isn't a zero-length block
+		if (headerByte) {
+			if (dst + headerByte > endDest) {
+				rv = 1;
+				break;
+			}
+
+			// set the next 'headerByte' pixels to the next colour
+			// at 'source'
+			memset(dst, *src, headerByte);
+
+			// increment destination pointer to just after this
+			// block
+			dst += headerByte;
+
+			// increment source pointer to just after this colour
+			src++;
+
+			// if we've decompressed all of the data
+			if (dst == endDest) {
+				rv = 0;		// return "OK"
+				break;
+			}
+		}
+
+		// RAW block
+		// read RAW block header & increment 'scan' to first pixel of
+		// block
+		headerByte = *src++;
+
+		// if this isn't a zero-length block
+		if (headerByte) {
+			if (dst + headerByte > endDest) {
+				rv = 1;
+				break;
+			}
+
+			// copy the next 'headerByte' pixels from source to
+			// destination
+			memcpy(dst, src, headerByte);
+
+			// increment destination pointer to just after this
+			// block
+			dst += headerByte;
+
+			// increment source pointer to just after this block
+			src += headerByte;
+
+			// if we've decompressed all of the data
+			if (dst == endDest) {
+				rv = 0;		// return "OK"
+				break;
+			}
+		}
+	}
+
+	return rv;
+}
+
+/**
+ * Unwinds a run of 16-colour data into 256-colour palette data.
+ */
+
+void Screen::unwindRaw16(byte *dst, byte *src, uint8 blockSize, byte *colTable) {
+	// for each pair of pixels
+	while (blockSize > 1) {
+		// 1st colour = number in table at position given by upper
+		// nibble of source byte
+		*dst++ = colTable[(*src) >> 4];
+
+		// 2nd colour = number in table at position given by lower
+		// nibble of source byte
+		*dst++ = colTable[(*src) & 0x0f];
+
+		// point to next source byte
+		src++;
+
+		// decrement count of how many pixels left to read
+		blockSize -= 2;
+	}
+
+	// if there's a final odd pixel
+	if (blockSize) {
+		// colour = number in table at position given by upper nibble
+		// of source byte
+		*dst++ = colTable[(*src) >> 4];
+	}
+}
+
+/**
+ * This function takes a compressed frame of a sprite (with up to 16 colours)
+ * and decompresses it.
+ * @param dst destination buffer
+ * @param src source buffer
+ * @param decompSize the expected size of the uncompressed sprite
+ * @param colTable mapping from the 16 encoded colours to the current palette
+ */
+
+int32 Screen::decompressRLE16(byte *dst, byte *src, int32 decompSize, byte *colTable) {
+	byte headerByte;			// block header byte
+	byte *endDest = dst + decompSize;	// pointer to byte after end of decomp buffer
+	int32 rv;
+
+	while (1) {
+		// FLAT block
+		// read FLAT block header & increment 'scan' to first pixel
+		// of block
+		headerByte = *src++;
+
+		// if this isn't a zero-length block
+		if (headerByte) {
+			if (dst + headerByte > endDest) {
+				rv = 1;
+				break;
+			}
+
+			// set the next 'headerByte' pixels to the next
+			// colour at 'source'
+			memset(dst, *src, headerByte);
+
+			// increment destination pointer to just after this
+			// block
+			dst += headerByte;
+
+			// increment source pointer to just after this colour
+			src++;
+
+			// if we've decompressed all of the data
+			if (dst == endDest) {
+				rv = 0;		// return "OK"
+				break;
+			}
+		}
+
+		// RAW block
+		// read RAW block header & increment 'scan' to first pixel of
+		// block
+		headerByte = *src++;
+
+		// if this isn't a zero-length block
+		if (headerByte) {
+			if (dst + headerByte > endDest) {
+				rv = 1;
+				break;
+			}
+
+			// copy the next 'headerByte' pixels from source to
+			// destination (NB. 2 pixels per byte)
+			unwindRaw16(dst, src, headerByte, colTable);
+
+			// increment destination pointer to just after this
+			// block
+			dst += headerByte;
+
+			// increment source pointer to just after this block
+			// (NB. headerByte gives pixels, so /2 for bytes)
+			src += (headerByte + 1) / 2;
+
+			// if we've decompressed all of the data
+			if (dst >= endDest) {
+				rv = 0;		// return "OK"
+				break;
+			}
+		}
+	}
+
+	return rv;
+}
+
+/**
+ * Creates a sprite surface. Sprite surfaces are used by the in-game dialogs
+ * and for displaying cutscene subtitles, which makes them much easier to draw
+ * than standard sprites.
+ * @param s information about how to decode the sprite
+ * @param sprite the buffer that will be created to store the surface
+ * @return RD_OK, or an error code
+ */
+
+int32 Screen::createSurface(SpriteInfo *s, byte **sprite) {
+	*sprite = (byte *)malloc(s->w * s->h);
+	if (!*sprite)
+		return RDERR_OUTOFMEMORY;
+
+	// Surfaces are either uncompressed or RLE256-compressed. No need to
+	// test for anything else.
+
+	if (s->type & RDSPR_NOCOMPRESSION) {
+		memcpy(*sprite, s->data, s->w * s->h);
+	} else if (decompressRLE256(*sprite, s->data, s->w * s->h)) {
+		free(*sprite);
+		return RDERR_DECOMPRESSION;
+	}
+
+	return RD_OK;
+}
+
+/**
+ * Draws the sprite surface created earlier.
+ * @param s information about how to place the sprite
+ * @param surface pointer to the surface created earlier
+ * @param clipRect the clipping rectangle
+ */
+
+void Screen::drawSurface(SpriteInfo *s, byte *surface, Common::Rect *clipRect) {
+	Common::Rect rd, rs;
+	uint16 x, y;
+	byte *src, *dst;
+
+	rs.left = 0;
+	rs.right = s->w;
+	rs.top = 0;
+	rs.bottom = s->h;
+
+	rd.left = s->x;
+	rd.right = rd.left + rs.right;
+	rd.top = s->y;
+	rd.bottom = rd.top + rs.bottom;
+
+	Common::Rect defClipRect(0, 0, _screenWide, _screenDeep);
+
+	if (!clipRect) {
+		clipRect = &defClipRect;
+	}
+
+	if (clipRect->left > rd.left) {
+		rs.left += (clipRect->left - rd.left);
+		rd.left = clipRect->left;
+	}
+
+	if (clipRect->top > rd.top) {
+		rs.top += (clipRect->top - rd.top);
+		rd.top = clipRect->top;
+	}
+
+	if (clipRect->right < rd.right) {
+		rd.right = clipRect->right;
+	}
+
+	if (clipRect->bottom < rd.bottom) {
+		rd.bottom = clipRect->bottom;
+	}
+
+	if (rd.width() <= 0 || rd.height() <= 0)
+		return;
+
+	src = surface + rs.top * s->w + rs.left;
+	dst = _buffer + _screenWide * rd.top + rd.left;
+
+	// Surfaces are always transparent.
+
+	for (y = 0; y < rd.height(); y++) {
+		for (x = 0; x < rd.width(); x++) {
+			if (src[x])
+				dst[x] = src[x];
+		}
+		src += s->w;
+		dst += _screenWide;
+	}
+
+	updateRect(&rd);
+}
+
+/**
+ * Destroys a surface.
+ */
+
+void Screen::deleteSurface(byte *surface) {
+	free(surface);
+}
+
+/**
+ * Draws a sprite onto the screen. The type of the sprite can be a combination
+ * of the following flags, some of which are mutually exclusive:
+ * RDSPR_DISPLAYALIGN	The sprite is drawn relative to the top left corner
+ *			of the screen
+ * RDSPR_FLIP		The sprite is mirrored
+ * RDSPR_TRANS		The sprite has a transparent colour zero
+ * RDSPR_BLEND		The sprite is translucent
+ * RDSPR_SHADOW		The sprite is affected by the light mask. (Scaled
+ *			sprites always are.)
+ * RDSPR_NOCOMPRESSION	The sprite data is not compressed
+ * RDSPR_RLE16		The sprite data is a 16-colour compressed sprite
+ * RDSPR_RLE256		The sprite data is a 256-colour compressed sprite
+ * @param s all the information needed to draw the sprite
+ * @warning Sprites will only be drawn onto the background, not over menubar
+ * areas.
+ */
+
+// FIXME: I'm sure this could be optimized. There's plenty of data copying and
+// mallocing here.
+
+int32 Screen::drawSprite(SpriteInfo *s) {
+	byte *src, *dst;
+	byte *sprite, *newSprite;
+	uint16 scale;
+	int16 i, j;
+	uint16 srcPitch;
+	bool freeSprite = false;
+	Common::Rect rd, rs;
+
+	// -----------------------------------------------------------------
+	// Decompression and mirroring
+	// -----------------------------------------------------------------
+
+	if (s->type & RDSPR_NOCOMPRESSION)
+		sprite = s->data;
+	else {
+		sprite = (byte *)malloc(s->w * s->h);
+		freeSprite = true;
+		if (!sprite)
+			return RDERR_OUTOFMEMORY;
+		if ((s->type & 0xff00) == RDSPR_RLE16) {
+			if (decompressRLE16(sprite, s->data, s->w * s->h, s->colourTable)) {
+				free(sprite);
+				return RDERR_DECOMPRESSION;
+			}
+		} else {
+			if (decompressRLE256(sprite, s->data, s->w * s->h)) {
+				free(sprite);
+				return RDERR_DECOMPRESSION;
+			}
+		}
+	}
+
+	if (s->type & RDSPR_FLIP) {
+		newSprite = (byte *)malloc(s->w * s->h);
+		if (newSprite == NULL) {
+			if (freeSprite)
+				free(sprite);
+			return RDERR_OUTOFMEMORY;
+		}
+		mirrorSprite(newSprite, sprite, s->w, s->h);
+		if (freeSprite)
+			free(sprite);
+		sprite = newSprite;
+		freeSprite = true;
+	}
+
+	// -----------------------------------------------------------------
+	// Positioning and clipping.
+	// -----------------------------------------------------------------
+
+	int16 spriteX = s->x;
+	int16 spriteY = s->y;
+
+	if (!(s->type & RDSPR_DISPLAYALIGN)) {
+		spriteX += _parallaxScrollX;
+		spriteY += _parallaxScrollY;
+	}
+
+	spriteY += MENUDEEP;
+
+	// A scale factor 0 or 256 means don't scale. Why do they use two
+	// different values to mean the same thing? Normalize it here for
+	// convenience.
+
+	scale = (s->scale == 0) ? 256 : s->scale;
+
+	rs.top = 0;
+	rs.left = 0;
+
+	if (scale != 256) {
+		rs.right = s->scaledWidth;
+		rs.bottom = s->scaledHeight;
+		srcPitch = s->scaledWidth;
+	} else {
+		rs.right = s->w;
+		rs.bottom = s->h;
+		srcPitch = s->w;
+	}
+
+	rd.top = spriteY;
+	rd.left = spriteX;
+
+	if (!(s->type & RDSPR_DISPLAYALIGN)) {
+		rd.top -= _scrollY;
+		rd.left -= _scrollX;
+	}
+
+	rd.right = rd.left + rs.right;
+	rd.bottom = rd.top + rs.bottom;
+
+	// Check if the sprite would end up completely outside the screen.
+
+	if (rd.left > RENDERWIDE || rd.top > RENDERDEEP + MENUDEEP || rd.right < 0 || rd.bottom < MENUDEEP) {
+		if (freeSprite)
+			free(sprite);
+		return RD_OK;
+	}
+
+	if (rd.top < MENUDEEP) {
+		rs.top = MENUDEEP - rd.top;
+		rd.top = MENUDEEP;
+	}
+	if (rd.bottom > RENDERDEEP + MENUDEEP) {
+		rd.bottom = RENDERDEEP + MENUDEEP;
+		rs.bottom = rs.top + (rd.bottom - rd.top);
+	}
+	if (rd.left < 0) {
+		rs.left = -rd.left;
+		rd.left = 0;
+	}
+	if (rd.right > RENDERWIDE) {
+		rd.right = RENDERWIDE;
+		rs.right = rs.left + (rd.right - rd.left);
+	}
+
+	// -----------------------------------------------------------------
+	// Scaling
+	// -----------------------------------------------------------------
+
+	if (scale != 256) {
+		if (s->scaledWidth > SCALE_MAXWIDTH || s->scaledHeight > SCALE_MAXHEIGHT) {
+			if (freeSprite)
+				free(sprite);
+			return RDERR_NOTIMPLEMENTED;
+		}
+
+		newSprite = (byte *)malloc(s->scaledWidth * s->scaledHeight);
+		if (newSprite == NULL) {
+			if (freeSprite)
+				free(sprite);
+			return RDERR_OUTOFMEMORY;
+		}
+
+		if (_renderCaps & RDBLTFX_EDGEBLEND)
+			scaleImageGood(newSprite, s->scaledWidth, s->scaledWidth, s->scaledHeight, sprite, s->w, s->w, s->h, _buffer + _screenWide * rd.top + rd.left);
+		else
+			scaleImageFast(newSprite, s->scaledWidth, s->scaledWidth, s->scaledHeight, sprite, s->w, s->w, s->h);
+
+		if (freeSprite)
+			free(sprite);
+		sprite = newSprite;
+		freeSprite = true;
+	}
+
+	// -----------------------------------------------------------------
+	// Light masking
+	// -----------------------------------------------------------------
+
+	// The light mask is an optional layer that covers the entire room
+	// and which is used to simulate light and shadows. Scaled sprites
+	// (actors, presumably) are always affected.
+
+	if ((_renderCaps & RDBLTFX_SHADOWBLEND) && _lightMask && (scale != 256 || (s->type & RDSPR_SHADOW))) {
+		byte *lightMap;
+
+		// Make sure that we never apply the shadow to the original
+		// resource data. This could only ever happen in the
+		// RDSPR_NOCOMPRESSION case.
+
+		if (!freeSprite) {
+			newSprite = (byte *)malloc(s->w * s->h);
+			memcpy(newSprite, sprite, s->w * s->h);
+			sprite = newSprite;
+			freeSprite = true;
+		}
+
+		src = sprite + rs.top * srcPitch + rs.left;
+		lightMap = _lightMask + (rd.top + _scrollY - MENUDEEP) * _locationWide + rd.left + _scrollX;
+
+		for (i = 0; i < rs.height(); i++) {
+			for (j = 0; j < rs.width(); j++) {
+				if (src[j] && lightMap[j]) {
+					uint8 r = ((32 - lightMap[j]) * _palette[src[j] * 4 + 0]) >> 5;
+					uint8 g = ((32 - lightMap[j]) * _palette[src[j] * 4 + 1]) >> 5;
+					uint8 b = ((32 - lightMap[j]) * _palette[src[j] * 4 + 2]) >> 5;
+					src[j] = quickMatch(r, g, b);
+				}
+			}
+			src += srcPitch;
+			lightMap += _locationWide;
+		}
+	}
+
+	// -----------------------------------------------------------------
+	// Drawing
+	// -----------------------------------------------------------------
+
+	src = sprite + rs.top * srcPitch + rs.left;
+	dst = _buffer + _screenWide * rd.top + rd.left;
+
+	if (s->type & RDSPR_BLEND) {
+		// The original code had two different blending cases. One for
+		// s->blend & 0x01 and one for s->blend & 0x02. However, the
+		// only values that actually appear in the cluster files are
+		// 0, 513 and 1025 so the s->blend & 0x02 case was never used.
+		// Which is just as well since that code made no sense to me.
+
+		if (!(_renderCaps & RDBLTFX_SPRITEBLEND)) {
+			for (i = 0; i < rs.height(); i++) {
+				for (j = 0; j < rs.width(); j++) {
+					if (src[j] && ((i & 1) == (j & 1)))
+						dst[j] = src[j];
+				}
+				src += srcPitch;
+				dst += _screenWide;
+			}
+		} else {
+			uint8 n = s->blend >> 8;
+
+			for (i = 0; i < rs.height(); i++) {
+				for (j = 0; j < rs.width(); j++) {
+					if (src[j]) {
+						uint8 r1 = _palette[src[j] * 4 + 0];
+						uint8 g1 = _palette[src[j] * 4 + 1];
+						uint8 b1 = _palette[src[j] * 4 + 2];
+						uint8 r2 = _palette[dst[j] * 4 + 0];
+						uint8 g2 = _palette[dst[j] * 4 + 1];
+						uint8 b2 = _palette[dst[j] * 4 + 2];
+
+						uint8 r = (r1 * n + r2 * (8 - n)) >> 3;
+						uint8 g = (g1 * n + g2 * (8 - n)) >> 3;
+						uint8 b = (b1 * n + b2 * (8 - n)) >> 3;
+						dst[j] = quickMatch(r, g, b);
+					}
+				}
+				src += srcPitch;
+				dst += _screenWide;
+			}
+		}
+	} else {
+		if (s->type & RDSPR_TRANS) {
+			for (i = 0; i < rs.height(); i++) {
+				for (j = 0; j < rs.width(); j++) {
+					if (src[j])
+						dst[j] = src[j];
+				}
+				src += srcPitch;
+				dst += _screenWide;
+			}
+		} else {
+			for (i = 0; i < rs.height(); i++) {
+				memcpy(dst, src, rs.width());
+				src += srcPitch;
+				dst += _screenWide;
+			}
+		}
+	}
+
+	if (freeSprite)
+		free(sprite);
+
+	markAsDirty(rd.left, rd.top, rd.right - 1, rd.bottom - 1);
+	return RD_OK;
+}
+
+/**
+ * Opens the light masking sprite for a room.
+ */
+
+int32 Screen::openLightMask(SpriteInfo *s) {
+	// FIXME: The light mask is only needed on higher graphics detail
+	// settings, so to save memory we could simply ignore it on lower
+	// settings. But then we need to figure out how to ensure that it
+	// is properly loaded if the user changes the settings in mid-game.
+
+	if (_lightMask)
+		return RDERR_NOTCLOSED;
+
+	_lightMask = (byte *)malloc(s->w * s->h);
+	if (!_lightMask)
+		return RDERR_OUTOFMEMORY;
+
+	if (decompressRLE256(_lightMask, s->data, s->w * s->h))
+		return RDERR_DECOMPRESSION;
+
+	return RD_OK;
+}
+
+/**
+ * Closes the light masking sprite for a room.
+ */
+
+int32 Screen::closeLightMask() {
+	if (!_lightMask)
+		return RDERR_NOTOPEN;
+
+	free(_lightMask);
+	_lightMask = NULL;
+	return RD_OK;
+}
+
+} // End of namespace Sword2







More information about the Scummvm-git-logs mailing list