[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