Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

cursor.cpp

/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-11-1/engines/scumm/cursor.cpp $
 * $Id: cursor.cpp 30944 2008-02-23 22:50:18Z sev $
 *
 */


#include "common/system.h"
#include "common/util.h"
#include "graphics/cursorman.h"
#include "scumm/bomp.h"
#include "scumm/charset.h"
#include "scumm/intern.h"
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/he/resource_he.h"
#include "scumm/saveload.h"
#include "scumm/scumm.h"

namespace Scumm {

/*
 * Mouse cursor cycle colors (for the default crosshair).
 */
static const byte default_v1_cursor_colors[4] = {
      1, 1, 12, 11
};

static const byte default_cursor_colors[4] = {
      15, 15, 7, 8
};

// 2bpp
static const uint16 default_he_cursor[128] = {
      0x0a80, 0x0000, 0x0000, 0x0000, 0x2568, 0x0000, 0x0000, 0x0000,
      0x9556, 0x8000, 0x0000, 0x0000, 0x9955, 0x6800, 0x0000, 0x0000,
      0x9955, 0x5680, 0x0000, 0x0000, 0x2655, 0x5568, 0x0000, 0x0000,
      0x2695, 0x5556, 0x8000, 0x0000, 0x0995, 0x5555, 0x6800, 0x0000,
      0x09a5, 0x5555, 0x5600, 0x0000, 0x0269, 0x5555, 0x5580, 0x0000,
      0x0269, 0x5555, 0x5560, 0x0000, 0x009a, 0x5555, 0x5560, 0x0000,
      0x009a, 0x9555, 0x5580, 0x0000, 0x0026, 0x9555, 0x5600, 0x0000,
      0x0026, 0xa555, 0x5580, 0x0000, 0x0009, 0xa955, 0x5560, 0x0000,
      0x0009, 0xa9a9, 0x5558, 0x0000, 0x0002, 0x6aaa, 0x5558, 0x0000,
      0x0002, 0x6a9a, 0xaa58, 0x0000, 0x0000, 0x9a66, 0xaa60, 0x0000,
      0x0000, 0x2589, 0xa980, 0x0000, 0x0000, 0x0a02, 0x5600, 0x0000,
      0x0000, 0x0000, 0xa800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
      0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};

static const byte default_v6_cursor[] = {
      0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
      0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
      0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
      0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
      0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
      0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x0F,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
      0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F, 0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0xFF,
      0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x0F,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0xFF,
      0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
      0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
      0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
      0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
      0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0x00,0x0F,0x00, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};

void ScummEngine_v5::animateCursor() {
      if (_cursor.animate) {
            if (!(_cursor.animateIndex & 0x1)) {
                  setBuiltinCursor((_cursor.animateIndex >> 1) & 3);
            }
            _cursor.animateIndex++;
      }
}

void ScummEngine_v6::setCursorHotspot(int x, int y) {
      _cursor.hotspotX = x;
      _cursor.hotspotY = y;
}

void ScummEngine_v6::setCursorTransparency(int a) {
      int i, size;

      size = _cursor.width * _cursor.height;

      for (i = 0; i < size; i++)
            if (_grabbedCursor[i] == (byte)a)
                  _grabbedCursor[i] = 0xFF;

      updateCursor();
}

void ScummEngine::updateCursor() {
      const int transColor = (_game.heversion >= 80) ? 5 : 255;
      CursorMan.replaceCursor(_grabbedCursor, _cursor.width, _cursor.height,
                                          _cursor.hotspotX, _cursor.hotspotY,
                                          (_game.platform == Common::kPlatformNES ? _grabbedCursor[63] : transColor),
                                          (_game.heversion == 70 ? 2 : 1));
}

void ScummEngine_v6::grabCursor(int x, int y, int w, int h) {
      VirtScreen *vs = findVirtScreen(y);

      if (vs == NULL) {
            debug(0, "grabCursor: invalid Y %d", y);
            return;
      }

      setCursorFromBuffer((byte *)vs->pixels + (y - vs->topline) * vs->pitch + x, w, h, vs->pitch);
}

void ScummEngine_v6::setDefaultCursor() {
      setCursorHotspot(7, 6);
      setCursorFromBuffer(default_v6_cursor, 16, 13, 16);
}

void ScummEngine::setCursorFromBuffer(const byte *ptr, int width, int height, int pitch) {
      uint size;
      byte *dst;

      size = width * height;
      if (size > sizeof(_grabbedCursor))
            error("grabCursor: grabbed cursor too big");

      _cursor.width = width;
      _cursor.height = height;
      _cursor.animate = 0;

      dst = _grabbedCursor;
      for (; height; height--) {
            memcpy(dst, ptr, width);
            dst += width;
            ptr += pitch;
      }

      updateCursor();
}

void ScummEngine_v70he::setCursorFromImg(uint img, uint room, uint imgindex) {
      _resExtractor->setCursor(img);
}

void ScummEngine_v70he::setDefaultCursor() {
      const uint16 *src;
      int i, j;
      static const byte palette[] = {0,    0,    0,    0, 
                                                   0xff, 0xff, 0xff, 0,
                                                   0,    0,    0,    0};

      memset(_grabbedCursor, 5, sizeof(_grabbedCursor));

      _cursor.hotspotX = _cursor.hotspotY = 2;
      src = default_he_cursor;

      _cursor.width = 32;
      _cursor.height = 32;

      for (i = 0; i < 32; i++) {
            int p = *src;
            for (j = 0; j < 32; j++) {
                  switch ((p & (0x3 << 14)) >> 14) {
                        case 1:
                              _grabbedCursor[32 * i + j] = 0xfe;
                              break;
                        case 2:
                              _grabbedCursor[32 * i + j] = 0xfd;
                              break;
                        default:
                              break;
                  }
                  p <<= 2;

                  if ((j + 1) % 8 == 0)
                        p = *(++src);
            }
      }

      // Since white color position is not guaranteed
      // we setup our own palette if supported by backend
      CursorMan.replaceCursorPalette(palette, 0xfd, 3);

      updateCursor();
}

void ScummEngine_v6::setCursorFromImg(uint img, uint room, uint imgindex) {
      int w, h;
      const byte *dataptr, *bomp;
      uint32 size;
      FindObjectInRoom foir;
      const ImageHeader *imhd;

      if (room == (uint) - 1)
            room = getObjectRoom(img);

      findObjectInRoom(&foir, foCodeHeader | foImageHeader | foCheckAlreadyLoaded, img, room);
      imhd = (const ImageHeader *)findResourceData(MKID_BE('IMHD'), foir.obim);

      if (_game.version == 8) {
            setCursorHotspot(READ_LE_UINT32(&imhd->v8.hotspot[0].x),
                              READ_LE_UINT32(&imhd->v8.hotspot[0].y));
            w = READ_LE_UINT32(&imhd->v8.width) / 8;
            h = READ_LE_UINT32(&imhd->v8.height) / 8;
      } else if (_game.version == 7) {
            setCursorHotspot(READ_LE_UINT16(&imhd->v7.hotspot[0].x),
                              READ_LE_UINT16(&imhd->v7.hotspot[0].y));
            w = READ_LE_UINT16(&imhd->v7.width) / 8;
            h = READ_LE_UINT16(&imhd->v7.height) / 8;
      } else {
            if (_game.heversion == 0) {
                  setCursorHotspot(READ_LE_UINT16(&imhd->old.hotspot[0].x),
                                  READ_LE_UINT16(&imhd->old.hotspot[0].y));
            }
            w = READ_LE_UINT16(&foir.cdhd->v6.w) / 8;
            h = READ_LE_UINT16(&foir.cdhd->v6.h) / 8;
      }

      dataptr = getObjectImage(foir.obim, imgindex);
      assert(dataptr);
      if (_game.version == 8) {
            bomp = dataptr;
      } else {
            size = READ_BE_UINT32(dataptr + 4);
            if (size > sizeof(_grabbedCursor))
                  error("setCursorFromImg: Cursor image too large");

            bomp = findResource(MKID_BE('BOMP'), dataptr);
      }

      if (bomp != NULL)
            useBompCursor(bomp, w, h);
      else
            useIm01Cursor(dataptr, w, h);
}

void ScummEngine_v6::useIm01Cursor(const byte *im, int w, int h) {
      VirtScreen *vs = &_virtscr[kMainVirtScreen];
      byte *buf, *dst;
      const byte *src;
      int i;

      w *= 8;
      h *= 8;

      // Backup the screen content
      dst = buf = (byte *)malloc(w * h);
      src = vs->getPixels(0, 0);

      for (i = 0; i < h; i++) {
            memcpy(dst, src, w);
            dst += w;
            src += vs->pitch;
      }

      // Do some drawing
      drawBox(0, 0, w - 1, h - 1, 0xFF);

      vs->hasTwoBuffers = false;
      _gdi->disableZBuffer();
      _gdi->drawBitmap(im, vs, _screenStartStrip, 0, w, h, 0, w / 8, 0);
      vs->hasTwoBuffers = true;
      _gdi->enableZBuffer();

      // Grab the data we just drew and setup the cursor with it
      setCursorFromBuffer(vs->getPixels(0, 0), w, h, vs->pitch);

      // Restore the screen content
      src = buf;
      dst = vs->getPixels(0, 0);

      for (i = 0; i < h; i++) {
            memcpy(dst, src, w);
            dst += vs->pitch;
            src += w;
      }

      free(buf);
}

void ScummEngine_v6::useBompCursor(const byte *im, int width, int height) {
      uint size;

      width *= 8;
      height *= 8;

      size = width * height;
      if (size > sizeof(_grabbedCursor))
            error("useBompCursor: cursor too big (%d)", size);

      _cursor.width = width;
      _cursor.height = height;
      _cursor.animate = 0;

      // Skip the header
      if (_game.version == 8) {
            im += 16;
      } else {
            im += 18;
      }
      decompressBomp(_grabbedCursor, im, width, height);

      updateCursor();
}

void ScummEngine_v5::redefineBuiltinCursorFromChar(int index, int chr) {
      // Cursor image in both Looms are based on images from charset.
      if (_game.id != GID_LOOM) {
            // FIXME: Actually: is this opcode ever called by a non-Loom game?
            // Which V3-V5 game besides Loom makes use of custom cursors, ever?
            error("V3--V5 SO_CURSOR_IMAGE(%d,%d) called - tell Fingolfin where you saw this!", index, chr);
      }

      assert(index >= 0 && index < 4);

//    const int oldID = _charset->getCurID();

      if (_game.version == 3) {
            _charset->setCurID(0);
      } else if (_game.version >= 4) {
            _charset->setCurID(1);
      }

      Graphics::Surface s;
      byte buf[16*17];
      memset(buf, 123, 16*17);
      s.pixels = buf;
      s.w = _charset->getCharWidth(chr);
      s.h = _charset->getFontHeight();
      s.pitch = s.w;
      // s.h = 17 for FM-TOWNS Loom Japanese. Fixes bug #1166917
      assert(s.w <= 16 && s.h <= 17);
      s.bytesPerPixel = 1;

      _charset->drawChar(chr, s, 0, 0);

      uint16 *ptr = _cursorImages[index];
      memset(ptr, 0, 17 * sizeof(uint16));
      for (int h = 0; h < s.h; h++) {
            for (int w = 0; w < s.w; w++) {
                  if (buf[s.pitch * h + w] != 123)
                        *ptr |= 1 << (15 - w);
            }
            ptr++;
      }

//    _charset->setCurID(oldID);
}

void ScummEngine_v5::redefineBuiltinCursorHotspot(int index, int x, int y) {
      // Cursor image in both Looms are based on images from charset.
      if (_game.id != GID_LOOM) {
            // FIXME: Actually: is this opcode ever called by a non-Loom game?
            // Which V3-V5 game besides Loom makes use of custom cursors, ever?
            error("V3--V5 SO_CURSOR_HOTSPOT(%d,%d,%d) called - tell Fingolfin where you saw this!", index, x, y);
      }

      assert(index >= 0 && index < 4);

      _cursorHotspots[index * 2] = x;
      _cursorHotspots[index * 2 + 1] = y;
}

void ScummEngine_v2::setBuiltinCursor(int idx) {
      int i, j;
      byte color;

      memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor));

      if (_game.version <= 1)
            color = default_v1_cursor_colors[idx];
      else
            color = default_cursor_colors[idx];

      if (_game.platform == Common::kPlatformNES) {
            _cursor.width = 8;
            _cursor.height = 8;
            _cursor.hotspotX = 0;
            _cursor.hotspotY = 0;

            byte *dst = _grabbedCursor;
            byte *src = &_NESPatTable[0][0xfa * 16];
            byte *palette = _NESPalette[1];

            for (i = 0; i < 8; i++) {
                  byte c0 = src[i];
                  byte c1 = src[i + 8];
                  for (j = 0; j < 8; j++)
                        *dst++ = palette[((c0 >> (7 - j)) & 1) | (((c1 >> (7 - j)) & 1) << 1) | ((idx == 3) ? 4 : 0)];
            }

      } else if (_game.platform == Common::kPlatformAmiga) {
            _cursor.width = 15;
            _cursor.height = 15;
            _cursor.hotspotX = 7;
            _cursor.hotspotY = 7;

            byte *hotspot = _grabbedCursor + _cursor.hotspotY * _cursor.width + _cursor.hotspotX;

            // Crosshair, symmetric
            // TODO: Instead of setting this up via code, we should simply extend
            //       default_cursor_images to contain this shape.
            for (i = 0; i < 5; i++) {
                  *(hotspot - 3 - i) = color;
                  *(hotspot + 3 + i) = color;
                  *(hotspot - _cursor.width * (3 + i)) = color;
                  *(hotspot + _cursor.width * (3 + i)) = color;
            }

            // Arrow heads, diagonal lines
            for (i = 1; i <= 2; i++) {
                  *(hotspot - _cursor.width * i - (3 + i)) = color;
                  *(hotspot + _cursor.width * i - (3 + i)) = color;
                  *(hotspot - _cursor.width * i + (3 + i)) = color;
                  *(hotspot + _cursor.width * i + (3 + i)) = color;
                  *(hotspot - _cursor.width * (3 + i) - i) = color;
                  *(hotspot + _cursor.width * (3 + i) - i) = color;
                  *(hotspot - _cursor.width * (3 + i) + i) = color;
                  *(hotspot + _cursor.width * (3 + i) + i) = color;
            }
      } else {
            _cursor.width = 23;
            _cursor.height = 21;
            _cursor.hotspotX = 11;
            _cursor.hotspotY = 10;

            byte *hotspot = _grabbedCursor + _cursor.hotspotY * _cursor.width + _cursor.hotspotX;

            // Crosshair, slightly assymetric
            // TODO: Instead of setting this up via code, we should simply extend
            //       default_cursor_images to contain this shape.

            for (i = 0; i < 7; i++) {
                  *(hotspot - 5 - i) = color;
                  *(hotspot + 5 + i) = color;
            }

            for (i = 0; i < 8; i++) {
                  *(hotspot - _cursor.width * (3 + i)) = color;
                  *(hotspot + _cursor.width * (3 + i)) = color;
            }

            // Arrow heads, diagonal lines

            for (i = 1; i <= 3; i++) {
                  *(hotspot - _cursor.width * i - 5 - i) = color;
                  *(hotspot + _cursor.width * i - 5 - i) = color;
                  *(hotspot - _cursor.width * i + 5 + i) = color;
                  *(hotspot + _cursor.width * i + 5 + i) = color;
                  *(hotspot - _cursor.width * (i + 3) - i) = color;
                  *(hotspot - _cursor.width * (i + 3) + i) = color;
                  *(hotspot + _cursor.width * (i + 3) - i) = color;
                  *(hotspot + _cursor.width * (i + 3) + i) = color;
            }

            // Final touches

            *(hotspot - _cursor.width - 7) = color;
            *(hotspot - _cursor.width + 7) = color;
            *(hotspot + _cursor.width - 7) = color;
            *(hotspot + _cursor.width + 7) = color;
            *(hotspot - (_cursor.width * 5) - 1) = color;
            *(hotspot - (_cursor.width * 5) + 1) = color;
            *(hotspot + (_cursor.width * 5) - 1) = color;
            *(hotspot + (_cursor.width * 5) + 1) = color;
      }

      updateCursor();
}

