/* 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 "groovie/cursor.h" #include "groovie/groovie.h" #include "common/debug.h" #include "common/archive.h" #include "common/file.h" #include "common/macresman.h" #include "common/textconsole.h" #include "graphics/cursorman.h" namespace Groovie { // Cursor Manager GrvCursorMan::GrvCursorMan(OSystem *system) : _syst(system), _lastTime(0), _current(255), _cursor(NULL), _lastFrame(0) { } GrvCursorMan::~GrvCursorMan() { // Delete the cursors for (uint cursor = 0; cursor < _cursors.size(); cursor++) { delete _cursors[cursor]; } CursorMan.popAllCursors(); } void GrvCursorMan::show(bool visible) { CursorMan.showMouse(visible); } uint8 GrvCursorMan::getStyle() { return _current; } void GrvCursorMan::setStyle(uint8 newStyle) { // Reset the animation _lastFrame = 254; _lastTime = 1; // Save the new cursor _current = newStyle; _cursor = _cursors[newStyle]; // Show the first frame _cursor->enable(); animate(); } void GrvCursorMan::animate() { if (_lastTime) { int newTime = _syst->getMillis(); if (_lastTime - newTime >= 75) { _lastFrame++; _lastFrame %= _cursor->getFrames(); _cursor->showFrame(_lastFrame); _lastTime = _syst->getMillis(); } } } // t7g Cursor class Cursor_t7g : public Cursor { public: Cursor_t7g(uint8 *img, uint8 *pal); void enable(); void showFrame(uint16 frame); private: byte *_img; byte *_pal; }; Cursor_t7g::Cursor_t7g(uint8 *img, uint8 *pal) : _pal(pal) { _width = img[0]; _height = img[1]; _numFrames = img[2]; uint8 elinor1 = img[3]; uint8 elinor2 = img[4]; _img = img + 5; debugC(1, kDebugCursor, "Groovie::Cursor: width: %d, height: %d, frames:%d", _width, _height, _numFrames); debugC(1, kDebugCursor | kDebugUnknown, "Groovie::Cursor: elinor: 0x%02X (%d), 0x%02X (%d)", elinor1, elinor1, elinor2, elinor2); } void Cursor_t7g::enable() { // Apply the palette CursorMan.replaceCursorPalette(_pal, 0, 32); } void Cursor_t7g::showFrame(uint16 frame) { // Set the mouse cursor int offset = _width * _height * frame; CursorMan.replaceCursor((const byte *)_img + offset, _width, _height, _width >> 1, _height >> 1, 0); } // t7g Cursor Manager #define NUM_IMGS 9 static const uint16 cursorDataOffsets[NUM_IMGS] = { 0x0000, 0x182f, 0x3b6d, 0x50cc, 0x6e79, 0x825d, 0x96d7, 0xa455, 0xa776 }; #define NUM_PALS 7 //Pals: 0xb794, 0xb7f4, 0xb854, 0xb8b4, 0xb914, 0xb974, 0xb9d4 #define NUM_STYLES 11 // pyramid is cursor 8, eyes are 9 & 10 const uint GrvCursorMan_t7g::_cursorImg[NUM_STYLES] = {3, 5, 4, 3, 1, 0, 2, 6, 7, 8, 8}; const uint GrvCursorMan_t7g::_cursorPal[NUM_STYLES] = {0, 0, 0, 0, 2, 0, 1, 3, 5, 4, 6}; GrvCursorMan_t7g::GrvCursorMan_t7g(OSystem *system, Common::MacResManager *macResFork) : GrvCursorMan(system) { Common::SeekableReadStream *robgjd = 0; if (macResFork) { // Open the cursors file from the resource fork robgjd = macResFork->getResource("rob.gjd"); } else { // Open the cursors file robgjd = SearchMan.createReadStreamForMember("rob.gjd"); } if (!robgjd) error("Groovie::Cursor: Couldn't open rob.gjd"); // Load the images for (uint imgnum = 0; imgnum < NUM_IMGS; imgnum++) { robgjd->seek(cursorDataOffsets[imgnum]); _images.push_back(loadImage(*robgjd)); } // Load the palettes robgjd->seek(-0x60 * NUM_PALS, SEEK_END); for (uint palnum = 0; palnum < NUM_PALS; palnum++) { _palettes.push_back(loadPalette(*robgjd)); } // Build the cursors for (uint cursor = 0; cursor < NUM_STYLES; cursor++) { Cursor *s = new Cursor_t7g(_images[_cursorImg[cursor]], _palettes[_cursorPal[cursor]]); _cursors.push_back(s); } delete robgjd; } GrvCursorMan_t7g::~GrvCursorMan_t7g() { // Delete the images for (uint img = 0; img < _images.size(); img++) { delete[] _images[img]; } // Delete the palettes for (uint pal = 0; pal < _palettes.size(); pal++) { delete[] _palettes[pal]; } } byte *GrvCursorMan_t7g::loadImage(Common::SeekableReadStream &file) { uint16 decompbytes = 0, offset, i, length; uint8 flagbyte, lengthmask = 0x0F, offsetlen, var_8; byte *cursorStorage = new byte[65536]; uint8 *runningcursor = cursorStorage; bool finished = false; while (!(finished || file.eos())) { flagbyte = file.readByte(); for (i = 1; i <= 8; i++) { if (!file.eos()) { if (flagbyte & 1) { *(runningcursor++) = file.readByte(); decompbytes++; } else { var_8 = file.readByte(); offsetlen = file.readByte(); if (var_8 == 0 && offsetlen == 0) { finished = true; break; } length = (offsetlen & lengthmask) + 3; offsetlen >>= 4; offset = (offsetlen << 8) + var_8; decompbytes += length; for (; length > 0; length--, runningcursor++) { *(runningcursor) = *(runningcursor - offset); } } flagbyte = flagbyte >> 1; } } } return cursorStorage; } byte *GrvCursorMan_t7g::loadPalette(Common::SeekableReadStream &file) { byte *palette = new byte[3 * 32]; file.read(palette, 3 * 32); return palette; } // v2 Cursor class Cursor_v2 : public Cursor { public: Cursor_v2(Common::File &file); ~Cursor_v2(); void enable(); void showFrame(uint16 frame); private: // Currently locked to 16bit byte *_img; Graphics::PixelFormat _format; void decodeFrame(byte *pal, byte *data, byte *dest); }; Cursor_v2::Cursor_v2(Common::File &file) { byte *pal = new byte[0x20 * 3]; _format = g_system->getScreenFormat(); _numFrames = file.readUint16LE(); _width = file.readUint16LE(); _height = file.readUint16LE(); _img = new byte[_width * _height * _numFrames * 4]; debugC(1, kDebugCursor, "Groovie::Cursor: width: %d, height: %d, frames:%d", _width, _height, _numFrames); uint16 tmp16 = file.readUint16LE(); debugC(5, kDebugCursor, "hotspot x?: %d\n", tmp16); tmp16 = file.readUint16LE(); debugC(5, kDebugCursor, "hotspot y?: %d\n", tmp16); int loop2count = file.readUint16LE(); debugC(5, kDebugCursor, "loop2count?: %d\n", loop2count); for (int l = 0; l < loop2count; l++) { tmp16 = file.readUint16LE(); debugC(5, kDebugCursor, "loop2a: %d\n", tmp16); // Index frame can merge to/from? tmp16 = file.readUint16LE(); debugC(5, kDebugCursor, "loop2b: %d\n", tmp16); // Number of frames? } file.read(pal, 0x20 * 3); for (int f = 0; f < _numFrames; f++) { uint32 tmp32 = file.readUint32LE(); debugC(5, kDebugCursor, "loop3: %d\n", tmp32); byte *data = new byte[tmp32]; file.read(data, tmp32); decodeFrame(pal, data, _img + (f * _width * _height * 4)); delete[] data; } delete[] pal; } Cursor_v2::~Cursor_v2() { delete[] _img; } void Cursor_v2::decodeFrame(byte *pal, byte *data, byte *dest) { // Scratch memory byte *tmp = new byte[_width * _height * 4]; byte *ptr = tmp; memset(tmp, 0, _width * _height * 4); byte ctrA = 0, ctrB = 0; byte alpha = 0, palIdx = 0; byte r, g, b; // Start frame decoding for (int y = 0; y < _height; y++) { for (int x = 0; x < _width; x++) { // If both counters are empty if (ctrA == 0 && ctrB == 0) { if (*data & 0x80) { ctrA = (*data++ & 0x7F) + 1; } else { ctrB = *data++ + 1; alpha = *data & 0xE0; palIdx = *data++ & 0x1F; } } if (ctrA) { // Block type A - chunk of non-continuous pixels palIdx = *data & 0x1F; alpha = *data++ & 0xE0; r = *(pal + palIdx); g = *(pal + palIdx + 0x20); b = *(pal + palIdx + 0x40); ctrA--; } else { // Block type B - chunk of continuous pixels r = *(pal + palIdx); g = *(pal + palIdx + 0x20); b = *(pal + palIdx + 0x40); ctrB--; } // Decode pixel if (alpha) { if (alpha != 0xE0) { alpha = ((alpha << 8) / 224); // TODO: The * 0 to be replaced by the component value of each pixel // below, respectively - does blending r = (byte)((alpha * r + (256 - alpha) * 0) >> 8); g = (byte)((alpha * g + (256 - alpha) * 0) >> 8); b = (byte)((alpha * b + (256 - alpha) * 0) >> 8); } *ptr = 1; *(ptr + 1) = r; *(ptr + 2) = g; *(ptr + 3) = b; } ptr += 4; } } // Convert to screen format // NOTE: Currently locked to 32bpp ptr = tmp; for (int y = 0; y < _height; y++) { for (int x = 0; x < _width; x++) { if (*ptr == 1) { *(uint32 *)dest = _format.RGBToColor(*(ptr + 1), *(ptr + 2), *(ptr + 3)); } else { *(uint32 *)dest = 0; } dest += 4; ptr += 4; } } delete[] tmp; } void Cursor_v2::enable() { } void Cursor_v2::showFrame(uint16 frame) { int offset = _width * _height * frame * 4; CursorMan.replaceCursor((const byte *)(_img + offset), _width, _height, _width >> 1, _height >> 1, 0, false, &_format); } // v2 Cursor Manager GrvCursorMan_v2::GrvCursorMan_v2(OSystem *system) : GrvCursorMan(system) { // Open the icons file Common::File iconsFile; if (!iconsFile.open("icons.ph")) error("Groovie::Cursor: Couldn't open icons.ph"); // Verify the signature uint32 tmp32 = iconsFile.readUint32BE(); uint16 tmp16 = iconsFile.readUint16LE(); if (tmp32 != MKTAG('i','c','o','n') || tmp16 != 1) error("Groovie::Cursor: icons.ph signature failed: %s %d", tag2str(tmp32), tmp16); // Read the number of icons uint16 nicons = iconsFile.readUint16LE(); // Read the icons for (int i = 0; i < nicons; i++) { Cursor *s = new Cursor_v2(iconsFile); _cursors.push_back(s); } iconsFile.close(); } GrvCursorMan_v2::~GrvCursorMan_v2() { } void GrvCursorMan_v2::setStyle(uint8 newStyle) { // Cursor 4 is actually cursor 3, but with some changes to alpha blending // (which is currently not handled) GrvCursorMan::setStyle(newStyle == 4 ? 3 : newStyle); } } // End of Groovie namespace