diff options
author | Matthew Hoops | 2012-03-10 13:50:27 -0500 |
---|---|---|
committer | Matthew Hoops | 2012-03-10 13:55:08 -0500 |
commit | 5e52b0a5d46e4c5b230cba7464301484d27804f7 (patch) | |
tree | b0c732e5b7c6e98e43e2a7b1e6a8c7a399f3055d /engines/mohawk | |
parent | d142606f755203bf2e407729840898b0c8ca53d5 (diff) | |
download | scummvm-rg350-5e52b0a5d46e4c5b230cba7464301484d27804f7.tar.gz scummvm-rg350-5e52b0a5d46e4c5b230cba7464301484d27804f7.tar.bz2 scummvm-rg350-5e52b0a5d46e4c5b230cba7464301484d27804f7.zip |
MOHAWK: Split the graphics classes into their own files
Diffstat (limited to 'engines/mohawk')
31 files changed, 1449 insertions, 1215 deletions
diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp index 0234c86c7e..a7a650d8ed 100644 --- a/engines/mohawk/console.cpp +++ b/engines/mohawk/console.cpp @@ -21,7 +21,6 @@ */ #include "mohawk/console.h" -#include "mohawk/graphics.h" #include "mohawk/livingbooks.h" #include "mohawk/sound.h" #include "mohawk/video.h" @@ -36,6 +35,7 @@ #ifdef ENABLE_MYST #include "mohawk/myst.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/myst_scripts.h" #endif diff --git a/engines/mohawk/cstime.h b/engines/mohawk/cstime.h index 0bc236f930..db06b9791e 100644 --- a/engines/mohawk/cstime.h +++ b/engines/mohawk/cstime.h @@ -25,7 +25,7 @@ #include "mohawk/mohawk.h" #include "mohawk/console.h" -#include "mohawk/graphics.h" +#include "mohawk/cstime_graphics.h" #include "common/random.h" #include "common/list.h" diff --git a/engines/mohawk/cstime_graphics.cpp b/engines/mohawk/cstime_graphics.cpp new file mode 100644 index 0000000000..3a1452e67c --- /dev/null +++ b/engines/mohawk/cstime_graphics.cpp @@ -0,0 +1,64 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "mohawk/cstime.h" +#include "mohawk/cstime_graphics.h" +#include "mohawk/resource.h" + +#include "common/system.h" +#include "engines/util.h" + +namespace Mohawk { + +CSTimeGraphics::CSTimeGraphics(MohawkEngine_CSTime *vm) : GraphicsManager(), _vm(vm) { + _bmpDecoder = new MohawkBitmap(); + + initGraphics(640, 480, true); +} + +CSTimeGraphics::~CSTimeGraphics() { + delete _bmpDecoder; +} + +void CSTimeGraphics::drawRect(Common::Rect rect, byte color) { + rect.clip(Common::Rect(640, 480)); + + // Useful with debugging. Shows where hotspots are on the screen and whether or not they're active. + if (!rect.isValidRect() || rect.width() == 0 || rect.height() == 0) + return; + + Graphics::Surface *screen = _vm->_system->lockScreen(); + + screen->frameRect(rect, color); + + _vm->_system->unlockScreen(); +} + +MohawkSurface *CSTimeGraphics::decodeImage(uint16 id) { + return _bmpDecoder->decodeImage(_vm->getResource(ID_TBMP, id)); +} + +Common::Array<MohawkSurface *> CSTimeGraphics::decodeImages(uint16 id) { + return _bmpDecoder->decodeImages(_vm->getResource(ID_TBMH, id)); +} + +} // End of namespace Mohawk diff --git a/engines/mohawk/cstime_graphics.h b/engines/mohawk/cstime_graphics.h new file mode 100644 index 0000000000..5f034f47f4 --- /dev/null +++ b/engines/mohawk/cstime_graphics.h @@ -0,0 +1,51 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef MOHAWK_CSTIME_GRAPHICS_H +#define MOHAWK_CSTIME_GRAPHICS_H + +#include "mohawk/graphics.h" + +namespace Mohawk { + +class MohawkEngine_CSTime; + +class CSTimeGraphics : public GraphicsManager { +public: + CSTimeGraphics(MohawkEngine_CSTime *vm); + ~CSTimeGraphics(); + + void drawRect(Common::Rect rect, byte color); + +protected: + MohawkSurface *decodeImage(uint16 id); + Common::Array<MohawkSurface *> decodeImages(uint16 id); + MohawkEngine *getVM() { return (MohawkEngine *)_vm; } + +private: + MohawkBitmap *_bmpDecoder; + MohawkEngine_CSTime *_vm; +}; + +} // End of namespace Mohawk + +#endif diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp index 2ffabf1e8c..a08d034ef7 100644 --- a/engines/mohawk/graphics.cpp +++ b/engines/mohawk/graphics.cpp @@ -20,30 +20,13 @@ * */ +#include "mohawk/mohawk.h" #include "mohawk/resource.h" #include "mohawk/graphics.h" -#include "mohawk/livingbooks.h" -#include "common/substream.h" #include "common/system.h" -#include "common/textconsole.h" #include "engines/util.h" #include "graphics/palette.h" -#include "graphics/primitives.h" -#include "gui/message.h" - -#ifdef ENABLE_CSTIME -#include "mohawk/cstime.h" -#endif - -#ifdef ENABLE_MYST -#include "mohawk/myst.h" -#include "graphics/jpeg.h" -#endif - -#ifdef ENABLE_RIVEN -#include "mohawk/riven.h" -#endif namespace Mohawk { @@ -262,986 +245,4 @@ void GraphicsManager::addImageToCache(uint16 id, MohawkSurface *surface) { _cache[id] = surface; } -#ifdef ENABLE_MYST - -MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { - _bmpDecoder = new MystBitmap(); - - _viewport = Common::Rect(544, 332); - - // The original version of Myst could run in 8bpp color too. - // However, it dithered videos to 8bpp and they looked considerably - // worse (than they already did :P). So we're not even going to - // support 8bpp mode in Myst (Myst ME required >8bpp anyway). - initGraphics(_viewport.width(), _viewport.height(), true, NULL); // What an odd screen size! - - _pixelFormat = _vm->_system->getScreenFormat(); - - if (_pixelFormat.bytesPerPixel == 1) - error("Myst requires greater than 256 colors to run"); - - if (_vm->getFeatures() & GF_ME) { - _jpegDecoder = new Graphics::JPEG(); - _pictDecoder = new Graphics::PictDecoder(_pixelFormat); - } else { - _jpegDecoder = NULL; - _pictDecoder = NULL; - } - - _pictureFile.entries = NULL; - - // Initialize our buffer - _backBuffer = new Graphics::Surface(); - _backBuffer->create(_vm->_system->getWidth(), _vm->_system->getHeight(), _pixelFormat); - - _nextAllowedDrawTime = _vm->_system->getMillis(); - _enableDrawingTimeSimulation = 0; -} - -MystGraphics::~MystGraphics() { - delete _bmpDecoder; - delete _jpegDecoder; - delete _pictDecoder; - delete[] _pictureFile.entries; - - _backBuffer->free(); - delete _backBuffer; -} - -static const char *s_picFileNames[] = { - "CHpics", - "", - "", - "DUpics", - "INpics", - "", - "MEpics", - "MYpics", - "SEpics", - "", - "", - "STpics" -}; - -void MystGraphics::loadExternalPictureFile(uint16 stack) { - if (_vm->getPlatform() != Common::kPlatformMacintosh) - return; - - if (_pictureFile.picFile.isOpen()) - _pictureFile.picFile.close(); - delete[] _pictureFile.entries; - - if (!scumm_stricmp(s_picFileNames[stack], "")) - return; - - if (!_pictureFile.picFile.open(s_picFileNames[stack])) - error ("Could not open external picture file \'%s\'", s_picFileNames[stack]); - - _pictureFile.pictureCount = _pictureFile.picFile.readUint32BE(); - _pictureFile.entries = new PictureFile::PictureEntry[_pictureFile.pictureCount]; - - for (uint32 i = 0; i < _pictureFile.pictureCount; i++) { - _pictureFile.entries[i].offset = _pictureFile.picFile.readUint32BE(); - _pictureFile.entries[i].size = _pictureFile.picFile.readUint32BE(); - _pictureFile.entries[i].id = _pictureFile.picFile.readUint16BE(); - _pictureFile.entries[i].type = _pictureFile.picFile.readUint16BE(); - _pictureFile.entries[i].width = _pictureFile.picFile.readUint16BE(); - _pictureFile.entries[i].height = _pictureFile.picFile.readUint16BE(); - } -} - -MohawkSurface *MystGraphics::decodeImage(uint16 id) { - MohawkSurface *mhkSurface = 0; - - // Myst ME uses JPEG/PICT images instead of compressed Windows Bitmaps for room images, - // though there are a few weird ones that use that format. For further nonsense with images, - // the Macintosh version stores images in external "picture files." We check them before - // going to check for a PICT resource. - if (_vm->getFeatures() & GF_ME && _vm->getPlatform() == Common::kPlatformMacintosh && _pictureFile.picFile.isOpen()) { - for (uint32 i = 0; i < _pictureFile.pictureCount; i++) - if (_pictureFile.entries[i].id == id) { - if (_pictureFile.entries[i].type == 0) { - Common::SeekableReadStream *stream = new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size); - - if (!_jpegDecoder->read(stream)) - error("Could not decode Myst ME Mac JPEG"); - - mhkSurface = new MohawkSurface(_jpegDecoder->getSurface(_pixelFormat)); - delete stream; - } else if (_pictureFile.entries[i].type == 1) { - mhkSurface = new MohawkSurface(_pictDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size))); - } else - error ("Unknown Picture File type %d", _pictureFile.entries[i].type); - break; - } - } - - // We're not using the external Mac files, so it's time to delve into the main Mohawk - // archives. However, we still don't know if it's a PICT or WDIB resource. If it's Myst - // ME it's most likely a PICT, and if it's original it's definitely a WDIB. However, - // Myst ME throws us another curve ball in that PICT resources can contain WDIB's instead - // of PICT's. - if (!mhkSurface) { - bool isPict = false; - Common::SeekableReadStream *dataStream = NULL; - - if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, id)) { - // The PICT resource exists. However, it could still contain a MystBitmap - // instead of a PICT image... - dataStream = _vm->getResource(ID_PICT, id); - } else // No PICT, so the WDIB must exist. Let's go grab it. - dataStream = _vm->getResource(ID_WDIB, id); - - if (_vm->getFeatures() & GF_ME) { - // Here we detect whether it's really a PICT or a WDIB. Since a MystBitmap - // would be compressed, there's no way to detect for the BM without a hack. - // So, we search for the PICT version opcode for detection. - dataStream->seek(512 + 10); // 512 byte pict header - isPict = (dataStream->readUint32BE() == 0x001102FF); - dataStream->seek(0); - } - - if (isPict) - mhkSurface = new MohawkSurface(_pictDecoder->decodeImage(dataStream)); - else { - mhkSurface = _bmpDecoder->decodeImage(dataStream); - mhkSurface->convertToTrueColor(); - } - } - - assert(mhkSurface); - return mhkSurface; -} - -void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest) { - Graphics::Surface *surface = findImage(image)->getSurface(); - - // Make sure the image is bottom aligned in the dest rect - dest.top = dest.bottom - MIN<int>(surface->h, dest.height()); - - // Convert from bitmap coordinates to surface coordinates - uint16 top = surface->h - (src.top + MIN<int>(surface->h, dest.height())); - - // Do not draw the top pixels if the image is too tall - if (dest.height() > _viewport.height()) - top += dest.height() - _viewport.height(); - - // Clip the destination rect to the screen - if (dest.right > _vm->_system->getWidth() || dest.bottom > _vm->_system->getHeight()) - dest.debugPrint(4, "Clipping destination rect to the screen"); - dest.right = CLIP<int>(dest.right, 0, _vm->_system->getWidth()); - dest.bottom = CLIP<int>(dest.bottom, 0, _vm->_system->getHeight()); - - uint16 width = MIN<int>(surface->w, dest.width()); - uint16 height = MIN<int>(surface->h, dest.height()); - - // Clamp Width and Height to within src surface dimensions - if (src.left + width > surface->w) - width = surface->w - src.left; - if (src.top + height > surface->h) - height = surface->h - src.top; - - debug(3, "MystGraphics::copyImageSectionToScreen()"); - debug(3, "\tImage: %d", image); - debug(3, "\tsrc.left: %d", src.left); - debug(3, "\tsrc.top: %d", src.top); - debug(3, "\tdest.left: %d", dest.left); - debug(3, "\tdest.top: %d", dest.top); - debug(3, "\twidth: %d", width); - debug(3, "\theight: %d", height); - - simulatePreviousDrawDelay(dest); - - _vm->_system->copyRectToScreen((byte *)surface->getBasePtr(src.left, top), surface->pitch, dest.left, dest.top, width, height); -} - -void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest) { - Graphics::Surface *surface = findImage(image)->getSurface(); - - // Make sure the image is bottom aligned in the dest rect - dest.top = dest.bottom - MIN<int>(surface->h, dest.height()); - - // Convert from bitmap coordinates to surface coordinates - uint16 top = surface->h - (src.top + MIN<int>(surface->h, dest.height())); - - // Do not draw the top pixels if the image is too tall - if (dest.height() > _viewport.height()) { - top += dest.height() - _viewport.height(); - } - - // Clip the destination rect to the screen - if (dest.right > _vm->_system->getWidth() || dest.bottom > _vm->_system->getHeight()) - dest.debugPrint(4, "Clipping destination rect to the screen"); - dest.right = CLIP<int>(dest.right, 0, _vm->_system->getWidth()); - dest.bottom = CLIP<int>(dest.bottom, 0, _vm->_system->getHeight()); - - uint16 width = MIN<int>(surface->w, dest.width()); - uint16 height = MIN<int>(surface->h, dest.height()); - - // Clamp Width and Height to within src surface dimensions - if (src.left + width > surface->w) - width = surface->w - src.left; - if (src.top + height > surface->h) - height = surface->h - src.top; - - debug(3, "MystGraphics::copyImageSectionToBackBuffer()"); - debug(3, "\tImage: %d", image); - debug(3, "\tsrc.left: %d", src.left); - debug(3, "\tsrc.top: %d", src.top); - debug(3, "\tdest.left: %d", dest.left); - debug(3, "\tdest.top: %d", dest.top); - debug(3, "\twidth: %d", width); - debug(3, "\theight: %d", height); - - for (uint16 i = 0; i < height; i++) - memcpy(_backBuffer->getBasePtr(dest.left, i + dest.top), surface->getBasePtr(src.left, top + i), width * surface->format.bytesPerPixel); -} - -void MystGraphics::copyImageToScreen(uint16 image, Common::Rect dest) { - copyImageSectionToScreen(image, Common::Rect(544, 333), dest); -} - -void MystGraphics::copyImageToBackBuffer(uint16 image, Common::Rect dest) { - copyImageSectionToBackBuffer(image, Common::Rect(544, 333), dest); -} - -void MystGraphics::copyBackBufferToScreen(Common::Rect r) { - r.clip(_viewport); - - simulatePreviousDrawDelay(r); - - _vm->_system->copyRectToScreen((byte *)_backBuffer->getBasePtr(r.left, r.top), _backBuffer->pitch, r.left, r.top, r.width(), r.height()); -} - -void MystGraphics::runTransition(uint16 type, Common::Rect rect, uint16 steps, uint16 delay) { - - // Do not artificially delay during transitions - int oldEnableDrawingTimeSimulation = _enableDrawingTimeSimulation; - _enableDrawingTimeSimulation = 0; - - switch (type) { - case 0: { - debugC(kDebugScript, "Left to Right"); - - uint16 step = (rect.right - rect.left) / steps; - Common::Rect area = rect; - for (uint i = 0; i < steps; i++) { - area.left = rect.left + step * i; - area.right = area.left + step; - - _vm->_system->delayMillis(delay); - - copyBackBufferToScreen(area); - _vm->_system->updateScreen(); - } - if (area.right < rect.right) { - area.left = area.right; - area.right = rect.right; - - copyBackBufferToScreen(area); - _vm->_system->updateScreen(); - } - } - break; - case 1: { - debugC(kDebugScript, "Right to Left"); - - uint16 step = (rect.right - rect.left) / steps; - Common::Rect area = rect; - for (uint i = 0; i < steps; i++) { - area.right = rect.right - step * i; - area.left = area.right - step; - - _vm->_system->delayMillis(delay); - - copyBackBufferToScreen(area); - _vm->_system->updateScreen(); - } - if (area.left > rect.left) { - area.right = area.left; - area.left = rect.left; - - copyBackBufferToScreen(area); - _vm->_system->updateScreen(); - } - } - break; - case 5: { - debugC(kDebugScript, "Top to Bottom"); - - uint16 step = (rect.bottom - rect.top) / steps; - Common::Rect area = rect; - for (uint i = 0; i < steps; i++) { - area.top = rect.top + step * i; - area.bottom = area.top + step; - - _vm->_system->delayMillis(delay); - - copyBackBufferToScreen(area); - _vm->_system->updateScreen(); - } - if (area.bottom < rect.bottom) { - area.top = area.bottom; - area.bottom = rect.bottom; - - copyBackBufferToScreen(area); - _vm->_system->updateScreen(); - } - } - break; - case 6: { - debugC(kDebugScript, "Bottom to Top"); - - uint16 step = (rect.bottom - rect.top) / steps; - Common::Rect area = rect; - for (uint i = 0; i < steps; i++) { - area.bottom = rect.bottom - step * i; - area.top = area.bottom - step; - - _vm->_system->delayMillis(delay); - - copyBackBufferToScreen(area); - _vm->_system->updateScreen(); - } - if (area.top > rect.top) { - area.bottom = area.top; - area.top = rect.top; - - copyBackBufferToScreen(area); - _vm->_system->updateScreen(); - } - } - break; - default: - warning("Unknown Update Direction"); - - //TODO: Replace minimal implementation - copyBackBufferToScreen(rect); - _vm->_system->updateScreen(); - break; - } - - _enableDrawingTimeSimulation = oldEnableDrawingTimeSimulation; -} - -void MystGraphics::drawRect(Common::Rect rect, RectState state) { - rect.clip(_viewport); - - // Useful with debugging. Shows where hotspots are on the screen and whether or not they're active. - if (!rect.isValidRect() || rect.width() == 0 || rect.height() == 0) - return; - - Graphics::Surface *screen = _vm->_system->lockScreen(); - - if (state == kRectEnabled) - screen->frameRect(rect, _pixelFormat.RGBToColor(0, 255, 0)); - else if (state == kRectUnreachable) - screen->frameRect(rect, _pixelFormat.RGBToColor(0, 0, 255)); - else - screen->frameRect(rect, _pixelFormat.RGBToColor(255, 0, 0)); - - _vm->_system->unlockScreen(); -} - -void MystGraphics::drawLine(const Common::Point &p1, const Common::Point &p2, uint32 color) { - _backBuffer->drawLine(p1.x, p1.y, p2.x, p2.y, color); -} - -void MystGraphics::enableDrawingTimeSimulation(bool enable) { - if (enable) - _enableDrawingTimeSimulation++; - else - _enableDrawingTimeSimulation--; - - if (_enableDrawingTimeSimulation < 0) - _enableDrawingTimeSimulation = 0; -} - -void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) { - uint32 time = 0; - - if (_enableDrawingTimeSimulation) { - time = _vm->_system->getMillis(); - - // Do not draw anything new too quickly after the previous draw call - // so that images stay at least a little while on screen - // This is enabled only for scripted draw calls - if (time < _nextAllowedDrawTime) - _vm->_system->delayMillis(_nextAllowedDrawTime - time); - } - - // Next draw call allowed at DELAY + AERA * COEFF milliseconds from now - time = _vm->_system->getMillis(); - _nextAllowedDrawTime = time + _constantDrawDelay + dest.height() * dest.width() / _proportionalDrawDelay; -} - -void MystGraphics::copyBackBufferToScreenWithSaturation(int16 saturation) { - Graphics::Surface *screen = _vm->_system->lockScreen(); - - for (uint16 y = 0; y < _viewport.height(); y++) - for (uint16 x = 0; x < _viewport.width(); x++) { - uint32 color; - uint8 r, g, b; - - if (_pixelFormat.bytesPerPixel == 2) - color = *(const uint16 *)_backBuffer->getBasePtr(x, y); - else - color = *(const uint32 *)_backBuffer->getBasePtr(x, y); - - _pixelFormat.colorToRGB(color, r, g, b); - - r = CLIP<int16>((int16)r - saturation, 0, 255); - g = CLIP<int16>((int16)g - saturation, 0, 255); - b = CLIP<int16>((int16)b - saturation, 0, 255); - - color = _pixelFormat.RGBToColor(r, g, b); - - if (_pixelFormat.bytesPerPixel == 2) { - uint16 *dst = (uint16 *)screen->getBasePtr(x, y); - *dst = color; - } else { - uint32 *dst = (uint32 *)screen->getBasePtr(x, y); - *dst = color; - } - } - - _vm->_system->unlockScreen(); - _vm->_system->updateScreen(); -} - -void MystGraphics::fadeToBlack() { - for (int16 i = 0; i < 256; i += 32) { - copyBackBufferToScreenWithSaturation(i); - } -} - -void MystGraphics::fadeFromBlack() { - for (int16 i = 256; i >= 0; i -= 32) { - copyBackBufferToScreenWithSaturation(i); - } -} - -#endif // ENABLE_MYST - -#ifdef ENABLE_RIVEN - -RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm) { - _bitmapDecoder = new MohawkBitmap(); - - // Give me the best you've got! - initGraphics(608, 436, true, NULL); - _pixelFormat = _vm->_system->getScreenFormat(); - - if (_pixelFormat.bytesPerPixel == 1) - error("Riven requires greater than 256 colors to run"); - - // The actual game graphics only take up the first 392 rows. The inventory - // occupies the rest of the screen and we don't use the buffer to hold that. - _mainScreen = new Graphics::Surface(); - _mainScreen->create(608, 392, _pixelFormat); - - _updatesEnabled = true; - _scheduledTransition = -1; // no transition - _dirtyScreen = false; - _inventoryDrawn = false; - - _creditsImage = 302; - _creditsPos = 0; -} - -RivenGraphics::~RivenGraphics() { - _mainScreen->free(); - delete _mainScreen; - delete _bitmapDecoder; -} - -MohawkSurface *RivenGraphics::decodeImage(uint16 id) { - MohawkSurface *surface = _bitmapDecoder->decodeImage(_vm->getResource(ID_TBMP, id)); - surface->convertToTrueColor(); - return surface; -} - -void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uint32 right, uint32 bottom) { - Graphics::Surface *surface = findImage(image)->getSurface(); - - // Clip the width to fit on the screen. Fixes some images. - if (left + surface->w > 608) - surface->w = 608 - left; - - for (uint16 i = 0; i < surface->h; i++) - memcpy(_mainScreen->getBasePtr(left, i + top), surface->getBasePtr(0, i), surface->w * surface->format.bytesPerPixel); - - _dirtyScreen = true; -} - -void RivenGraphics::drawPLST(uint16 x) { - Common::SeekableReadStream* plst = _vm->getResource(ID_PLST, _vm->getCurCard()); - uint16 recordCount = plst->readUint16BE(); - - for (uint16 i = 0; i < recordCount; i++) { - uint16 index = plst->readUint16BE(); - uint16 id = plst->readUint16BE(); - uint16 left = plst->readUint16BE(); - uint16 top = plst->readUint16BE(); - uint16 right = plst->readUint16BE(); - uint16 bottom = plst->readUint16BE(); - - // We are also checking here to make sure we haven't drawn the image yet on screen. - // This fixes problems with drawing PLST 1 twice and some other images twice. PLST - // 1 is sometimes not called by the scripts, so some cards don't appear if we don't - // draw PLST 1 each time. This "hack" is here to catch any PLST attempting to draw - // twice. There should never be a problem with doing it this way. - if (index == x && !(Common::find(_activatedPLSTs.begin(), _activatedPLSTs.end(), x) != _activatedPLSTs.end())) { - debug(0, "Drawing image %d", id); - copyImageToScreen(id, left, top, right, bottom); - _activatedPLSTs.push_back(x); - break; - } - } - - delete plst; -} - -void RivenGraphics::updateScreen(Common::Rect updateRect) { - if (_updatesEnabled) { - _vm->runUpdateScreenScript(); - - if (_dirtyScreen) { - _activatedPLSTs.clear(); - - // Copy to screen if there's no transition. Otherwise transition. ;) - if (_scheduledTransition < 0) - _vm->_system->copyRectToScreen((byte *)_mainScreen->getBasePtr(updateRect.left, updateRect.top), _mainScreen->pitch, updateRect.left, updateRect.top, updateRect.width(), updateRect.height()); - else - runScheduledTransition(); - - // Finally, update the screen. - _vm->_system->updateScreen(); - _dirtyScreen = false; - } - } -} - -void RivenGraphics::scheduleWaterEffect(uint16 sfxeID) { - Common::SeekableReadStream *sfxeStream = _vm->getResource(ID_SFXE, sfxeID); - - if (sfxeStream->readUint16BE() != 'SL') - error ("Unknown sfxe tag"); - - // Read in header info - SFXERecord sfxeRecord; - sfxeRecord.frameCount = sfxeStream->readUint16BE(); - uint32 offsetTablePosition = sfxeStream->readUint32BE(); - sfxeRecord.rect.left = sfxeStream->readUint16BE(); - sfxeRecord.rect.top = sfxeStream->readUint16BE(); - sfxeRecord.rect.right = sfxeStream->readUint16BE(); - sfxeRecord.rect.bottom = sfxeStream->readUint16BE(); - sfxeRecord.speed = sfxeStream->readUint16BE(); - // Skip the rest of the fields... - - // Read in offsets - sfxeStream->seek(offsetTablePosition); - uint32 *frameOffsets = new uint32[sfxeRecord.frameCount]; - for (uint16 i = 0; i < sfxeRecord.frameCount; i++) - frameOffsets[i] = sfxeStream->readUint32BE(); - sfxeStream->seek(frameOffsets[0]); - - // Read in the scripts - for (uint16 i = 0; i < sfxeRecord.frameCount; i++) - sfxeRecord.frameScripts.push_back(sfxeStream->readStream((i == sfxeRecord.frameCount - 1) ? sfxeStream->size() - frameOffsets[i] : frameOffsets[i + 1] - frameOffsets[i])); - - // Set it to the first frame - sfxeRecord.curFrame = 0; - sfxeRecord.lastFrameTime = 0; - - delete[] frameOffsets; - delete sfxeStream; - _waterEffects.push_back(sfxeRecord); -} - -void RivenGraphics::clearWaterEffects() { - _waterEffects.clear(); -} - -bool RivenGraphics::runScheduledWaterEffects() { - // Don't run the effect if it's disabled - if (_vm->_vars["waterenabled"] == 0) - return false; - - Graphics::Surface *screen = NULL; - - for (uint16 i = 0; i < _waterEffects.size(); i++) { - if (_vm->_system->getMillis() > _waterEffects[i].lastFrameTime + 1000 / _waterEffects[i].speed) { - // Lock the screen! - if (!screen) - screen = _vm->_system->lockScreen(); - - // Make sure the script is at the starting point - Common::SeekableReadStream *script = _waterEffects[i].frameScripts[_waterEffects[i].curFrame]; - if (script->pos() != 0) - script->seek(0); - - // Run script - uint16 curRow = 0; - for (uint16 op = script->readUint16BE(); op != 4; op = script->readUint16BE()) { - if (op == 1) { // Increment Row - curRow++; - } else if (op == 3) { // Copy Pixels - uint16 dstLeft = script->readUint16BE(); - uint16 srcLeft = script->readUint16BE(); - uint16 srcTop = script->readUint16BE(); - uint16 rowWidth = script->readUint16BE(); - memcpy ((byte *)screen->getBasePtr(dstLeft, curRow + _waterEffects[i].rect.top), (byte *)_mainScreen->getBasePtr(srcLeft, srcTop), rowWidth * _pixelFormat.bytesPerPixel); - } else if (op != 4) { // End of Script - error ("Unknown SFXE opcode %d", op); - } - } - - // Increment frame - _waterEffects[i].curFrame++; - if (_waterEffects[i].curFrame == _waterEffects[i].frameCount) - _waterEffects[i].curFrame = 0; - - // Set the new time - _waterEffects[i].lastFrameTime = _vm->_system->getMillis(); - } - } - - // Unlock the screen if it has been locked and return true to update the screen - if (screen) { - _vm->_system->unlockScreen(); - return true; - } - - return false; -} - -void RivenGraphics::scheduleTransition(uint16 id, Common::Rect rect) { - _scheduledTransition = id; - _transitionRect = rect; -} - -void RivenGraphics::runScheduledTransition() { - if (_scheduledTransition < 0) // No transition is scheduled - return; - - // TODO: There's a lot to be done here... - - // Note: Transitions 0-11 are actual transitions, but none are used in-game. - // There's no point in implementing them if they're not used. These extra - // transitions were found by hacking scripts. - - switch (_scheduledTransition) { - case 0: // Swipe Left - case 1: // Swipe Right - case 2: // Swipe Up - case 3: // Swipe Down - case 12: // Pan Left - case 13: // Pan Right - case 14: // Pan Up - case 15: // Pan Down - case 16: // Dissolve - case 17: // Dissolve (tspit CARD 155) - break; - default: - if (_scheduledTransition >= 4 && _scheduledTransition <= 11) - error("Found unused transition %d", _scheduledTransition); - else - error("Found unknown transition %d", _scheduledTransition); - } - - // For now, just copy the image to screen without doing any transition. - _vm->_system->copyRectToScreen((byte *)_mainScreen->pixels, _mainScreen->pitch, 0, 0, _mainScreen->w, _mainScreen->h); - _vm->_system->updateScreen(); - - _scheduledTransition = -1; // Clear scheduled transition -} - -void RivenGraphics::clearMainScreen() { - _mainScreen->fillRect(Common::Rect(0, 0, 608, 392), _pixelFormat.RGBToColor(0, 0, 0)); -} - -void RivenGraphics::fadeToBlack() { - // The transition speed is forced to best here - setTransitionSpeed(kRivenTransitionSpeedBest); - scheduleTransition(16); - clearMainScreen(); - runScheduledTransition(); -} - -void RivenGraphics::showInventory() { - // Don't redraw the inventory - if (_inventoryDrawn) - return; - - // Clear the inventory area - clearInventoryArea(); - - // Draw the demo's exit button - if (_vm->getFeatures() & GF_DEMO) { - // extras.mhk tBMP 101 contains "EXIT" instead of Atrus' journal in the demo! - // The demo's extras.mhk contains all the other inventory/marble/credits image - // but has hacked tBMP 101 with "EXIT". *sigh* - drawInventoryImage(101, g_demoExitRect); - } else { - // We don't want to show the inventory on setup screens or in other journals. - if (_vm->getCurStack() == aspit) - return; - - // There are three books and three vars. We have three different - // combinations. At the start you have just Atrus' journal. Later, - // you get Catherine's journal and the trap book. Near the end, - // you lose the trap book and have just the two journals. - - bool hasCathBook = _vm->_vars["acathbook"] != 0; - bool hasTrapBook = _vm->_vars["atrapbook"] != 0; - - if (!hasCathBook) { - drawInventoryImage(101, g_atrusJournalRect1); - } else if (!hasTrapBook) { - drawInventoryImage(101, g_atrusJournalRect2); - drawInventoryImage(102, g_cathJournalRect2); - } else { - drawInventoryImage(101, g_atrusJournalRect3); - drawInventoryImage(102, g_cathJournalRect3); - drawInventoryImage(100, g_trapBookRect3); - } - } - - _vm->_system->updateScreen(); - _inventoryDrawn = true; -} - -void RivenGraphics::hideInventory() { - // Don't hide the inventory twice - if (!_inventoryDrawn) - return; - - // Clear the area - clearInventoryArea(); - - _inventoryDrawn = false; -} - -void RivenGraphics::clearInventoryArea() { - // Clear the inventory area - static const Common::Rect inventoryRect = Common::Rect(0, 392, 608, 436); - - // Lock the screen - Graphics::Surface *screen = _vm->_system->lockScreen(); - - // Fill the inventory area with black - screen->fillRect(inventoryRect, _pixelFormat.RGBToColor(0, 0, 0)); - - _vm->_system->unlockScreen(); -} - -void RivenGraphics::drawInventoryImage(uint16 id, const Common::Rect *rect) { - MohawkSurface *mhkSurface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id)); - mhkSurface->convertToTrueColor(); - Graphics::Surface *surface = mhkSurface->getSurface(); - - _vm->_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, rect->left, rect->top, surface->w, surface->h); - - delete mhkSurface; -} - -void RivenGraphics::drawRect(Common::Rect rect, bool active) { - // Useful with debugging. Shows where hotspots are on the screen and whether or not they're active. - Graphics::Surface *screen = _vm->_system->lockScreen(); - - if (active) - screen->frameRect(rect, _pixelFormat.RGBToColor(0, 255, 0)); - else - screen->frameRect(rect, _pixelFormat.RGBToColor(255, 0, 0)); - - _vm->_system->unlockScreen(); -} - -void RivenGraphics::drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect) { - // Draw tBMP id from srcRect to dstRect - Graphics::Surface *surface = findImage(id)->getSurface(); - - assert(srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height()); - - for (uint16 i = 0; i < srcRect.height(); i++) - memcpy(_mainScreen->getBasePtr(dstRect.left, i + dstRect.top), surface->getBasePtr(srcRect.left, i + srcRect.top), srcRect.width() * surface->format.bytesPerPixel); - - _dirtyScreen = true; -} - -void RivenGraphics::drawExtrasImage(uint16 id, Common::Rect dstRect) { - MohawkSurface *mhkSurface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id)); - mhkSurface->convertToTrueColor(); - Graphics::Surface *surface = mhkSurface->getSurface(); - - assert(dstRect.width() == surface->w); - - for (uint16 i = 0; i < surface->h; i++) - memcpy(_mainScreen->getBasePtr(dstRect.left, i + dstRect.top), surface->getBasePtr(0, i), surface->pitch); - - delete mhkSurface; - _dirtyScreen = true; -} - -void RivenGraphics::beginCredits() { - // Clear the old cache - clearCache(); - - // Now cache all the credits images - for (uint16 i = 302; i <= 320; i++) { - MohawkSurface *surface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, i)); - surface->convertToTrueColor(); - addImageToCache(i, surface); - } - - // And clear our screen too - clearMainScreen(); -} - -void RivenGraphics::updateCredits() { - if ((_creditsImage == 303 || _creditsImage == 304) && _creditsPos == 0) - fadeToBlack(); - - if (_creditsImage < 304) { - // For the first two credit images, they are faded from black to the image and then out again - scheduleTransition(16); - - Graphics::Surface *frame = findImage(_creditsImage++)->getSurface(); - - for (int y = 0; y < frame->h; y++) - memcpy(_mainScreen->getBasePtr(124, y), frame->getBasePtr(0, y), frame->pitch); - - runScheduledTransition(); - } else { - // Otheriwse, we're scrolling - // Move the screen up one row - memmove(_mainScreen->pixels, _mainScreen->getBasePtr(0, 1), _mainScreen->pitch * (_mainScreen->h - 1)); - - // Only update as long as we're not before the last frame - // Otherwise, we're just moving up a row (which we already did) - if (_creditsImage <= 320) { - // Copy the next row to the bottom of the screen - Graphics::Surface *frame = findImage(_creditsImage)->getSurface(); - memcpy(_mainScreen->getBasePtr(124, _mainScreen->h - 1), frame->getBasePtr(0, _creditsPos), frame->pitch); - _creditsPos++; - - if (_creditsPos == _mainScreen->h) { - _creditsImage++; - _creditsPos = 0; - } - } - - // Now flush the new screen - _vm->_system->copyRectToScreen((byte *)_mainScreen->pixels, _mainScreen->pitch, 0, 0, _mainScreen->w, _mainScreen->h); - _vm->_system->updateScreen(); - } -} - -#endif // ENABLE_RIVEN - -LBGraphics::LBGraphics(MohawkEngine_LivingBooks *vm, uint16 width, uint16 height) : GraphicsManager(), _vm(vm) { - _bmpDecoder = _vm->isPreMohawk() ? new LivingBooksBitmap_v1() : new MohawkBitmap(); - - initGraphics(width, height, true); -} - -LBGraphics::~LBGraphics() { - delete _bmpDecoder; -} - -MohawkSurface *LBGraphics::decodeImage(uint16 id) { - if (_vm->isPreMohawk()) - return _bmpDecoder->decodeImage(_vm->wrapStreamEndian(ID_BMAP, id)); - - return _bmpDecoder->decodeImage(_vm->getResource(ID_TBMP, id)); -} - -void LBGraphics::copyOffsetAnimImageToScreen(uint16 image, int left, int top) { - MohawkSurface *mhkSurface = findImage(image); - - left -= mhkSurface->getOffsetX(); - top -= mhkSurface->getOffsetY(); - - GraphicsManager::copyAnimImageToScreen(image, left, top); -} - -bool LBGraphics::imageIsTransparentAt(uint16 image, bool useOffsets, int x, int y) { - MohawkSurface *mhkSurface = findImage(image); - - if (useOffsets) { - x += mhkSurface->getOffsetX(); - y += mhkSurface->getOffsetY(); - } - - if (x < 0 || y < 0) - return true; - - Graphics::Surface *surface = mhkSurface->getSurface(); - if (x >= surface->w || y >= surface->h) - return true; - - return *(byte *)surface->getBasePtr(x, y) == 0; -} - -void LBGraphics::setPalette(uint16 id) { - // Old Living Books games use the old CTBL-style palette format while newer - // games use the better tPAL format which can store partial palettes. - if (_vm->isPreMohawk()) { - Common::SeekableSubReadStreamEndian *ctblStream = _vm->wrapStreamEndian(ID_CTBL, id); - uint16 colorCount = ctblStream->readUint16(); - byte *palette = new byte[colorCount * 3]; - - for (uint16 i = 0; i < colorCount; i++) { - palette[i * 3 + 0] = ctblStream->readByte(); - palette[i * 3 + 1] = ctblStream->readByte(); - palette[i * 3 + 2] = ctblStream->readByte(); - ctblStream->readByte(); - } - - delete ctblStream; - - _vm->_system->getPaletteManager()->setPalette(palette, 0, colorCount); - delete[] palette; - } else { - GraphicsManager::setPalette(id); - } -} - -#ifdef ENABLE_CSTIME - -CSTimeGraphics::CSTimeGraphics(MohawkEngine_CSTime *vm) : GraphicsManager(), _vm(vm) { - _bmpDecoder = new MohawkBitmap(); - - initGraphics(640, 480, true); -} - -CSTimeGraphics::~CSTimeGraphics() { - delete _bmpDecoder; -} - -void CSTimeGraphics::drawRect(Common::Rect rect, byte color) { - rect.clip(Common::Rect(640, 480)); - - // Useful with debugging. Shows where hotspots are on the screen and whether or not they're active. - if (!rect.isValidRect() || rect.width() == 0 || rect.height() == 0) - return; - - Graphics::Surface *screen = _vm->_system->lockScreen(); - - screen->frameRect(rect, color); - - _vm->_system->unlockScreen(); -} - -MohawkSurface *CSTimeGraphics::decodeImage(uint16 id) { - return _bmpDecoder->decodeImage(_vm->getResource(ID_TBMP, id)); -} - -Common::Array<MohawkSurface *> CSTimeGraphics::decodeImages(uint16 id) { - return _bmpDecoder->decodeImages(_vm->getResource(ID_TBMH, id)); -} - -#endif - } // End of namespace Mohawk diff --git a/engines/mohawk/graphics.h b/engines/mohawk/graphics.h index 8fa5d5f895..51d25db5d9 100644 --- a/engines/mohawk/graphics.h +++ b/engines/mohawk/graphics.h @@ -25,12 +25,11 @@ #include "mohawk/bitmap.h" -#include "common/file.h" #include "common/hashmap.h" -#include "graphics/pict.h" +#include "common/rect.h" namespace Graphics { -class JPEG; +struct Surface; } namespace Mohawk { @@ -103,196 +102,6 @@ private: Common::HashMap<uint16, Common::Array<MohawkSurface *> > _subImageCache; }; -#ifdef ENABLE_MYST - -class MystBitmap; -class MohawkEngine_Myst; - -enum RectState { - kRectEnabled, - kRectDisabled, - kRectUnreachable -}; - -class MystGraphics : public GraphicsManager { -public: - MystGraphics(MohawkEngine_Myst*); - ~MystGraphics(); - - void loadExternalPictureFile(uint16 stack); - void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest); - void copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest); - void copyImageToScreen(uint16 image, Common::Rect dest); - void copyImageToBackBuffer(uint16 image, Common::Rect dest); - void copyBackBufferToScreen(Common::Rect r); - void runTransition(uint16 type, Common::Rect rect, uint16 steps, uint16 delay); - void drawRect(Common::Rect rect, RectState state); - void drawLine(const Common::Point &p1, const Common::Point &p2, uint32 color); - void enableDrawingTimeSimulation(bool enable); - void fadeToBlack(); - void fadeFromBlack(); - -protected: - MohawkSurface *decodeImage(uint16 id); - MohawkEngine *getVM() { return (MohawkEngine *)_vm; } - void simulatePreviousDrawDelay(const Common::Rect &dest); - void copyBackBufferToScreenWithSaturation(int16 saturation); - -private: - MohawkEngine_Myst *_vm; - MystBitmap *_bmpDecoder; - Graphics::PictDecoder *_pictDecoder; - Graphics::JPEG *_jpegDecoder; - - struct PictureFile { - uint32 pictureCount; - struct PictureEntry { - uint32 offset; - uint32 size; - uint16 id; - uint16 type; - uint16 width; - uint16 height; - } *entries; - - Common::File picFile; - } _pictureFile; - - Graphics::Surface *_backBuffer; - Graphics::PixelFormat _pixelFormat; - Common::Rect _viewport; - - int _enableDrawingTimeSimulation; - uint32 _nextAllowedDrawTime; - static const uint _constantDrawDelay = 10; // ms - static const uint _proportionalDrawDelay = 500; // pixels per ms -}; - -#endif // ENABLE_MYST - -#ifdef ENABLE_RIVEN - -class MohawkEngine_Riven; - -class RivenGraphics : public GraphicsManager { -public: - RivenGraphics(MohawkEngine_Riven *vm); - ~RivenGraphics(); - - void copyImageToScreen(uint16, uint32, uint32, uint32, uint32); - void updateScreen(Common::Rect updateRect = Common::Rect(0, 0, 608, 392)); - bool _updatesEnabled; - Common::Array<uint16> _activatedPLSTs; - void drawPLST(uint16 x); - void drawRect(Common::Rect rect, bool active); - void drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect); - void drawExtrasImage(uint16 id, Common::Rect dstRect); - - // Water Effect - void scheduleWaterEffect(uint16); - void clearWaterEffects(); - bool runScheduledWaterEffects(); - - // Transitions - void scheduleTransition(uint16 id, Common::Rect rect = Common::Rect(0, 0, 608, 392)); - void runScheduledTransition(); - void fadeToBlack(); - void setTransitionSpeed(uint32 speed) { _transitionSpeed = speed; } - - // Inventory - void showInventory(); - void hideInventory(); - - // Credits - void beginCredits(); - void updateCredits(); - uint getCurCreditsImage() { return _creditsImage; } - -protected: - MohawkSurface *decodeImage(uint16 id); - MohawkEngine *getVM() { return (MohawkEngine *)_vm; } - -private: - MohawkEngine_Riven *_vm; - MohawkBitmap *_bitmapDecoder; - - // Water Effects - struct SFXERecord { - // Record values - uint16 frameCount; - Common::Rect rect; - uint16 speed; - Common::Array<Common::SeekableReadStream *> frameScripts; - - // Cur frame - uint16 curFrame; - uint32 lastFrameTime; - }; - Common::Array<SFXERecord> _waterEffects; - - // Transitions - int16 _scheduledTransition; - Common::Rect _transitionRect; - uint32 _transitionSpeed; - - // Inventory - void clearInventoryArea(); - void drawInventoryImage(uint16 id, const Common::Rect *rect); - bool _inventoryDrawn; - - // Screen Related - Graphics::Surface *_mainScreen; - bool _dirtyScreen; - Graphics::PixelFormat _pixelFormat; - void clearMainScreen(); - - // Credits - uint _creditsImage, _creditsPos; -}; - -#endif // ENABLE_RIVEN - -class LBGraphics : public GraphicsManager { -public: - LBGraphics(MohawkEngine_LivingBooks *vm, uint16 width, uint16 height); - ~LBGraphics(); - - void setPalette(uint16 id); - void copyOffsetAnimImageToScreen(uint16 image, int left = 0, int top = 0); - bool imageIsTransparentAt(uint16 image, bool useOffsets, int x, int y); - -protected: - MohawkSurface *decodeImage(uint16 id); - MohawkEngine *getVM() { return (MohawkEngine *)_vm; } - -private: - MohawkBitmap *_bmpDecoder; - MohawkEngine_LivingBooks *_vm; -}; - -#ifdef ENABLE_CSTIME - -class MohawkEngine_CSTime; - -class CSTimeGraphics : public GraphicsManager { -public: - CSTimeGraphics(MohawkEngine_CSTime *vm); - ~CSTimeGraphics(); - - void drawRect(Common::Rect rect, byte color); - -protected: - MohawkSurface *decodeImage(uint16 id); - Common::Array<MohawkSurface *> decodeImages(uint16 id); - MohawkEngine *getVM() { return (MohawkEngine *)_vm; } - -private: - MohawkBitmap *_bmpDecoder; - MohawkEngine_CSTime *_vm; -}; - -#endif - } // End of namespace Mohawk #endif diff --git a/engines/mohawk/livingbooks.h b/engines/mohawk/livingbooks.h index 23ab0acfb1..91d6a8cd30 100644 --- a/engines/mohawk/livingbooks.h +++ b/engines/mohawk/livingbooks.h @@ -25,7 +25,7 @@ #include "mohawk/mohawk.h" #include "mohawk/console.h" -#include "mohawk/graphics.h" +#include "mohawk/livingbooks_graphics.h" #include "mohawk/sound.h" #include "common/config-file.h" diff --git a/engines/mohawk/livingbooks_graphics.cpp b/engines/mohawk/livingbooks_graphics.cpp new file mode 100644 index 0000000000..fb764fa15b --- /dev/null +++ b/engines/mohawk/livingbooks_graphics.cpp @@ -0,0 +1,102 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "mohawk/resource.h" +#include "mohawk/livingbooks.h" +#include "mohawk/livingbooks_graphics.h" + +#include "common/substream.h" +#include "common/system.h" +#include "engines/util.h" +#include "graphics/palette.h" + +namespace Mohawk { + +LBGraphics::LBGraphics(MohawkEngine_LivingBooks *vm, uint16 width, uint16 height) : GraphicsManager(), _vm(vm) { + _bmpDecoder = _vm->isPreMohawk() ? new LivingBooksBitmap_v1() : new MohawkBitmap(); + + initGraphics(width, height, true); +} + +LBGraphics::~LBGraphics() { + delete _bmpDecoder; +} + +MohawkSurface *LBGraphics::decodeImage(uint16 id) { + if (_vm->isPreMohawk()) + return _bmpDecoder->decodeImage(_vm->wrapStreamEndian(ID_BMAP, id)); + + return _bmpDecoder->decodeImage(_vm->getResource(ID_TBMP, id)); +} + +void LBGraphics::copyOffsetAnimImageToScreen(uint16 image, int left, int top) { + MohawkSurface *mhkSurface = findImage(image); + + left -= mhkSurface->getOffsetX(); + top -= mhkSurface->getOffsetY(); + + GraphicsManager::copyAnimImageToScreen(image, left, top); +} + +bool LBGraphics::imageIsTransparentAt(uint16 image, bool useOffsets, int x, int y) { + MohawkSurface *mhkSurface = findImage(image); + + if (useOffsets) { + x += mhkSurface->getOffsetX(); + y += mhkSurface->getOffsetY(); + } + + if (x < 0 || y < 0) + return true; + + Graphics::Surface *surface = mhkSurface->getSurface(); + if (x >= surface->w || y >= surface->h) + return true; + + return *(byte *)surface->getBasePtr(x, y) == 0; +} + +void LBGraphics::setPalette(uint16 id) { + // Old Living Books games use the old CTBL-style palette format while newer + // games use the better tPAL format which can store partial palettes. + if (_vm->isPreMohawk()) { + Common::SeekableSubReadStreamEndian *ctblStream = _vm->wrapStreamEndian(ID_CTBL, id); + uint16 colorCount = ctblStream->readUint16(); + byte *palette = new byte[colorCount * 3]; + + for (uint16 i = 0; i < colorCount; i++) { + palette[i * 3 + 0] = ctblStream->readByte(); + palette[i * 3 + 1] = ctblStream->readByte(); + palette[i * 3 + 2] = ctblStream->readByte(); + ctblStream->readByte(); + } + + delete ctblStream; + + _vm->_system->getPaletteManager()->setPalette(palette, 0, colorCount); + delete[] palette; + } else { + GraphicsManager::setPalette(id); + } +} + +} // End of namespace Mohawk diff --git a/engines/mohawk/livingbooks_graphics.h b/engines/mohawk/livingbooks_graphics.h new file mode 100644 index 0000000000..3e2609750a --- /dev/null +++ b/engines/mohawk/livingbooks_graphics.h @@ -0,0 +1,52 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef MOHAWK_LIVINGBOOKS_GRAPHICS_H +#define MOHAWK_LIVINGBOOKS_GRAPHICS_H + +#include "mohawk/graphics.h" + +namespace Mohawk { + +class MohawkEngine_LivingBooks; + +class LBGraphics : public GraphicsManager { +public: + LBGraphics(MohawkEngine_LivingBooks *vm, uint16 width, uint16 height); + ~LBGraphics(); + + void setPalette(uint16 id); + void copyOffsetAnimImageToScreen(uint16 image, int left = 0, int top = 0); + bool imageIsTransparentAt(uint16 image, bool useOffsets, int x, int y); + +protected: + MohawkSurface *decodeImage(uint16 id); + MohawkEngine *getVM() { return (MohawkEngine *)_vm; } + +private: + MohawkBitmap *_bmpDecoder; + MohawkEngine_LivingBooks *_vm; +}; + +} // End of namespace Mohawk + +#endif diff --git a/engines/mohawk/module.mk b/engines/mohawk/module.mk index 882f3966b2..83e541e3e4 100644 --- a/engines/mohawk/module.mk +++ b/engines/mohawk/module.mk @@ -10,6 +10,7 @@ MODULE_OBJS = \ installer_archive.o \ livingbooks.o \ livingbooks_code.o \ + livingbooks_graphics.o \ livingbooks_lbx.o \ mohawk.o \ resource.o \ @@ -22,6 +23,7 @@ MODULE_OBJS += \ cstime.o \ cstime_cases.o \ cstime_game.o \ + cstime_graphics.o \ cstime_ui.o \ cstime_view.o endif @@ -30,6 +32,7 @@ ifdef ENABLE_MYST MODULE_OBJS += \ myst.o \ myst_areas.o \ + myst_graphics.o \ myst_scripts.o \ myst_state.o \ resource_cache.o \ @@ -51,6 +54,7 @@ ifdef ENABLE_RIVEN MODULE_OBJS += \ riven.o \ riven_external.o \ + riven_graphics.o \ riven_saveload.o \ riven_scripts.o \ riven_vars.o diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index eeb4594f3c..c22b30ad4d 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -27,9 +27,9 @@ #include "common/textconsole.h" #include "mohawk/cursors.h" -#include "mohawk/graphics.h" #include "mohawk/myst.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/myst_scripts.h" #include "mohawk/myst_state.h" #include "mohawk/dialogs.h" diff --git a/engines/mohawk/myst_areas.cpp b/engines/mohawk/myst_areas.cpp index 294fcea2f1..fe33ceaa9b 100644 --- a/engines/mohawk/myst_areas.cpp +++ b/engines/mohawk/myst_areas.cpp @@ -20,8 +20,8 @@ * */ -#include "mohawk/graphics.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/myst_scripts.h" #include "mohawk/sound.h" #include "mohawk/video.h" diff --git a/engines/mohawk/myst_areas.h b/engines/mohawk/myst_areas.h index 297a7cc92b..62af5ec4cf 100644 --- a/engines/mohawk/myst_areas.h +++ b/engines/mohawk/myst_areas.h @@ -20,11 +20,13 @@ * */ +#ifndef MYST_AREAS_H +#define MYST_AREAS_H + #include "mohawk/myst.h" #include "mohawk/video.h" -#ifndef MYST_AREAS_H -#define MYST_AREAS_H +#include "common/rect.h" namespace Mohawk { diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp new file mode 100644 index 0000000000..151390580f --- /dev/null +++ b/engines/mohawk/myst_graphics.cpp @@ -0,0 +1,493 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "mohawk/myst.h" +#include "mohawk/myst_graphics.h" +#include "mohawk/resource.h" + +#include "common/substream.h" +#include "common/system.h" +#include "common/textconsole.h" +#include "engines/util.h" +#include "graphics/jpeg.h" +#include "graphics/pict.h" + +namespace Mohawk { + +MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { + _bmpDecoder = new MystBitmap(); + + _viewport = Common::Rect(544, 332); + + // The original version of Myst could run in 8bpp color too. + // However, it dithered videos to 8bpp and they looked considerably + // worse (than they already did :P). So we're not even going to + // support 8bpp mode in Myst (Myst ME required >8bpp anyway). + initGraphics(_viewport.width(), _viewport.height(), true, NULL); // What an odd screen size! + + _pixelFormat = _vm->_system->getScreenFormat(); + + if (_pixelFormat.bytesPerPixel == 1) + error("Myst requires greater than 256 colors to run"); + + if (_vm->getFeatures() & GF_ME) { + _jpegDecoder = new Graphics::JPEG(); + _pictDecoder = new Graphics::PictDecoder(_pixelFormat); + } else { + _jpegDecoder = NULL; + _pictDecoder = NULL; + } + + _pictureFile.entries = NULL; + + // Initialize our buffer + _backBuffer = new Graphics::Surface(); + _backBuffer->create(_vm->_system->getWidth(), _vm->_system->getHeight(), _pixelFormat); + + _nextAllowedDrawTime = _vm->_system->getMillis(); + _enableDrawingTimeSimulation = 0; +} + +MystGraphics::~MystGraphics() { + delete _bmpDecoder; + delete _jpegDecoder; + delete _pictDecoder; + delete[] _pictureFile.entries; + + _backBuffer->free(); + delete _backBuffer; +} + +static const char *s_picFileNames[] = { + "CHpics", + "", + "", + "DUpics", + "INpics", + "", + "MEpics", + "MYpics", + "SEpics", + "", + "", + "STpics" +}; + +void MystGraphics::loadExternalPictureFile(uint16 stack) { + if (_vm->getPlatform() != Common::kPlatformMacintosh) + return; + + if (_pictureFile.picFile.isOpen()) + _pictureFile.picFile.close(); + delete[] _pictureFile.entries; + + if (!scumm_stricmp(s_picFileNames[stack], "")) + return; + + if (!_pictureFile.picFile.open(s_picFileNames[stack])) + error ("Could not open external picture file \'%s\'", s_picFileNames[stack]); + + _pictureFile.pictureCount = _pictureFile.picFile.readUint32BE(); + _pictureFile.entries = new PictureFile::PictureEntry[_pictureFile.pictureCount]; + + for (uint32 i = 0; i < _pictureFile.pictureCount; i++) { + _pictureFile.entries[i].offset = _pictureFile.picFile.readUint32BE(); + _pictureFile.entries[i].size = _pictureFile.picFile.readUint32BE(); + _pictureFile.entries[i].id = _pictureFile.picFile.readUint16BE(); + _pictureFile.entries[i].type = _pictureFile.picFile.readUint16BE(); + _pictureFile.entries[i].width = _pictureFile.picFile.readUint16BE(); + _pictureFile.entries[i].height = _pictureFile.picFile.readUint16BE(); + } +} + +MohawkSurface *MystGraphics::decodeImage(uint16 id) { + MohawkSurface *mhkSurface = 0; + + // Myst ME uses JPEG/PICT images instead of compressed Windows Bitmaps for room images, + // though there are a few weird ones that use that format. For further nonsense with images, + // the Macintosh version stores images in external "picture files." We check them before + // going to check for a PICT resource. + if (_vm->getFeatures() & GF_ME && _vm->getPlatform() == Common::kPlatformMacintosh && _pictureFile.picFile.isOpen()) { + for (uint32 i = 0; i < _pictureFile.pictureCount; i++) + if (_pictureFile.entries[i].id == id) { + if (_pictureFile.entries[i].type == 0) { + Common::SeekableReadStream *stream = new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size); + + if (!_jpegDecoder->read(stream)) + error("Could not decode Myst ME Mac JPEG"); + + mhkSurface = new MohawkSurface(_jpegDecoder->getSurface(_pixelFormat)); + delete stream; + } else if (_pictureFile.entries[i].type == 1) { + mhkSurface = new MohawkSurface(_pictDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size))); + } else + error ("Unknown Picture File type %d", _pictureFile.entries[i].type); + break; + } + } + + // We're not using the external Mac files, so it's time to delve into the main Mohawk + // archives. However, we still don't know if it's a PICT or WDIB resource. If it's Myst + // ME it's most likely a PICT, and if it's original it's definitely a WDIB. However, + // Myst ME throws us another curve ball in that PICT resources can contain WDIB's instead + // of PICT's. + if (!mhkSurface) { + bool isPict = false; + Common::SeekableReadStream *dataStream = NULL; + + if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, id)) { + // The PICT resource exists. However, it could still contain a MystBitmap + // instead of a PICT image... + dataStream = _vm->getResource(ID_PICT, id); + } else // No PICT, so the WDIB must exist. Let's go grab it. + dataStream = _vm->getResource(ID_WDIB, id); + + if (_vm->getFeatures() & GF_ME) { + // Here we detect whether it's really a PICT or a WDIB. Since a MystBitmap + // would be compressed, there's no way to detect for the BM without a hack. + // So, we search for the PICT version opcode for detection. + dataStream->seek(512 + 10); // 512 byte pict header + isPict = (dataStream->readUint32BE() == 0x001102FF); + dataStream->seek(0); + } + + if (isPict) + mhkSurface = new MohawkSurface(_pictDecoder->decodeImage(dataStream)); + else { + mhkSurface = _bmpDecoder->decodeImage(dataStream); + mhkSurface->convertToTrueColor(); + } + } + + assert(mhkSurface); + return mhkSurface; +} + +void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest) { + Graphics::Surface *surface = findImage(image)->getSurface(); + + // Make sure the image is bottom aligned in the dest rect + dest.top = dest.bottom - MIN<int>(surface->h, dest.height()); + + // Convert from bitmap coordinates to surface coordinates + uint16 top = surface->h - (src.top + MIN<int>(surface->h, dest.height())); + + // Do not draw the top pixels if the image is too tall + if (dest.height() > _viewport.height()) + top += dest.height() - _viewport.height(); + + // Clip the destination rect to the screen + if (dest.right > _vm->_system->getWidth() || dest.bottom > _vm->_system->getHeight()) + dest.debugPrint(4, "Clipping destination rect to the screen"); + dest.right = CLIP<int>(dest.right, 0, _vm->_system->getWidth()); + dest.bottom = CLIP<int>(dest.bottom, 0, _vm->_system->getHeight()); + + uint16 width = MIN<int>(surface->w, dest.width()); + uint16 height = MIN<int>(surface->h, dest.height()); + + // Clamp Width and Height to within src surface dimensions + if (src.left + width > surface->w) + width = surface->w - src.left; + if (src.top + height > surface->h) + height = surface->h - src.top; + + debug(3, "MystGraphics::copyImageSectionToScreen()"); + debug(3, "\tImage: %d", image); + debug(3, "\tsrc.left: %d", src.left); + debug(3, "\tsrc.top: %d", src.top); + debug(3, "\tdest.left: %d", dest.left); + debug(3, "\tdest.top: %d", dest.top); + debug(3, "\twidth: %d", width); + debug(3, "\theight: %d", height); + + simulatePreviousDrawDelay(dest); + + _vm->_system->copyRectToScreen((byte *)surface->getBasePtr(src.left, top), surface->pitch, dest.left, dest.top, width, height); +} + +void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest) { + Graphics::Surface *surface = findImage(image)->getSurface(); + + // Make sure the image is bottom aligned in the dest rect + dest.top = dest.bottom - MIN<int>(surface->h, dest.height()); + + // Convert from bitmap coordinates to surface coordinates + uint16 top = surface->h - (src.top + MIN<int>(surface->h, dest.height())); + + // Do not draw the top pixels if the image is too tall + if (dest.height() > _viewport.height()) { + top += dest.height() - _viewport.height(); + } + + // Clip the destination rect to the screen + if (dest.right > _vm->_system->getWidth() || dest.bottom > _vm->_system->getHeight()) + dest.debugPrint(4, "Clipping destination rect to the screen"); + dest.right = CLIP<int>(dest.right, 0, _vm->_system->getWidth()); + dest.bottom = CLIP<int>(dest.bottom, 0, _vm->_system->getHeight()); + + uint16 width = MIN<int>(surface->w, dest.width()); + uint16 height = MIN<int>(surface->h, dest.height()); + + // Clamp Width and Height to within src surface dimensions + if (src.left + width > surface->w) + width = surface->w - src.left; + if (src.top + height > surface->h) + height = surface->h - src.top; + + debug(3, "MystGraphics::copyImageSectionToBackBuffer()"); + debug(3, "\tImage: %d", image); + debug(3, "\tsrc.left: %d", src.left); + debug(3, "\tsrc.top: %d", src.top); + debug(3, "\tdest.left: %d", dest.left); + debug(3, "\tdest.top: %d", dest.top); + debug(3, "\twidth: %d", width); + debug(3, "\theight: %d", height); + + for (uint16 i = 0; i < height; i++) + memcpy(_backBuffer->getBasePtr(dest.left, i + dest.top), surface->getBasePtr(src.left, top + i), width * surface->format.bytesPerPixel); +} + +void MystGraphics::copyImageToScreen(uint16 image, Common::Rect dest) { + copyImageSectionToScreen(image, Common::Rect(544, 333), dest); +} + +void MystGraphics::copyImageToBackBuffer(uint16 image, Common::Rect dest) { + copyImageSectionToBackBuffer(image, Common::Rect(544, 333), dest); +} + +void MystGraphics::copyBackBufferToScreen(Common::Rect r) { + r.clip(_viewport); + + simulatePreviousDrawDelay(r); + + _vm->_system->copyRectToScreen((byte *)_backBuffer->getBasePtr(r.left, r.top), _backBuffer->pitch, r.left, r.top, r.width(), r.height()); +} + +void MystGraphics::runTransition(uint16 type, Common::Rect rect, uint16 steps, uint16 delay) { + + // Do not artificially delay during transitions + int oldEnableDrawingTimeSimulation = _enableDrawingTimeSimulation; + _enableDrawingTimeSimulation = 0; + + switch (type) { + case 0: { + debugC(kDebugScript, "Left to Right"); + + uint16 step = (rect.right - rect.left) / steps; + Common::Rect area = rect; + for (uint i = 0; i < steps; i++) { + area.left = rect.left + step * i; + area.right = area.left + step; + + _vm->_system->delayMillis(delay); + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + if (area.right < rect.right) { + area.left = area.right; + area.right = rect.right; + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + } + break; + case 1: { + debugC(kDebugScript, "Right to Left"); + + uint16 step = (rect.right - rect.left) / steps; + Common::Rect area = rect; + for (uint i = 0; i < steps; i++) { + area.right = rect.right - step * i; + area.left = area.right - step; + + _vm->_system->delayMillis(delay); + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + if (area.left > rect.left) { + area.right = area.left; + area.left = rect.left; + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + } + break; + case 5: { + debugC(kDebugScript, "Top to Bottom"); + + uint16 step = (rect.bottom - rect.top) / steps; + Common::Rect area = rect; + for (uint i = 0; i < steps; i++) { + area.top = rect.top + step * i; + area.bottom = area.top + step; + + _vm->_system->delayMillis(delay); + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + if (area.bottom < rect.bottom) { + area.top = area.bottom; + area.bottom = rect.bottom; + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + } + break; + case 6: { + debugC(kDebugScript, "Bottom to Top"); + + uint16 step = (rect.bottom - rect.top) / steps; + Common::Rect area = rect; + for (uint i = 0; i < steps; i++) { + area.bottom = rect.bottom - step * i; + area.top = area.bottom - step; + + _vm->_system->delayMillis(delay); + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + if (area.top > rect.top) { + area.bottom = area.top; + area.top = rect.top; + + copyBackBufferToScreen(area); + _vm->_system->updateScreen(); + } + } + break; + default: + warning("Unknown Update Direction"); + + //TODO: Replace minimal implementation + copyBackBufferToScreen(rect); + _vm->_system->updateScreen(); + break; + } + + _enableDrawingTimeSimulation = oldEnableDrawingTimeSimulation; +} + +void MystGraphics::drawRect(Common::Rect rect, RectState state) { + rect.clip(_viewport); + + // Useful with debugging. Shows where hotspots are on the screen and whether or not they're active. + if (!rect.isValidRect() || rect.width() == 0 || rect.height() == 0) + return; + + Graphics::Surface *screen = _vm->_system->lockScreen(); + + if (state == kRectEnabled) + screen->frameRect(rect, _pixelFormat.RGBToColor(0, 255, 0)); + else if (state == kRectUnreachable) + screen->frameRect(rect, _pixelFormat.RGBToColor(0, 0, 255)); + else + screen->frameRect(rect, _pixelFormat.RGBToColor(255, 0, 0)); + + _vm->_system->unlockScreen(); +} + +void MystGraphics::drawLine(const Common::Point &p1, const Common::Point &p2, uint32 color) { + _backBuffer->drawLine(p1.x, p1.y, p2.x, p2.y, color); +} + +void MystGraphics::enableDrawingTimeSimulation(bool enable) { + if (enable) + _enableDrawingTimeSimulation++; + else + _enableDrawingTimeSimulation--; + + if (_enableDrawingTimeSimulation < 0) + _enableDrawingTimeSimulation = 0; +} + +void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) { + uint32 time = 0; + + if (_enableDrawingTimeSimulation) { + time = _vm->_system->getMillis(); + + // Do not draw anything new too quickly after the previous draw call + // so that images stay at least a little while on screen + // This is enabled only for scripted draw calls + if (time < _nextAllowedDrawTime) + _vm->_system->delayMillis(_nextAllowedDrawTime - time); + } + + // Next draw call allowed at DELAY + AERA * COEFF milliseconds from now + time = _vm->_system->getMillis(); + _nextAllowedDrawTime = time + _constantDrawDelay + dest.height() * dest.width() / _proportionalDrawDelay; +} + +void MystGraphics::copyBackBufferToScreenWithSaturation(int16 saturation) { + Graphics::Surface *screen = _vm->_system->lockScreen(); + + for (uint16 y = 0; y < _viewport.height(); y++) + for (uint16 x = 0; x < _viewport.width(); x++) { + uint32 color; + uint8 r, g, b; + + if (_pixelFormat.bytesPerPixel == 2) + color = *(const uint16 *)_backBuffer->getBasePtr(x, y); + else + color = *(const uint32 *)_backBuffer->getBasePtr(x, y); + + _pixelFormat.colorToRGB(color, r, g, b); + + r = CLIP<int16>((int16)r - saturation, 0, 255); + g = CLIP<int16>((int16)g - saturation, 0, 255); + b = CLIP<int16>((int16)b - saturation, 0, 255); + + color = _pixelFormat.RGBToColor(r, g, b); + + if (_pixelFormat.bytesPerPixel == 2) { + uint16 *dst = (uint16 *)screen->getBasePtr(x, y); + *dst = color; + } else { + uint32 *dst = (uint32 *)screen->getBasePtr(x, y); + *dst = color; + } + } + + _vm->_system->unlockScreen(); + _vm->_system->updateScreen(); +} + +void MystGraphics::fadeToBlack() { + for (int16 i = 0; i < 256; i += 32) { + copyBackBufferToScreenWithSaturation(i); + } +} + +void MystGraphics::fadeFromBlack() { + for (int16 i = 256; i >= 0; i -= 32) { + copyBackBufferToScreenWithSaturation(i); + } +} + +} // End of namespace Mohawk diff --git a/engines/mohawk/myst_graphics.h b/engines/mohawk/myst_graphics.h new file mode 100644 index 0000000000..e2b02db5fc --- /dev/null +++ b/engines/mohawk/myst_graphics.h @@ -0,0 +1,102 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef MOHAWK_MYST_GRAPHICS_H +#define MOHAWK_MYST_GRAPHICS_H + +#include "mohawk/graphics.h" + +#include "common/file.h" + +namespace Graphics { +class JPEG; +class PictDecoder; +} + +namespace Mohawk { + +class MystBitmap; +class MohawkEngine_Myst; + +enum RectState { + kRectEnabled, + kRectDisabled, + kRectUnreachable +}; + +class MystGraphics : public GraphicsManager { +public: + MystGraphics(MohawkEngine_Myst*); + ~MystGraphics(); + + void loadExternalPictureFile(uint16 stack); + void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest); + void copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest); + void copyImageToScreen(uint16 image, Common::Rect dest); + void copyImageToBackBuffer(uint16 image, Common::Rect dest); + void copyBackBufferToScreen(Common::Rect r); + void runTransition(uint16 type, Common::Rect rect, uint16 steps, uint16 delay); + void drawRect(Common::Rect rect, RectState state); + void drawLine(const Common::Point &p1, const Common::Point &p2, uint32 color); + void enableDrawingTimeSimulation(bool enable); + void fadeToBlack(); + void fadeFromBlack(); + +protected: + MohawkSurface *decodeImage(uint16 id); + MohawkEngine *getVM() { return (MohawkEngine *)_vm; } + void simulatePreviousDrawDelay(const Common::Rect &dest); + void copyBackBufferToScreenWithSaturation(int16 saturation); + +private: + MohawkEngine_Myst *_vm; + MystBitmap *_bmpDecoder; + Graphics::PictDecoder *_pictDecoder; + Graphics::JPEG *_jpegDecoder; + + struct PictureFile { + uint32 pictureCount; + struct PictureEntry { + uint32 offset; + uint32 size; + uint16 id; + uint16 type; + uint16 width; + uint16 height; + } *entries; + + Common::File picFile; + } _pictureFile; + + Graphics::Surface *_backBuffer; + Graphics::PixelFormat _pixelFormat; + Common::Rect _viewport; + + int _enableDrawingTimeSimulation; + uint32 _nextAllowedDrawTime; + static const uint _constantDrawDelay = 10; // ms + static const uint _proportionalDrawDelay = 500; // pixels per ms +}; + +} // End of namespace Mohawk + +#endif diff --git a/engines/mohawk/myst_scripts.cpp b/engines/mohawk/myst_scripts.cpp index ca8e985491..b9353312c7 100644 --- a/engines/mohawk/myst_scripts.cpp +++ b/engines/mohawk/myst_scripts.cpp @@ -22,8 +22,8 @@ #include "mohawk/cursors.h" #include "mohawk/myst.h" -#include "mohawk/graphics.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/myst_scripts.h" #include "mohawk/sound.h" #include "mohawk/video.h" diff --git a/engines/mohawk/myst_stacks/channelwood.cpp b/engines/mohawk/myst_stacks/channelwood.cpp index 9ca47cc92a..069281f5dc 100644 --- a/engines/mohawk/myst_stacks/channelwood.cpp +++ b/engines/mohawk/myst_stacks/channelwood.cpp @@ -22,8 +22,8 @@ #include "mohawk/cursors.h" #include "mohawk/myst.h" -#include "mohawk/graphics.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/myst_state.h" #include "mohawk/sound.h" #include "mohawk/video.h" diff --git a/engines/mohawk/myst_stacks/credits.cpp b/engines/mohawk/myst_stacks/credits.cpp index ad593e3542..192e55d5e3 100644 --- a/engines/mohawk/myst_stacks/credits.cpp +++ b/engines/mohawk/myst_stacks/credits.cpp @@ -21,8 +21,8 @@ */ #include "mohawk/myst.h" -#include "mohawk/graphics.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/sound.h" #include "mohawk/video.h" #include "mohawk/myst_stacks/credits.h" diff --git a/engines/mohawk/myst_stacks/demo.cpp b/engines/mohawk/myst_stacks/demo.cpp index fbad7dc384..29a12571fd 100644 --- a/engines/mohawk/myst_stacks/demo.cpp +++ b/engines/mohawk/myst_stacks/demo.cpp @@ -21,8 +21,8 @@ */ #include "mohawk/cursors.h" -#include "mohawk/graphics.h" #include "mohawk/myst.h" +#include "mohawk/myst_graphics.h" #include "mohawk/myst_stacks/demo.h" #include "common/system.h" diff --git a/engines/mohawk/myst_stacks/intro.cpp b/engines/mohawk/myst_stacks/intro.cpp index 7d3179fa24..0af386f51f 100644 --- a/engines/mohawk/myst_stacks/intro.cpp +++ b/engines/mohawk/myst_stacks/intro.cpp @@ -21,8 +21,8 @@ */ #include "mohawk/myst.h" -#include "mohawk/graphics.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/myst_state.h" #include "mohawk/sound.h" #include "mohawk/video.h" diff --git a/engines/mohawk/myst_stacks/mechanical.cpp b/engines/mohawk/myst_stacks/mechanical.cpp index 12d9dc7e2f..79de03308c 100644 --- a/engines/mohawk/myst_stacks/mechanical.cpp +++ b/engines/mohawk/myst_stacks/mechanical.cpp @@ -22,8 +22,8 @@ #include "mohawk/cursors.h" #include "mohawk/myst.h" -#include "mohawk/graphics.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/myst_state.h" #include "mohawk/sound.h" #include "mohawk/video.h" diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp index b3222e0322..c1ddc74c82 100644 --- a/engines/mohawk/myst_stacks/myst.cpp +++ b/engines/mohawk/myst_stacks/myst.cpp @@ -22,8 +22,8 @@ #include "mohawk/cursors.h" #include "mohawk/myst.h" -#include "mohawk/graphics.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/myst_state.h" #include "mohawk/sound.h" #include "mohawk/video.h" diff --git a/engines/mohawk/myst_stacks/preview.cpp b/engines/mohawk/myst_stacks/preview.cpp index 31e22bb8c5..0b8dcf897a 100644 --- a/engines/mohawk/myst_stacks/preview.cpp +++ b/engines/mohawk/myst_stacks/preview.cpp @@ -22,8 +22,8 @@ #include "mohawk/cursors.h" #include "mohawk/myst.h" -#include "mohawk/graphics.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/sound.h" #include "mohawk/video.h" #include "mohawk/myst_stacks/preview.h" diff --git a/engines/mohawk/myst_stacks/slides.cpp b/engines/mohawk/myst_stacks/slides.cpp index 794793e49c..c0bb400db1 100644 --- a/engines/mohawk/myst_stacks/slides.cpp +++ b/engines/mohawk/myst_stacks/slides.cpp @@ -22,8 +22,8 @@ #include "mohawk/cursors.h" #include "mohawk/myst.h" -#include "mohawk/graphics.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/sound.h" #include "mohawk/video.h" #include "mohawk/myst_stacks/slides.h" diff --git a/engines/mohawk/myst_stacks/stoneship.cpp b/engines/mohawk/myst_stacks/stoneship.cpp index 6d54d0c586..ef228e62f3 100644 --- a/engines/mohawk/myst_stacks/stoneship.cpp +++ b/engines/mohawk/myst_stacks/stoneship.cpp @@ -22,8 +22,8 @@ #include "mohawk/cursors.h" #include "mohawk/myst.h" -#include "mohawk/graphics.h" #include "mohawk/myst_areas.h" +#include "mohawk/myst_graphics.h" #include "mohawk/myst_state.h" #include "mohawk/sound.h" #include "mohawk/video.h" diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index 3e2fa4f979..38b38d2c56 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -27,11 +27,11 @@ #include "common/system.h" #include "mohawk/cursors.h" -#include "mohawk/graphics.h" #include "mohawk/installer_archive.h" #include "mohawk/resource.h" #include "mohawk/riven.h" #include "mohawk/riven_external.h" +#include "mohawk/riven_graphics.h" #include "mohawk/riven_saveload.h" #include "mohawk/dialogs.h" #include "mohawk/sound.h" diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp index 9e1365f8da..8dfc74ebf0 100644 --- a/engines/mohawk/riven_external.cpp +++ b/engines/mohawk/riven_external.cpp @@ -21,9 +21,9 @@ */ #include "mohawk/cursors.h" -#include "mohawk/graphics.h" #include "mohawk/riven.h" #include "mohawk/riven_external.h" +#include "mohawk/riven_graphics.h" #include "mohawk/sound.h" #include "mohawk/video.h" diff --git a/engines/mohawk/riven_graphics.cpp b/engines/mohawk/riven_graphics.cpp new file mode 100644 index 0000000000..9415e51412 --- /dev/null +++ b/engines/mohawk/riven_graphics.cpp @@ -0,0 +1,445 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "mohawk/resource.h" +#include "mohawk/riven.h" +#include "mohawk/riven_graphics.h" + +#include "common/system.h" +#include "engines/util.h" + +namespace Mohawk { + +RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : GraphicsManager(), _vm(vm) { + _bitmapDecoder = new MohawkBitmap(); + + // Give me the best you've got! + initGraphics(608, 436, true, NULL); + _pixelFormat = _vm->_system->getScreenFormat(); + + if (_pixelFormat.bytesPerPixel == 1) + error("Riven requires greater than 256 colors to run"); + + // The actual game graphics only take up the first 392 rows. The inventory + // occupies the rest of the screen and we don't use the buffer to hold that. + _mainScreen = new Graphics::Surface(); + _mainScreen->create(608, 392, _pixelFormat); + + _updatesEnabled = true; + _scheduledTransition = -1; // no transition + _dirtyScreen = false; + _inventoryDrawn = false; + + _creditsImage = 302; + _creditsPos = 0; +} + +RivenGraphics::~RivenGraphics() { + _mainScreen->free(); + delete _mainScreen; + delete _bitmapDecoder; +} + +MohawkSurface *RivenGraphics::decodeImage(uint16 id) { + MohawkSurface *surface = _bitmapDecoder->decodeImage(_vm->getResource(ID_TBMP, id)); + surface->convertToTrueColor(); + return surface; +} + +void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uint32 right, uint32 bottom) { + Graphics::Surface *surface = findImage(image)->getSurface(); + + // Clip the width to fit on the screen. Fixes some images. + if (left + surface->w > 608) + surface->w = 608 - left; + + for (uint16 i = 0; i < surface->h; i++) + memcpy(_mainScreen->getBasePtr(left, i + top), surface->getBasePtr(0, i), surface->w * surface->format.bytesPerPixel); + + _dirtyScreen = true; +} + +void RivenGraphics::drawPLST(uint16 x) { + Common::SeekableReadStream* plst = _vm->getResource(ID_PLST, _vm->getCurCard()); + uint16 recordCount = plst->readUint16BE(); + + for (uint16 i = 0; i < recordCount; i++) { + uint16 index = plst->readUint16BE(); + uint16 id = plst->readUint16BE(); + uint16 left = plst->readUint16BE(); + uint16 top = plst->readUint16BE(); + uint16 right = plst->readUint16BE(); + uint16 bottom = plst->readUint16BE(); + + // We are also checking here to make sure we haven't drawn the image yet on screen. + // This fixes problems with drawing PLST 1 twice and some other images twice. PLST + // 1 is sometimes not called by the scripts, so some cards don't appear if we don't + // draw PLST 1 each time. This "hack" is here to catch any PLST attempting to draw + // twice. There should never be a problem with doing it this way. + if (index == x && !(Common::find(_activatedPLSTs.begin(), _activatedPLSTs.end(), x) != _activatedPLSTs.end())) { + debug(0, "Drawing image %d", id); + copyImageToScreen(id, left, top, right, bottom); + _activatedPLSTs.push_back(x); + break; + } + } + + delete plst; +} + +void RivenGraphics::updateScreen(Common::Rect updateRect) { + if (_updatesEnabled) { + _vm->runUpdateScreenScript(); + + if (_dirtyScreen) { + _activatedPLSTs.clear(); + + // Copy to screen if there's no transition. Otherwise transition. ;) + if (_scheduledTransition < 0) + _vm->_system->copyRectToScreen((byte *)_mainScreen->getBasePtr(updateRect.left, updateRect.top), _mainScreen->pitch, updateRect.left, updateRect.top, updateRect.width(), updateRect.height()); + else + runScheduledTransition(); + + // Finally, update the screen. + _vm->_system->updateScreen(); + _dirtyScreen = false; + } + } +} + +void RivenGraphics::scheduleWaterEffect(uint16 sfxeID) { + Common::SeekableReadStream *sfxeStream = _vm->getResource(ID_SFXE, sfxeID); + + if (sfxeStream->readUint16BE() != 'SL') + error ("Unknown sfxe tag"); + + // Read in header info + SFXERecord sfxeRecord; + sfxeRecord.frameCount = sfxeStream->readUint16BE(); + uint32 offsetTablePosition = sfxeStream->readUint32BE(); + sfxeRecord.rect.left = sfxeStream->readUint16BE(); + sfxeRecord.rect.top = sfxeStream->readUint16BE(); + sfxeRecord.rect.right = sfxeStream->readUint16BE(); + sfxeRecord.rect.bottom = sfxeStream->readUint16BE(); + sfxeRecord.speed = sfxeStream->readUint16BE(); + // Skip the rest of the fields... + + // Read in offsets + sfxeStream->seek(offsetTablePosition); + uint32 *frameOffsets = new uint32[sfxeRecord.frameCount]; + for (uint16 i = 0; i < sfxeRecord.frameCount; i++) + frameOffsets[i] = sfxeStream->readUint32BE(); + sfxeStream->seek(frameOffsets[0]); + + // Read in the scripts + for (uint16 i = 0; i < sfxeRecord.frameCount; i++) + sfxeRecord.frameScripts.push_back(sfxeStream->readStream((i == sfxeRecord.frameCount - 1) ? sfxeStream->size() - frameOffsets[i] : frameOffsets[i + 1] - frameOffsets[i])); + + // Set it to the first frame + sfxeRecord.curFrame = 0; + sfxeRecord.lastFrameTime = 0; + + delete[] frameOffsets; + delete sfxeStream; + _waterEffects.push_back(sfxeRecord); +} + +void RivenGraphics::clearWaterEffects() { + _waterEffects.clear(); +} + +bool RivenGraphics::runScheduledWaterEffects() { + // Don't run the effect if it's disabled + if (_vm->_vars["waterenabled"] == 0) + return false; + + Graphics::Surface *screen = NULL; + + for (uint16 i = 0; i < _waterEffects.size(); i++) { + if (_vm->_system->getMillis() > _waterEffects[i].lastFrameTime + 1000 / _waterEffects[i].speed) { + // Lock the screen! + if (!screen) + screen = _vm->_system->lockScreen(); + + // Make sure the script is at the starting point + Common::SeekableReadStream *script = _waterEffects[i].frameScripts[_waterEffects[i].curFrame]; + if (script->pos() != 0) + script->seek(0); + + // Run script + uint16 curRow = 0; + for (uint16 op = script->readUint16BE(); op != 4; op = script->readUint16BE()) { + if (op == 1) { // Increment Row + curRow++; + } else if (op == 3) { // Copy Pixels + uint16 dstLeft = script->readUint16BE(); + uint16 srcLeft = script->readUint16BE(); + uint16 srcTop = script->readUint16BE(); + uint16 rowWidth = script->readUint16BE(); + memcpy ((byte *)screen->getBasePtr(dstLeft, curRow + _waterEffects[i].rect.top), (byte *)_mainScreen->getBasePtr(srcLeft, srcTop), rowWidth * _pixelFormat.bytesPerPixel); + } else if (op != 4) { // End of Script + error ("Unknown SFXE opcode %d", op); + } + } + + // Increment frame + _waterEffects[i].curFrame++; + if (_waterEffects[i].curFrame == _waterEffects[i].frameCount) + _waterEffects[i].curFrame = 0; + + // Set the new time + _waterEffects[i].lastFrameTime = _vm->_system->getMillis(); + } + } + + // Unlock the screen if it has been locked and return true to update the screen + if (screen) { + _vm->_system->unlockScreen(); + return true; + } + + return false; +} + +void RivenGraphics::scheduleTransition(uint16 id, Common::Rect rect) { + _scheduledTransition = id; + _transitionRect = rect; +} + +void RivenGraphics::runScheduledTransition() { + if (_scheduledTransition < 0) // No transition is scheduled + return; + + // TODO: There's a lot to be done here... + + // Note: Transitions 0-11 are actual transitions, but none are used in-game. + // There's no point in implementing them if they're not used. These extra + // transitions were found by hacking scripts. + + switch (_scheduledTransition) { + case 0: // Swipe Left + case 1: // Swipe Right + case 2: // Swipe Up + case 3: // Swipe Down + case 12: // Pan Left + case 13: // Pan Right + case 14: // Pan Up + case 15: // Pan Down + case 16: // Dissolve + case 17: // Dissolve (tspit CARD 155) + break; + default: + if (_scheduledTransition >= 4 && _scheduledTransition <= 11) + error("Found unused transition %d", _scheduledTransition); + else + error("Found unknown transition %d", _scheduledTransition); + } + + // For now, just copy the image to screen without doing any transition. + _vm->_system->copyRectToScreen((byte *)_mainScreen->pixels, _mainScreen->pitch, 0, 0, _mainScreen->w, _mainScreen->h); + _vm->_system->updateScreen(); + + _scheduledTransition = -1; // Clear scheduled transition +} + +void RivenGraphics::clearMainScreen() { + _mainScreen->fillRect(Common::Rect(0, 0, 608, 392), _pixelFormat.RGBToColor(0, 0, 0)); +} + +void RivenGraphics::fadeToBlack() { + // The transition speed is forced to best here + setTransitionSpeed(kRivenTransitionSpeedBest); + scheduleTransition(16); + clearMainScreen(); + runScheduledTransition(); +} + +void RivenGraphics::showInventory() { + // Don't redraw the inventory + if (_inventoryDrawn) + return; + + // Clear the inventory area + clearInventoryArea(); + + // Draw the demo's exit button + if (_vm->getFeatures() & GF_DEMO) { + // extras.mhk tBMP 101 contains "EXIT" instead of Atrus' journal in the demo! + // The demo's extras.mhk contains all the other inventory/marble/credits image + // but has hacked tBMP 101 with "EXIT". *sigh* + drawInventoryImage(101, g_demoExitRect); + } else { + // We don't want to show the inventory on setup screens or in other journals. + if (_vm->getCurStack() == aspit) + return; + + // There are three books and three vars. We have three different + // combinations. At the start you have just Atrus' journal. Later, + // you get Catherine's journal and the trap book. Near the end, + // you lose the trap book and have just the two journals. + + bool hasCathBook = _vm->_vars["acathbook"] != 0; + bool hasTrapBook = _vm->_vars["atrapbook"] != 0; + + if (!hasCathBook) { + drawInventoryImage(101, g_atrusJournalRect1); + } else if (!hasTrapBook) { + drawInventoryImage(101, g_atrusJournalRect2); + drawInventoryImage(102, g_cathJournalRect2); + } else { + drawInventoryImage(101, g_atrusJournalRect3); + drawInventoryImage(102, g_cathJournalRect3); + drawInventoryImage(100, g_trapBookRect3); + } + } + + _vm->_system->updateScreen(); + _inventoryDrawn = true; +} + +void RivenGraphics::hideInventory() { + // Don't hide the inventory twice + if (!_inventoryDrawn) + return; + + // Clear the area + clearInventoryArea(); + + _inventoryDrawn = false; +} + +void RivenGraphics::clearInventoryArea() { + // Clear the inventory area + static const Common::Rect inventoryRect = Common::Rect(0, 392, 608, 436); + + // Lock the screen + Graphics::Surface *screen = _vm->_system->lockScreen(); + + // Fill the inventory area with black + screen->fillRect(inventoryRect, _pixelFormat.RGBToColor(0, 0, 0)); + + _vm->_system->unlockScreen(); +} + +void RivenGraphics::drawInventoryImage(uint16 id, const Common::Rect *rect) { + MohawkSurface *mhkSurface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id)); + mhkSurface->convertToTrueColor(); + Graphics::Surface *surface = mhkSurface->getSurface(); + + _vm->_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, rect->left, rect->top, surface->w, surface->h); + + delete mhkSurface; +} + +void RivenGraphics::drawRect(Common::Rect rect, bool active) { + // Useful with debugging. Shows where hotspots are on the screen and whether or not they're active. + Graphics::Surface *screen = _vm->_system->lockScreen(); + + if (active) + screen->frameRect(rect, _pixelFormat.RGBToColor(0, 255, 0)); + else + screen->frameRect(rect, _pixelFormat.RGBToColor(255, 0, 0)); + + _vm->_system->unlockScreen(); +} + +void RivenGraphics::drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect) { + // Draw tBMP id from srcRect to dstRect + Graphics::Surface *surface = findImage(id)->getSurface(); + + assert(srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height()); + + for (uint16 i = 0; i < srcRect.height(); i++) + memcpy(_mainScreen->getBasePtr(dstRect.left, i + dstRect.top), surface->getBasePtr(srcRect.left, i + srcRect.top), srcRect.width() * surface->format.bytesPerPixel); + + _dirtyScreen = true; +} + +void RivenGraphics::drawExtrasImage(uint16 id, Common::Rect dstRect) { + MohawkSurface *mhkSurface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id)); + mhkSurface->convertToTrueColor(); + Graphics::Surface *surface = mhkSurface->getSurface(); + + assert(dstRect.width() == surface->w); + + for (uint16 i = 0; i < surface->h; i++) + memcpy(_mainScreen->getBasePtr(dstRect.left, i + dstRect.top), surface->getBasePtr(0, i), surface->pitch); + + delete mhkSurface; + _dirtyScreen = true; +} + +void RivenGraphics::beginCredits() { + // Clear the old cache + clearCache(); + + // Now cache all the credits images + for (uint16 i = 302; i <= 320; i++) { + MohawkSurface *surface = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, i)); + surface->convertToTrueColor(); + addImageToCache(i, surface); + } + + // And clear our screen too + clearMainScreen(); +} + +void RivenGraphics::updateCredits() { + if ((_creditsImage == 303 || _creditsImage == 304) && _creditsPos == 0) + fadeToBlack(); + + if (_creditsImage < 304) { + // For the first two credit images, they are faded from black to the image and then out again + scheduleTransition(16); + + Graphics::Surface *frame = findImage(_creditsImage++)->getSurface(); + + for (int y = 0; y < frame->h; y++) + memcpy(_mainScreen->getBasePtr(124, y), frame->getBasePtr(0, y), frame->pitch); + + runScheduledTransition(); + } else { + // Otheriwse, we're scrolling + // Move the screen up one row + memmove(_mainScreen->pixels, _mainScreen->getBasePtr(0, 1), _mainScreen->pitch * (_mainScreen->h - 1)); + + // Only update as long as we're not before the last frame + // Otherwise, we're just moving up a row (which we already did) + if (_creditsImage <= 320) { + // Copy the next row to the bottom of the screen + Graphics::Surface *frame = findImage(_creditsImage)->getSurface(); + memcpy(_mainScreen->getBasePtr(124, _mainScreen->h - 1), frame->getBasePtr(0, _creditsPos), frame->pitch); + _creditsPos++; + + if (_creditsPos == _mainScreen->h) { + _creditsImage++; + _creditsPos = 0; + } + } + + // Now flush the new screen + _vm->_system->copyRectToScreen((byte *)_mainScreen->pixels, _mainScreen->pitch, 0, 0, _mainScreen->w, _mainScreen->h); + _vm->_system->updateScreen(); + } +} + +} // End of namespace Mohawk diff --git a/engines/mohawk/riven_graphics.h b/engines/mohawk/riven_graphics.h new file mode 100644 index 0000000000..48dda28afd --- /dev/null +++ b/engines/mohawk/riven_graphics.h @@ -0,0 +1,110 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef MOHAWK_RIVEN_GRAPHICS_H +#define MOHAWK_RIVEN_GRAPHICS_H + +#include "mohawk/graphics.h" + +namespace Mohawk { + +class MohawkEngine_Riven; + +class RivenGraphics : public GraphicsManager { +public: + RivenGraphics(MohawkEngine_Riven *vm); + ~RivenGraphics(); + + void copyImageToScreen(uint16, uint32, uint32, uint32, uint32); + void updateScreen(Common::Rect updateRect = Common::Rect(0, 0, 608, 392)); + bool _updatesEnabled; + Common::Array<uint16> _activatedPLSTs; + void drawPLST(uint16 x); + void drawRect(Common::Rect rect, bool active); + void drawImageRect(uint16 id, Common::Rect srcRect, Common::Rect dstRect); + void drawExtrasImage(uint16 id, Common::Rect dstRect); + + // Water Effect + void scheduleWaterEffect(uint16); + void clearWaterEffects(); + bool runScheduledWaterEffects(); + + // Transitions + void scheduleTransition(uint16 id, Common::Rect rect = Common::Rect(0, 0, 608, 392)); + void runScheduledTransition(); + void fadeToBlack(); + void setTransitionSpeed(uint32 speed) { _transitionSpeed = speed; } + + // Inventory + void showInventory(); + void hideInventory(); + + // Credits + void beginCredits(); + void updateCredits(); + uint getCurCreditsImage() { return _creditsImage; } + +protected: + MohawkSurface *decodeImage(uint16 id); + MohawkEngine *getVM() { return (MohawkEngine *)_vm; } + +private: + MohawkEngine_Riven *_vm; + MohawkBitmap *_bitmapDecoder; + + // Water Effects + struct SFXERecord { + // Record values + uint16 frameCount; + Common::Rect rect; + uint16 speed; + Common::Array<Common::SeekableReadStream *> frameScripts; + + // Cur frame + uint16 curFrame; + uint32 lastFrameTime; + }; + Common::Array<SFXERecord> _waterEffects; + + // Transitions + int16 _scheduledTransition; + Common::Rect _transitionRect; + uint32 _transitionSpeed; + + // Inventory + void clearInventoryArea(); + void drawInventoryImage(uint16 id, const Common::Rect *rect); + bool _inventoryDrawn; + + // Screen Related + Graphics::Surface *_mainScreen; + bool _dirtyScreen; + Graphics::PixelFormat _pixelFormat; + void clearMainScreen(); + + // Credits + uint _creditsImage, _creditsPos; +}; + +} // End of namespace Mohawk + +#endif diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp index 6e3e9a34dc..352a018990 100644 --- a/engines/mohawk/riven_scripts.cpp +++ b/engines/mohawk/riven_scripts.cpp @@ -21,9 +21,9 @@ */ #include "mohawk/cursors.h" -#include "mohawk/graphics.h" #include "mohawk/riven.h" #include "mohawk/riven_external.h" +#include "mohawk/riven_graphics.h" #include "mohawk/riven_scripts.h" #include "mohawk/sound.h" #include "mohawk/video.h" diff --git a/engines/mohawk/riven_scripts.h b/engines/mohawk/riven_scripts.h index a871f0afa0..6df4a2e523 100644 --- a/engines/mohawk/riven_scripts.h +++ b/engines/mohawk/riven_scripts.h @@ -27,8 +27,6 @@ #include "common/ptr.h" #include "common/textconsole.h" -class MohawkEngine_Riven; - #define DECLARE_OPCODE(x) void x(uint16 op, uint16 argc, uint16 *argv) namespace Mohawk { @@ -50,6 +48,7 @@ enum { kStoredOpcodeScript // This is ScummVM-only to denote the script from a storeMovieOpcode() call }; +class MohawkEngine_Riven; class RivenScript; class RivenScript { |