void ScummEngine_v5::resetCursors() {
      static const uint16 default_cursor_images[4][16] = {
            /* cross-hair */
            { 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0000, 0x7e3f,
              0x0000, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0080, 0x0000 },
            /* hourglass */
            { 0x0000, 0x7ffe, 0x6006, 0x300c, 0x1818, 0x0c30, 0x0660, 0x03c0,
              0x0660, 0x0c30, 0x1998, 0x33cc, 0x67e6, 0x7ffe, 0x0000, 0x0000 },
            /* arrow */
            { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x7f00,
              0x7f80, 0x78c0, 0x7c00, 0x4600, 0x0600, 0x0300, 0x0300, 0x0180 },
            /* hand */
            { 0x1e00, 0x1200, 0x1200, 0x1200, 0x1200, 0x13ff, 0x1249, 0x1249,
              0xf249, 0x9001, 0x9001, 0x9001, 0x8001, 0x8001, 0x8001, 0xffff },
      };

      static const byte default_cursor_hotspots[10] = {
            8, 7,   8, 7,   1, 1,   5, 0,
            8, 7, //zak256
      };


      for (int i = 0; i < 4; i++) {
            memcpy(_cursorImages[i], default_cursor_images[i], 32);
      }
      memcpy(_cursorHotspots, default_cursor_hotspots, 8);

}

void ScummEngine_v5::setBuiltinCursor(int idx) {
      int i, j;
      byte color = default_cursor_colors[idx];
      const uint16 *src = _cursorImages[_currentCursor];

      memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor));

      _cursor.hotspotX = _cursorHotspots[2 * _currentCursor];
      _cursor.hotspotY = _cursorHotspots[2 * _currentCursor + 1];
      _cursor.width = 16;
      _cursor.height = 16;

      for (i = 0; i < 16; i++) {
            for (j = 0; j < 16; j++) {
                  if (src[i] & (1 << j))
                        _grabbedCursor[16 * i + 15 - j] = color;
            }
      }

      updateCursor();
}

} // End of namespace Scumm

Generated by  Doxygen 1.6.0   Back to index