/* 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. * * Additional copyright for this file: * Copyright (C) 1995-1997 Presto Studios, Inc. * * 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 "common/events.h" #include "common/stream.h" #include "common/system.h" #include "graphics/cursorman.h" #include "graphics/surface.h" #include "image/pict.h" #include "pegasus/cursor.h" #include "pegasus/graphics.h" #include "pegasus/pegasus.h" namespace Pegasus { Cursor::Cursor() { _cursorObscured = false; _index = -1; startIdling(); } Cursor::~Cursor() { for (uint32 i = 0; i < _info.size(); i++) { if (_info[i].surface) { _info[i].surface->free(); delete _info[i].surface; } delete[] _info[i].palette; } stopIdling(); } void Cursor::addCursorFrames(uint16 id) { PegasusEngine *vm = (PegasusEngine *)g_engine; Common::SeekableReadStream *cursStream = vm->_resFork->getResource(MKTAG('C', 'u', 'r', 's'), id); if (!cursStream) error("Could not load cursor frames set %d", id); uint16 frameCount = cursStream->readUint16BE(); for (uint16 i = 0; i < frameCount; i++) { CursorInfo info; info.tag = cursStream->readUint16BE(); info.hotspot.x = cursStream->readUint16BE(); info.hotspot.y = cursStream->readUint16BE(); info.surface = 0; info.palette = 0; info.colorCount = 0; _info.push_back(info); } delete cursStream; setCurrentFrameIndex(0); } void Cursor::setCurrentFrameIndex(int32 index) { if (_index != index) { _index = index; if (index != -1) { loadCursorImage(_info[index]); if (_info[index].surface->format.bytesPerPixel == 1) { CursorMan.replaceCursorPalette(_info[index].palette, 0, _info[index].colorCount); CursorMan.replaceCursor(_info[index].surface->getPixels(), _info[index].surface->w, _info[index].surface->h, _info[index].hotspot.x, _info[index].hotspot.y, 0); } else { CursorMan.replaceCursor(_info[index].surface->getPixels(), _info[index].surface->w, _info[index].surface->h, _info[index].hotspot.x, _info[index].hotspot.y, _info[index].surface->format.RGBToColor(0xFF, 0xFF, 0xFF), false, &_info[index].surface->format); } } } } int32 Cursor::getCurrentFrameIndex() const { return _index; } void Cursor::show() { if (!isVisible()) CursorMan.showMouse(true); _cursorObscured = false; } void Cursor::hide() { CursorMan.showMouse(false); setCurrentFrameIndex(0); } void Cursor::hideUntilMoved() { if (!_cursorObscured) { hide(); _cursorObscured = true; } } void Cursor::useIdleTime() { if (g_system->getEventManager()->getMousePos() != _cursorLocation) { _cursorLocation = g_system->getEventManager()->getMousePos(); if (_index != -1 && _cursorObscured) show(); } } void Cursor::getCursorLocation(Common::Point &pt) const { pt = _cursorLocation; } bool Cursor::isVisible() { return CursorMan.isVisible(); } void Cursor::loadCursorImage(CursorInfo &cursorInfo) { if (cursorInfo.surface) return; PegasusEngine *vm = (PegasusEngine *)g_engine; if (vm->isDVD()) { // The DVD version has some higher color PICT images for its cursors Common::SeekableReadStream *pictStream = vm->_resFork->getResource(MKTAG('P', 'I', 'C', 'T'), cursorInfo.tag + 1000); if (pictStream) { Image::PICTDecoder pict; if (!pict.loadStream(*pictStream)) error("Failed to decode cursor PICT %d", cursorInfo.tag + 1000); cursorInfo.surface = pict.getSurface()->convertTo(g_system->getScreenFormat()); delete pictStream; return; } } cursorInfo.surface = new Graphics::Surface(); Common::SeekableReadStream *cicnStream = vm->_resFork->getResource(MKTAG('c', 'i', 'c', 'n'), cursorInfo.tag); if (!cicnStream) error("Failed to find color icon %d", cursorInfo.tag); // PixMap section Image::PICTDecoder::PixMap pixMap = Image::PICTDecoder::readPixMap(*cicnStream); // Mask section cicnStream->readUint32BE(); // mask baseAddr uint16 maskRowBytes = cicnStream->readUint16BE(); // mask rowBytes cicnStream->skip(3 * 2); // mask rect /* uint16 maskHeight = */ cicnStream->readUint16BE(); // Bitmap section cicnStream->readUint32BE(); // baseAddr uint16 rowBytes = cicnStream->readUint16BE(); cicnStream->readUint16BE(); // top cicnStream->readUint16BE(); // left uint16 height = cicnStream->readUint16BE(); // bottom cicnStream->readUint16BE(); // right // Data section cicnStream->readUint32BE(); // icon handle cicnStream->skip(maskRowBytes * height); // FIXME: maskHeight doesn't work here, though the specs say it should cicnStream->skip(rowBytes * height); // Palette section cicnStream->readUint32BE(); // always 0 cicnStream->readUint16BE(); // always 0 cursorInfo.colorCount = cicnStream->readUint16BE() + 1; cursorInfo.palette = new byte[cursorInfo.colorCount * 3]; for (uint16 i = 0; i < cursorInfo.colorCount; i++) { cicnStream->readUint16BE(); cursorInfo.palette[i * 3] = cicnStream->readUint16BE() >> 8; cursorInfo.palette[i * 3 + 1] = cicnStream->readUint16BE() >> 8; cursorInfo.palette[i * 3 + 2] = cicnStream->readUint16BE() >> 8; } // PixMap data if (pixMap.pixelSize == 8) { cursorInfo.surface->create(pixMap.rowBytes, pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8()); cicnStream->read(cursorInfo.surface->getPixels(), pixMap.rowBytes * pixMap.bounds.height()); // While this looks sensible, it actually doesn't work for some cursors // (ie. the 'can grab' hand) //cursorInfo.surface->w = pixMap.bounds.width(); } else if (pixMap.pixelSize == 1) { cursorInfo.surface->create(pixMap.bounds.width(), pixMap.bounds.height(), Graphics::PixelFormat::createFormatCLUT8()); for (int y = 0; y < pixMap.bounds.height(); y++) { byte *line = (byte *)cursorInfo.surface->getBasePtr(0, y); for (int x = 0; x < pixMap.bounds.width();) { byte b = cicnStream->readByte(); for (int i = 0; i < 8; i++) { *line++ = ((b & (1 << (7 - i))) != 0) ? 1 : 0; if (++x == pixMap.bounds.width()) break; } } } } else { error("Unhandled %dbpp cicn images", pixMap.pixelSize); } delete cicnStream; } } // End of namespace Pegasus