aboutsummaryrefslogtreecommitdiff
path: root/engines/mohawk/graphics.cpp
diff options
context:
space:
mode:
authorEugene Sandulenko2009-12-29 23:18:24 +0000
committerEugene Sandulenko2009-12-29 23:18:24 +0000
commit0ea022d076c491d802431ee90b658d5e8c06d0e0 (patch)
tree23953ed8dbd2c1cc798b6aa9aa51df93c7041c7d /engines/mohawk/graphics.cpp
parent5f1d2a88b51af43d8903866b46a424fe556abb3c (diff)
downloadscummvm-rg350-0ea022d076c491d802431ee90b658d5e8c06d0e0.tar.gz
scummvm-rg350-0ea022d076c491d802431ee90b658d5e8c06d0e0.tar.bz2
scummvm-rg350-0ea022d076c491d802431ee90b658d5e8c06d0e0.zip
Add Mohawk engine code. Part 1/3: main code.
svn-id: r46727
Diffstat (limited to 'engines/mohawk/graphics.cpp')
-rw-r--r--engines/mohawk/graphics.cpp739
1 files changed, 739 insertions, 0 deletions
diff --git a/engines/mohawk/graphics.cpp b/engines/mohawk/graphics.cpp
new file mode 100644
index 0000000000..ee9fe702de
--- /dev/null
+++ b/engines/mohawk/graphics.cpp
@@ -0,0 +1,739 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "mohawk/file.h"
+#include "mohawk/graphics.h"
+#include "mohawk/myst.h"
+#include "mohawk/riven.h"
+#include "mohawk/riven_cursors.h"
+
+#include "graphics/cursorman.h"
+#include "graphics/primitives.h"
+#include "gui/message.h"
+
+namespace Mohawk {
+
+Graphics::Surface *ImageData::getSurface() {
+ Graphics::PixelFormat pixelFormat = g_system->getScreenFormat();
+ Graphics::Surface *surface = new Graphics::Surface();
+ surface->create(_surface->w, _surface->h, pixelFormat.bytesPerPixel);
+
+ if (_surface->bytesPerPixel == 1) {
+ assert(_palette);
+
+ for (uint16 i = 0; i < _surface->h; i++) {
+ for (uint16 j = 0; j < _surface->w; j++) {
+ byte palIndex = *((byte *)_surface->pixels + i * _surface->pitch + j);
+ byte r = _palette[palIndex * 4];
+ byte g = _palette[palIndex * 4 + 1];
+ byte b = _palette[palIndex * 4 + 2];
+ if (pixelFormat.bytesPerPixel == 2)
+ *((uint16 *)surface->getBasePtr(j, i)) = pixelFormat.RGBToColor(r, g, b);
+ else
+ *((uint32 *)surface->getBasePtr(j, i)) = pixelFormat.RGBToColor(r, g, b);
+ }
+ }
+ } else
+ memcpy(surface->pixels, _surface->pixels, _surface->w * _surface->h * _surface->bytesPerPixel);
+
+ return surface;
+}
+
+MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : _vm(vm) {
+ _bmpDecoder = new MystBitmap();
+
+ // 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(544, 333, 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 MystJPEG();
+ _pictDecoder = new MystPICT(_jpegDecoder);
+ } else {
+ _jpegDecoder = NULL;
+ _pictDecoder = NULL;
+ }
+
+ _pictureFile.entries = NULL;
+}
+
+MystGraphics::~MystGraphics() {
+ delete _bmpDecoder;
+ delete _jpegDecoder;
+ delete _pictDecoder;
+}
+
+static const char* 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(picFileNames[stack], ""))
+ return;
+
+ if (!_pictureFile.picFile.open(picFileNames[stack]))
+ error ("Could not open external picture file \'%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();
+ }
+}
+
+void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest) {
+ // 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());
+
+ Graphics::Surface *surface = NULL;
+
+
+ // 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 == image) {
+ if (_pictureFile.entries[i].type == 0)
+ surface = _jpegDecoder->decodeImage(new Common::SeekableSubReadStream(&_pictureFile.picFile, _pictureFile.entries[i].offset, _pictureFile.entries[i].offset + _pictureFile.entries[i].size));
+ else if (_pictureFile.entries[i].type == 1)
+ surface = _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 (!surface) {
+ bool isPict = false;
+ Common::SeekableReadStream *dataStream = NULL;
+
+ if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, image)) {
+ // The PICT resource exists. However, it could still contain a MystBitmap
+ // instead of a PICT image...
+ dataStream = _vm->getRawData(ID_PICT, image);
+
+ // 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);
+ } else // No PICT, so the WDIB must exist. Let's go grab it.
+ dataStream = _vm->getRawData(ID_WDIB, image);
+
+ if (isPict)
+ surface = _pictDecoder->decodeImage(dataStream);
+ else {
+ ImageData *imageData = _bmpDecoder->decodeImage(dataStream);
+ surface = imageData->getSurface();
+ delete imageData;
+ }
+ }
+
+ debug(3, "Image Blit:");
+ debug(3, "src.x: %d", src.left);
+ debug(3, "src.y: %d", src.top);
+ debug(3, "dest.x: %d", dest.left);
+ debug(3, "dest.y: %d", dest.top);
+ debug(3, "width: %d", src.width());
+ debug(3, "height: %d", src.height());
+
+ if (surface) {
+ uint16 width = MIN<int>(surface->w, dest.width());
+ uint16 height = MIN<int>(surface->h, dest.height());
+ _vm->_system->copyRectToScreen((byte *)surface->getBasePtr(src.left, src.top), surface->pitch, dest.left, dest.top, width, height);
+ surface->free();
+ delete surface;
+ }
+
+ // FIXME: Remove this and update only at certain points
+ _vm->_system->updateScreen();
+}
+
+void MystGraphics::copyImageToScreen(uint16 image, Common::Rect dest) {
+ copyImageSectionToScreen(image, Common::Rect(0, 0, 544, 333), dest);
+}
+
+void MystGraphics::showCursor(void) {
+ CursorMan.showMouse(true);
+ _vm->_needsUpdate = true;
+}
+
+void MystGraphics::hideCursor(void) {
+ CursorMan.showMouse(false);
+ _vm->_needsUpdate = true;
+}
+
+void MystGraphics::changeCursor(uint16 cursor) {
+ // Both Myst and Myst ME use the "MystBitmap" format for cursor images.
+ ImageData *data = _bmpDecoder->decodeImage(_vm->getRawData(ID_WDIB, cursor));
+ Common::SeekableReadStream *clrcStream = _vm->getRawData(ID_CLRC, cursor);
+ uint16 hotspotX = clrcStream->readUint16LE();
+ uint16 hotspotY = clrcStream->readUint16LE();
+ delete clrcStream;
+
+ // Myst ME stores some cursors as 24bpp images instead of 8bpp
+ if (data->_surface->bytesPerPixel == 1) {
+ CursorMan.replaceCursor((byte *)data->_surface->pixels, data->_surface->w, data->_surface->h, hotspotX, hotspotY, 0);
+ CursorMan.replaceCursorPalette(data->_palette, 0, 256);
+ } else
+ CursorMan.replaceCursor((byte *)data->_surface->pixels, data->_surface->w, data->_surface->h, hotspotX, hotspotY, 0xFFFFFFFF, 1, &_pixelFormat);
+
+ _vm->_needsUpdate = true;
+}
+
+void MystGraphics::drawRect(Common::Rect rect, bool active) {
+ // Useful with debugging. Shows where hotspots are on the screen and whether or not they're active.
+ if (rect.left < 0 || rect.top < 0 || rect.right > 544 || rect.bottom > 333 || !rect.isValidRect() || rect.width() == 0 || rect.height() == 0)
+ return;
+
+ 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();
+}
+
+RivenGraphics::RivenGraphics(MohawkEngine_Riven* vm) : _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.bytesPerPixel);
+
+ _updatesEnabled = true;
+ _scheduledTransition = -1; // no transition
+ _dirtyScreen = false;
+ _inventoryDrawn = false;
+}
+
+RivenGraphics::~RivenGraphics() {
+ _mainScreen->free();
+ delete _mainScreen;
+ delete _bitmapDecoder;
+}
+
+void RivenGraphics::copyImageToScreen(uint16 image, uint32 left, uint32 top, uint32 right, uint32 bottom) {
+ // First, decode the image and get the high color surface
+ ImageData *imageData = _bitmapDecoder->decodeImage(_vm->getRawData(ID_TBMP, image));
+ Graphics::Surface *surface = imageData->getSurface();
+ delete imageData;
+
+ // 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->bytesPerPixel);
+
+ surface->free();
+ delete surface;
+
+ _dirtyScreen = true;
+}
+
+void RivenGraphics::drawPLST(uint16 x) {
+ Common::SeekableReadStream* plst = _vm->getRawData(ID_PLST, _vm->getCurCard());
+ uint16 index, id, left, top, right, bottom;
+ uint16 recordCount = plst->readUint16BE();
+
+ for (uint16 i = 0; i < recordCount; i++) {
+ index = plst->readUint16BE();
+ id = plst->readUint16BE();
+ left = plst->readUint16BE();
+ top = plst->readUint16BE();
+ right = plst->readUint16BE();
+ 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() {
+ 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->pixels, _mainScreen->pitch, 0, 0, _mainScreen->w, _mainScreen->h);
+ else
+ runScheduledTransition();
+
+ // Finally, update the screen.
+ _vm->_system->updateScreen();
+ _dirtyScreen = false;
+ }
+ }
+}
+
+void RivenGraphics::scheduleWaterEffect(uint16 sfxeID) {
+ Common::SeekableReadStream *sfxeStream = _vm->getRawData(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->matchVarToString("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 12: // Pan Left
+ warning ("STUB: Pan left");
+ break;
+ case 13: // Pan Right
+ warning ("STUB: Pan right");
+ break;
+ case 14: // Pan Up
+ warning ("STUB: Pan up");
+ break;
+ case 15: // Pan Down
+ warning ("STUB: Pan down");
+ break;
+ case 16: // Dissolve
+ case 17: // Dissolve (tspit CARD 155)
+ warning ("STUB: Dissolve");
+ break;
+ default:
+ if (_scheduledTransition < 12)
+ 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
+}
+
+// TODO: Marble Cursors/Palettes
+void RivenGraphics::changeCursor(uint16 num) {
+ // All of Riven's cursors are hardcoded. See riven_cursors.h for these definitions.
+
+ switch (num) {
+ case 1002:
+ // Zip Mode
+ CursorMan.replaceCursor(zipModeCursor, 16, 16, 8, 8, 0);
+ CursorMan.replaceCursorPalette(zipModeCursorPalette, 1, ARRAYSIZE(zipModeCursorPalette) / 4);
+ break;
+ case 2003:
+ // Hand Over Object
+ CursorMan.replaceCursor(objectHandCursor, 16, 16, 8, 8, 0);
+ CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ break;
+ case 2004:
+ // Grabbing/Using Object
+ CursorMan.replaceCursor(grabbingHandCursor, 13, 13, 6, 6, 0);
+ CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ break;
+ case 3000:
+ // Standard Hand
+ CursorMan.replaceCursor(standardHandCursor, 15, 16, 6, 0, 0);
+ CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ break;
+ case 3001:
+ // Pointing Left
+ CursorMan.replaceCursor(pointingLeftCursor, 15, 13, 0, 3, 0);
+ CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ break;
+ case 3002:
+ // Pointing Right
+ CursorMan.replaceCursor(pointingRightCursor, 15, 13, 14, 3, 0);
+ CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ break;
+ case 3003:
+ // Pointing Down (Palm Up)
+ CursorMan.replaceCursor(pointingDownCursorPalmUp, 13, 16, 3, 15, 0);
+ CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ break;
+ case 3004:
+ // Pointing Up (Palm Up)
+ CursorMan.replaceCursor(pointingUpCursorPalmUp, 13, 16, 3, 0, 0);
+ CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ break;
+ case 3005:
+ // Pointing Left (Curved)
+ CursorMan.replaceCursor(pointingLeftCursorBent, 15, 13, 0, 5, 0);
+ CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ break;
+ case 3006:
+ // Pointing Right (Curved)
+ CursorMan.replaceCursor(pointingRightCursorBent, 15, 13, 14, 5, 0);
+ CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ break;
+ case 3007:
+ // Pointing Down (Palm Down)
+ CursorMan.replaceCursor(pointingDownCursorPalmDown, 15, 16, 7, 15, 0);
+ CursorMan.replaceCursorPalette(handCursorPalette, 1, ARRAYSIZE(handCursorPalette) / 4);
+ break;
+ case 4001:
+ // Red Marble
+ break;
+ case 4002:
+ // Orange Marble
+ break;
+ case 4003:
+ // Yellow Marble
+ break;
+ case 4004:
+ // Green Marble
+ break;
+ case 4005:
+ // Blue Marble
+ break;
+ case 4006:
+ // Purple Marble
+ break;
+ case 5000:
+ // Pellet
+ CursorMan.replaceCursor(pelletCursor, 8, 8, 4, 4, 0);
+ CursorMan.replaceCursorPalette(pelletCursorPalette, 1, ARRAYSIZE(pelletCursorPalette) / 4);
+ break;
+ case 9000:
+ // Hide Cursor
+ CursorMan.showMouse(false);
+ break;
+ default:
+ error ("Cursor %d does not exist!", num);
+ }
+
+ if (num != 9000) // Show Cursor
+ CursorMan.showMouse(true);
+
+ // Should help in cases where we need to hide the cursor immediately.
+ _vm->_system->updateScreen();
+}
+
+void RivenGraphics::showInventory() {
+ // Don't redraw the inventory
+ if (_inventoryDrawn)
+ return;
+
+ // Clear the inventory area
+ clearInventoryArea();
+
+ // The demo doesn't have the inventory system and we don't want
+ // to show the inventory on setup screens or in other journals.
+ if (_vm->getFeatures() & GF_DEMO || _vm->getCurStack() == aspit)
+ return;
+
+ // There are three books and three vars. However, there's only
+ // a possible two combinations. Either you have only Atrus'
+ // journal or you have all three books.
+ // bool hasAtrusBook = *_vm->matchVarToString("aatrusbook") != 0;
+ bool hasCathBook = *_vm->matchVarToString("acathbook") != 0;
+ // bool hasTrapBook = *_vm->matchVarToString("atrapbook") != 0;
+
+ if (!hasCathBook) {
+ drawInventoryImage(101, atrusJournalRectSolo);
+ } else {
+ drawInventoryImage(101, atrusJournalRect);
+ drawInventoryImage(102, cathJournalRect);
+ drawInventoryImage(100, trapBookRect);
+ }
+
+ _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, Common::Rect rect) {
+ ImageData *imageData = _bitmapDecoder->decodeImage(_vm->getExtrasResource(ID_TBMP, id));
+ Graphics::Surface *surface = imageData->getSurface();
+ delete imageData;
+
+ _vm->_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, rect.left, rect.top, surface->w, surface->h);
+
+ surface->free();
+ delete surface;
+}
+
+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();
+}
+
+LBGraphics::LBGraphics(MohawkEngine_LivingBooks *vm) : _vm(vm) {
+ _bmpDecoder = (_vm->getGameType() == GType_OLDLIVINGBOOKS) ? new OldMohawkBitmap() : new MohawkBitmap();
+ _palette = new byte[256 * 4];
+ memset(_palette, 0, 256 * 4);
+}
+
+LBGraphics::~LBGraphics() {
+ delete _bmpDecoder;
+ delete[] _palette;
+}
+
+void LBGraphics::copyImageToScreen(uint16 image, uint16 left, uint16 right) {
+ if (_vm->getGameType() == GType_OLDLIVINGBOOKS) {
+ // Drawing images in the old format isn't supported (yet)
+ ImageData *imageData = _bmpDecoder->decodeImage(_vm->wrapStreamEndian(ID_BMAP, image));
+ delete imageData;
+ } else {
+ ImageData *imageData = _bmpDecoder->decodeImage(_vm->getRawData(ID_TBMP, image));
+ imageData->_palette = _palette;
+ Graphics::Surface *surface = imageData->getSurface();
+ imageData->_palette = NULL; // Unset the palette so it doesn't get deleted
+ delete imageData;
+
+ uint16 width = MIN<int>(surface->w, 640);
+ uint16 height = MIN<int>(surface->h, 480);
+ _vm->_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, left, right, width, height);
+ surface->free();
+ delete surface;
+
+ // FIXME: Remove this and update only at certain points
+ _vm->_system->updateScreen();
+ }
+}
+
+void LBGraphics::setPalette(uint16 id) {
+ // Old Living Books gamnes use the old CTBL-style palette format while newer
+ // games use the better tPAL format which can store partial palettes.
+
+ if (_vm->getGameType() == GType_OLDLIVINGBOOKS) {
+ Common::SeekableSubReadStreamEndian *ctblStream = _vm->wrapStreamEndian(ID_CTBL, id);
+ uint16 colorCount = ctblStream->readUint16();
+
+ for (uint16 i = 0; i < colorCount; i++) {
+ _palette[i * 4] = ctblStream->readByte();
+ _palette[i * 4 + 1] = ctblStream->readByte();
+ _palette[i * 4 + 2] = ctblStream->readByte();
+ _palette[i * 4 + 3] = ctblStream->readByte();
+ }
+
+ delete ctblStream;
+ } else {
+ Common::SeekableReadStream *tpalStream = _vm->getRawData(ID_TPAL, id);
+ uint16 colorStart = tpalStream->readUint16BE();
+ uint16 colorCount = tpalStream->readUint16BE();
+
+ for (uint16 i = colorStart; i < colorStart + colorCount; i++) {
+ _palette[i * 4] = tpalStream->readByte();
+ _palette[i * 4 + 1] = tpalStream->readByte();
+ _palette[i * 4 + 2] = tpalStream->readByte();
+ _palette[i * 4 + 3] = tpalStream->readByte();
+ }
+
+ delete tpalStream;
+ }
+}
+
+} // End of namespace Mohawk