diff options
author | Max Horn | 2004-01-04 14:35:46 +0000 |
---|---|---|
committer | Max Horn | 2004-01-04 14:35:46 +0000 |
commit | 2ce4d13c048ec5b2cec8fd78071a39d14888af63 (patch) | |
tree | 19abbbab592ecd583a5c9b6909e2ea3e9b608696 /scumm | |
parent | 694f02155cba6b7f8ed5cd5d4170eba935e9dcc6 (diff) | |
download | scummvm-rg350-2ce4d13c048ec5b2cec8fd78071a39d14888af63.tar.gz scummvm-rg350-2ce4d13c048ec5b2cec8fd78071a39d14888af63.tar.bz2 scummvm-rg350-2ce4d13c048ec5b2cec8fd78071a39d14888af63.zip |
split out cursor/palette code into separate source files
svn-id: r12138
Diffstat (limited to 'scumm')
-rw-r--r-- | scumm/bomp.h | 2 | ||||
-rw-r--r-- | scumm/cursor.cpp | 314 | ||||
-rw-r--r-- | scumm/gfx.cpp | 1259 | ||||
-rw-r--r-- | scumm/module.mk | 2 | ||||
-rw-r--r-- | scumm/palette.cpp | 921 |
5 files changed, 1277 insertions, 1221 deletions
diff --git a/scumm/bomp.h b/scumm/bomp.h index d2c224ea29..005fe83879 100644 --- a/scumm/bomp.h +++ b/scumm/bomp.h @@ -22,6 +22,8 @@ #ifndef BOMP_H #define BOMP_H +#include "common/scummsys.h" + namespace Scumm { int32 setupBompScale(byte *scaling, int32 size, byte scale); diff --git a/scumm/cursor.cpp b/scumm/cursor.cpp new file mode 100644 index 0000000000..9a8ae5a4fa --- /dev/null +++ b/scumm/cursor.cpp @@ -0,0 +1,314 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2003 The ScummVM project + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "stdafx.h" +#include "scumm/bomp.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 +}; + + + +static const uint16 default_cursor_images[5][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 }, + /* cross-hair zak256 - chrilith palmos */ +/* + { 0x0080, 0x0080, 0x02a0, 0x01c0, 0x0080, 0x1004, 0x0808, 0x7c1f, + 0x0808, 0x1004, 0x0080, 0x01c0, 0x02a0, 0x0080, 0x0080, 0x0000 }, +*/ + { 0x0080, 0x02a0, 0x01c0, 0x0080, 0x0000, 0x2002, 0x1004, 0x780f, + 0x1004, 0x2002, 0x0000, 0x0080, 0x01c0, 0x02a0, 0x0080, 0x0000 }, +}; + +static const byte default_cursor_hotspots[10] = { + 8, 7, 8, 7, 1, 1, 5, 0, + 8, 7, //zak256 +}; + + +void ScummEngine::setupCursor() { + _cursor.animate = 1; + if (_gameId == GID_TENTACLE && res.roomno[rtRoom][60]) { + // HACK: For DOTT we manually set the default cursor. See also bug #786994 + setCursorImg(697, 60, 1); + makeCursorColorTransparent(1); + } +} + +void ScummEngine::grabCursor(int x, int y, int w, int h) { + VirtScreen *vs = findVirtScreen(y); + + if (vs == NULL) { + warning("grabCursor: invalid Y %d", y); + return; + } + + grabCursor(vs->screenPtr + (y - vs->topline) * vs->width + x, w, h); + +} + +void ScummEngine::grabCursor(byte *ptr, int width, int height) { + 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 += _screenWidth; + } + + updateCursor(); +} + +void ScummEngine::useIm01Cursor(const byte *im, int w, int h) { + VirtScreen *vs = &virtscr[0]; + byte *buf, *dst; + const byte *src; + int i; + + w *= 8; + h *= 8; + + dst = buf = (byte *) malloc(w * h); + src = vs->screenPtr + vs->xstart; + + for (i = 0; i < h; i++) { + memcpy(dst, src, w); + dst += w; + src += vs->width; + } + + drawBox(0, 0, w - 1, h - 1, 0xFF); + + vs->alloctwobuffers = false; + gdi.disableZBuffer(); + gdi.drawBitmap(im, vs, _screenStartStrip, 0, w, h, 0, w / 8, 0); + vs->alloctwobuffers = true; + gdi.enableZBuffer(); + + grabCursor(vs->screenPtr + vs->xstart, w, h); + + src = buf; + dst = vs->screenPtr + vs->xstart; + + for (i = 0; i < h; i++) { + memcpy(dst, src, w); + dst += vs->width; + src += w; + } + + free(buf); +} + +void ScummEngine::setCursor(int cursor) { + if (cursor >= 0 && cursor <= 3) + _currentCursor = cursor; + else + warning("setCursor(%d)", cursor); +} + +void ScummEngine::setCursorHotspot(int x, int y) { + _cursor.hotspotX = x; + _cursor.hotspotY = y; + // FIXME this hacks around offset cursor in the humongous games + if (_features & GF_HUMONGOUS) { + _cursor.hotspotX += 15; + _cursor.hotspotY += 15; + } +} + +void ScummEngine::updateCursor() { + _system->set_mouse_cursor(_grabbedCursor, _cursor.width, _cursor.height, + _cursor.hotspotX, _cursor.hotspotY); +} + +void ScummEngine::animateCursor() { + if (_cursor.animate) { + if (!(_cursor.animateIndex & 0x1)) { + decompressDefaultCursor((_cursor.animateIndex >> 1) & 3); + } + _cursor.animateIndex++; + } +} + +void ScummEngine::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 (_version == 8) { + im += 16; + } else { + im += 18; + } + decompressBomp(_grabbedCursor, im, width, height); + + updateCursor(); +} + +void ScummEngine::decompressDefaultCursor(int idx) { + int i, j; + byte color; + + memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor)); + + if (_version == 1) + color = default_v1_cursor_colors[idx]; + else + color = default_cursor_colors[idx]; + + // FIXME: None of the stock cursors are right for Loom. Why is that? + + if (_gameId == GID_LOOM || _gameId == GID_LOOM256) { + int w = 0; + + _cursor.width = 8; + _cursor.height = 8; + _cursor.hotspotX = 0; + _cursor.hotspotY = 0; + + for (i = 0; i < 8; i++) { + w += (i >= 6) ? -2 : 1; + for (j = 0; j < w; j++) + _grabbedCursor[i * 8 + j] = color; + } + } else if (_version <= 2) { + _cursor.width = 23; + _cursor.height = 21; + _cursor.hotspotX = 11; + _cursor.hotspotY = 10; + + byte *hotspot = _grabbedCursor + _cursor.hotspotY * _cursor.width + _cursor.hotspotX; + + // Crosshair, slightly assymetric + + 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; + } else { + byte currentCursor = _currentCursor; + +#ifdef __PALM_OS__ + if (_gameId == GID_ZAK256 && currentCursor == 0) + currentCursor = 4; +#endif + + _cursor.width = 16; + _cursor.height = 16; + _cursor.hotspotX = default_cursor_hotspots[2 * currentCursor]; + _cursor.hotspotY = default_cursor_hotspots[2 * currentCursor + 1]; + + for (i = 0; i < 16; i++) { + for (j = 0; j < 16; j++) { + if (default_cursor_images[currentCursor][i] & (1 << j)) + _grabbedCursor[16 * i + 15 - j] = color; + } + } + } + + updateCursor(); +} + +void ScummEngine::makeCursorColorTransparent(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(); +} + +} // End of namespace Scumm diff --git a/scumm/gfx.cpp b/scumm/gfx.cpp index 2c02407ca9..d6e74f5074 100644 --- a/scumm/gfx.cpp +++ b/scumm/gfx.cpp @@ -20,12 +20,9 @@ */ #include "stdafx.h" -#include "common/util.h" #include "scumm/scumm.h" #include "scumm/actor.h" -#include "scumm/bomp.h" #include "scumm/charset.h" -#include "scumm/intern.h" #include "scumm/resource.h" #include "scumm/usage_bits.h" @@ -159,47 +156,6 @@ static const TransitionEffect transitionEffects[5] = { }; #endif -/* - * 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 -}; - - - -static const uint16 default_cursor_images[5][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 }, - /* cross-hair zak256 - chrilith palmos */ -/* - { 0x0080, 0x0080, 0x02a0, 0x01c0, 0x0080, 0x1004, 0x0808, 0x7c1f, - 0x0808, 0x1004, 0x0080, 0x01c0, 0x02a0, 0x0080, 0x0080, 0x0000 }, -*/ - { 0x0080, 0x02a0, 0x01c0, 0x0080, 0x0000, 0x2002, 0x1004, 0x780f, - 0x1004, 0x2002, 0x0000, 0x0080, 0x01c0, 0x02a0, 0x0080, 0x0000 }, -}; - -static const byte default_cursor_hotspots[10] = { - 8, 7, 8, 7, 1, 1, 5, 0, - 8, 7, //zak256 -}; - - #pragma mark - #pragma mark --- Virtual Screens --- #pragma mark - @@ -464,41 +420,10 @@ void Gdi::drawStripToScreen(VirtScreen *vs, int x, int w, int t, int b) { _vm->_system->copy_rect(ptr, vs->width, x, vs->topline + t, w, height); } -/** - * Reset the background behind an actor or blast object. - */ -void Gdi::resetBackground(int top, int bottom, int strip) { - VirtScreen *vs = &_vm->virtscr[0]; - byte *backbuff_ptr, *bgbak_ptr; - int offs, numLinesToProcess; - - assert(0 <= strip && strip < _numStrips); - - if (top < vs->tdirty[strip]) - vs->tdirty[strip] = top; - - if (bottom > vs->bdirty[strip]) - vs->bdirty[strip] = bottom; - - offs = (top * _numStrips + _vm->_screenStartStrip + strip) * 8; - byte *mask_ptr = _vm->getMaskBuffer(strip * 8, top, 0); - bgbak_ptr = vs->backBuf + offs; - backbuff_ptr = vs->screenPtr + offs; - - numLinesToProcess = bottom - top; - if (numLinesToProcess) { - if ((_vm->_features & GF_NEW_OPCODES) || (_vm->VAR(_vm->VAR_CURRENT_LIGHTS) & LIGHTMODE_screen)) { - if (_vm->hasCharsetMask(strip * 8, top, (strip + 1) * 8, bottom)) - draw8ColWithMasking(backbuff_ptr, bgbak_ptr, numLinesToProcess, mask_ptr); - else - draw8Col(backbuff_ptr, bgbak_ptr, numLinesToProcess); - } else { - clear8Col(backbuff_ptr, numLinesToProcess); - } - } -} - #pragma mark - +#pragma mark --- Background buffers & charset mask --- +#pragma mark - + void ScummEngine::initBGBuffers(int height) { const byte *ptr; @@ -1275,6 +1200,40 @@ next_iter: } /** + * Reset the background behind an actor or blast object. + */ +void Gdi::resetBackground(int top, int bottom, int strip) { + VirtScreen *vs = &_vm->virtscr[0]; + byte *backbuff_ptr, *bgbak_ptr; + int offs, numLinesToProcess; + + assert(0 <= strip && strip < _numStrips); + + if (top < vs->tdirty[strip]) + vs->tdirty[strip] = top; + + if (bottom > vs->bdirty[strip]) + vs->bdirty[strip] = bottom; + + offs = (top * _numStrips + _vm->_screenStartStrip + strip) * 8; + byte *mask_ptr = _vm->getMaskBuffer(strip * 8, top, 0); + bgbak_ptr = vs->backBuf + offs; + backbuff_ptr = vs->screenPtr + offs; + + numLinesToProcess = bottom - top; + if (numLinesToProcess) { + if ((_vm->_features & GF_NEW_OPCODES) || (_vm->VAR(_vm->VAR_CURRENT_LIGHTS) & LIGHTMODE_screen)) { + if (_vm->hasCharsetMask(strip * 8, top, (strip + 1) * 8, bottom)) + draw8ColWithMasking(backbuff_ptr, bgbak_ptr, numLinesToProcess, mask_ptr); + else + draw8Col(backbuff_ptr, bgbak_ptr, numLinesToProcess); + } else { + clear8Col(backbuff_ptr, numLinesToProcess); + } + } +} + +/** * Create and fill a table with offsets to the graphic and mask strips in the * given V2 EGA bitmap. * @param src the V2 EGA bitmap @@ -2557,7 +2516,7 @@ void ScummEngine::unkScreenEffect5(int a) { // // I still don't know what unkScreenEffect5(1) is supposed to do. - /* XXX: not implemented */ + // FIXME: not implemented warning("stub unkScreenEffect(%d)", a); } @@ -2570,1148 +2529,6 @@ void ScummEngine::setShake(int mode) { _system->set_shake_pos(0); } -#pragma mark - -#pragma mark --- Palette --- -#pragma mark - - -void ScummEngine::setupAmigaPalette() { - setPalColor( 0, 0, 0, 0); - setPalColor( 1, 0, 0, 187); - setPalColor( 2, 0, 187, 0); - setPalColor( 3, 0, 187, 187); - setPalColor( 4, 187, 0, 0); - setPalColor( 5, 187, 0, 187); - setPalColor( 6, 187, 119, 0); - setPalColor( 7, 187, 187, 187); - setPalColor( 8, 119, 119, 119); - setPalColor( 9, 119, 119, 255); - setPalColor(10, 0, 255, 0); - setPalColor(11, 0, 255, 255); - setPalColor(12, 255, 136, 136); - setPalColor(13, 255, 0, 255); - setPalColor(14, 255, 255, 0); - setPalColor(15, 255, 255, 255); -} - -void ScummEngine::setupEGAPalette() { - setPalColor( 0, 0, 0, 0); - setPalColor( 1, 0, 0, 168); - setPalColor( 2, 0, 168, 0); - setPalColor( 3, 0, 168, 168); - setPalColor( 4, 168, 0, 0); - setPalColor( 5, 168, 0, 168); - setPalColor( 6, 168, 84, 0); - setPalColor( 7, 168, 168, 168); - setPalColor( 8, 84, 84, 84); - setPalColor( 9, 84, 84, 252); - setPalColor(10, 84, 252, 84); - setPalColor(11, 84, 252, 252); - setPalColor(12, 252, 84, 84); - setPalColor(13, 252, 84, 252); - setPalColor(14, 252, 252, 84); - setPalColor(15, 252, 252, 252); -} - -void ScummEngine::setupV1ManiacPalette() { - setPalColor( 0, 0, 0, 0); - setPalColor( 1, 252, 252, 252); - setPalColor( 2, 168, 0, 0); - setPalColor( 3, 0, 168, 168); - setPalColor( 4, 168, 0, 168); - setPalColor( 5, 0, 168, 0); - setPalColor( 6, 0, 0, 168); - setPalColor( 7, 252, 252, 84); - setPalColor( 8, 252, 84, 84); - setPalColor( 9, 168, 84, 0); - setPalColor(10, 252, 84, 84); - setPalColor(11, 84, 84, 84); - setPalColor(12, 168, 168, 168); - setPalColor(13, 84, 252, 84); - setPalColor(14, 84, 84, 252); - setPalColor(15, 84, 84, 84); - setPalColor(16, 252, 84, 252); -} - -void ScummEngine::setupV1ZakPalette() { - setPalColor( 0, 0, 0, 0); - setPalColor( 1, 252, 252, 252); - setPalColor( 2, 168, 0, 0); - setPalColor( 3, 0, 168, 168); - setPalColor( 4, 168, 0, 168); - setPalColor( 5, 0, 168, 0); - setPalColor( 6, 0, 0, 168); - setPalColor( 7, 252, 252, 84); - setPalColor( 8, 252, 84, 84); - setPalColor( 9, 168, 84, 0); - setPalColor(10, 252, 84, 84); - setPalColor(11, 84, 84, 84); - setPalColor(12, 168, 168, 168); - setPalColor(13, 84, 252, 84); - setPalColor(14, 84, 84, 252); - setPalColor(15, 168, 168, 168); - setPalColor(16, 252, 84, 252); -} - -void ScummEngine::setPaletteFromPtr(const byte *ptr) { - int i; - byte *dest, r, g, b; - int numcolor; - - if (_features & GF_SMALL_HEADER) { - if (_features & GF_OLD256) - numcolor = 256; - else - numcolor = READ_LE_UINT16(ptr + 6) / 3; - ptr += 8; - } else { - numcolor = getResourceDataSize(ptr) / 3; - } - - checkRange(256, 0, numcolor, "Too many colors (%d) in Palette"); - - dest = _currentPalette; - - for (i = 0; i < numcolor; i++) { - r = *ptr++; - g = *ptr++; - b = *ptr++; - - // This comparison might look weird, but it's what the disassembly (DOTT) says! - // FIXME: Fingolfin still thinks it looks weird: the value 252 = 4*63 clearly comes from - // the days 6/6/6 palettes were used, OK. But it breaks MonkeyVGA, so I had to add a - // check for that. And somebody before me added a check for V7 games, turning this - // off there, too... I wonder if it hurts other games, too? What exactly is broken - // if we remove this patch? - // Since it also causes problems in Zak256, I am turning it off for all V4 games and older. - if ((_version <= 4) || (_version >= 7) || (i <= 15 || r < 252 || g < 252 || b < 252)) { - *dest++ = r; - *dest++ = g; - *dest++ = b; - } else { - dest += 3; - } - } - setDirtyColors(0, numcolor - 1); -} - -void ScummEngine::setPaletteFromRes() { - byte *ptr; - ptr = getResourceAddress(rtRoom, _roomResource) + _CLUT_offs; - setPaletteFromPtr(ptr); -} - -void ScummEngine::setDirtyColors(int min, int max) { - if (_palDirtyMin > min) - _palDirtyMin = min; - if (_palDirtyMax < max) - _palDirtyMax = max; -} - -void ScummEngine::initCycl(const byte *ptr) { - int j; - ColorCycle *cycl; - - memset(_colorCycle, 0, sizeof(_colorCycle)); - - if (_features & GF_SMALL_HEADER) { - cycl = _colorCycle; - ptr += 6; - for (j = 0; j < 16; ++j, ++cycl) { - uint16 delay = READ_BE_UINT16(ptr); - ptr += 2; - byte start = *ptr++; - byte end = *ptr++; - - if (!delay || delay == 0x0aaa || start >= end) - continue; - - cycl->counter = 0; - cycl->delay = 16384 / delay; - cycl->flags = 2; - cycl->start = start; - cycl->end = end; - } - } else { - while ((j = *ptr++) != 0) { - if (j < 1 || j > 16) { - error("Invalid color cycle index %d", j); - } - cycl = &_colorCycle[j - 1]; - - ptr += 2; - cycl->counter = 0; - cycl->delay = 16384 / READ_BE_UINT16(ptr); - ptr += 2; - cycl->flags = READ_BE_UINT16(ptr); - ptr += 2; - cycl->start = *ptr++; - cycl->end = *ptr++; - } - } -} - -void ScummEngine::stopCycle(int i) { - ColorCycle *cycl; - - checkRange(16, 0, i, "Stop Cycle %d Out Of Range"); - if (i != 0) { - _colorCycle[i - 1].delay = 0; - return; - } - - for (i = 0, cycl = _colorCycle; i < 16; i++, cycl++) - cycl->delay = 0; -} - -/** - * Cycle the colors in the given palette in the intervael [cycleStart, cycleEnd] - * either one step forward or backward. - */ -static void doCyclePalette(byte *palette, int cycleStart, int cycleEnd, int size, bool forward) { - byte *start = palette + cycleStart * size; - byte *end = palette + cycleEnd * size; - int num = cycleEnd - cycleStart; - byte tmp[6]; - - assert(size <= 6); - - if (forward) { - memmove(tmp, end, size); - memmove(start + size, start, num * size); - memmove(start, tmp, size); - } else { - memmove(tmp, start, size); - memmove(start, start + size, num * size); - memmove(end, tmp, size); - } -} - -/** - * Adjust an 'indirect' color palette for the color cycling performed on its - * master palette. An indirect palette is a palette which contains indices - * pointing into another palette - it provides a level of indirection to map - * palette colors to other colors. Now when the target palette is cycled, the - * indirect palette suddenly point at the wrong color(s). This function takes - * care of adjusting an indirect palette by searching through it and replacing - * all indices that are in the cycle range by the new (cycled) index. - * - * Finally, the palette entries still have to be cycled normally. - */ -static void doCycleIndirectPalette(byte *palette, int cycleStart, int cycleEnd, bool forward) { - int num = cycleEnd - cycleStart + 1; - int i; - int offset = forward ? 1 : num - 1; - - for (i = 0; i < 256; i++) { - if (cycleStart <= palette[i] && palette[i] <= cycleEnd) { - palette[i] = (palette[i] - cycleStart + offset) % num + cycleStart; - } - } - - doCyclePalette(palette, cycleStart, cycleEnd, 1, forward); -} - - -void ScummEngine::cyclePalette() { - ColorCycle *cycl; - int valueToAdd; - int i, j; - - if (VAR_TIMER == 0xFF) { - // FIXME - no idea if this is right :-/ - // Needed for both V2 and V8 at this time - valueToAdd = VAR(VAR_TIMER_NEXT); - } else { - valueToAdd = VAR(VAR_TIMER); - if (valueToAdd < VAR(VAR_TIMER_NEXT)) - valueToAdd = VAR(VAR_TIMER_NEXT); - } - - if (!_colorCycle) // FIXME - return; - - for (i = 0, cycl = _colorCycle; i < 16; i++, cycl++) { - if (!cycl->delay || cycl->start > cycl->end) - continue; - cycl->counter += valueToAdd; - if (cycl->counter >= cycl->delay) { - cycl->counter %= cycl->delay; - - setDirtyColors(cycl->start, cycl->end); - moveMemInPalRes(cycl->start, cycl->end, cycl->flags & 2); - - if (!(_features & GF_SMALL_HEADER && _version > 2)) - doCyclePalette(_currentPalette, cycl->start, cycl->end, 3, !(cycl->flags & 2)); - - // Also cycle the other, indirect palettes - if (_proc_special_palette) { - doCycleIndirectPalette(_proc_special_palette, cycl->start, cycl->end, !(cycl->flags & 2)); - } - - if (_shadowPalette) { - if (_version >= 7) { - for (j = 0; j < NUM_SHADOW_PALETTE; j++) - doCycleIndirectPalette(_shadowPalette + j * 256, cycl->start, cycl->end, !(cycl->flags & 2)); - } else { - doCycleIndirectPalette(_shadowPalette, cycl->start, cycl->end, !(cycl->flags & 2)); - } - } - } - } -} - -/** - * Perform color cycling on the palManipulate data, too, otherwise - * color cycling will be disturbed by the palette fade. - */ -void ScummEngine::moveMemInPalRes(int start, int end, byte direction) { - if (!_palManipCounter) - return; - - doCyclePalette(_palManipPalette, start, end, 3, !direction); - doCyclePalette(_palManipIntermediatePal, start, end, 6, !direction); -} - -void ScummEngine::palManipulateInit(int resID, int start, int end, int time) { - byte *pal, *target, *between; - byte *string1, *string2, *string3; - int i; - - string1 = getStringAddress(resID); - string2 = getStringAddress(resID + 1); - string3 = getStringAddress(resID + 2); - if (!string1 || !string2 || !string3) { - warning("palManipulateInit(%d,%d,%d,%d): Cannot obtain string resources %d, %d and %d", - resID, start, end, time, resID, resID + 1, resID + 2); - return; - } - - string1 += start; - string2 += start; - string3 += start; - - _palManipStart = start; - _palManipEnd = end; - _palManipCounter = 0; - - if (!_palManipPalette) - _palManipPalette = (byte *)calloc(0x300, 1); - if (!_palManipIntermediatePal) - _palManipIntermediatePal = (byte *)calloc(0x600, 1); - - pal = _currentPalette + start * 3; - target = _palManipPalette + start * 3; - between = _palManipIntermediatePal + start * 6; - - for (i = start; i < end; ++i) { - *target++ = *string1++; - *target++ = *string2++; - *target++ = *string3++; - *(uint16 *)between = ((uint16) *pal++) << 8; - between += 2; - *(uint16 *)between = ((uint16) *pal++) << 8; - between += 2; - *(uint16 *)between = ((uint16) *pal++) << 8; - between += 2; - } - - _palManipCounter = time; -} - -void ScummEngine_v6::palManipulateInit(int resID, int start, int end, int time) { - byte *pal, *target, *between; - const byte *new_pal; - int i; - - new_pal = getPalettePtr(resID); - - new_pal += start*3; - - _palManipStart = start; - _palManipEnd = end; - _palManipCounter = 0; - - if (!_palManipPalette) - _palManipPalette = (byte *)calloc(0x300, 1); - if (!_palManipIntermediatePal) - _palManipIntermediatePal = (byte *)calloc(0x600, 1); - - pal = _currentPalette + start * 3; - target = _palManipPalette + start * 3; - between = _palManipIntermediatePal + start * 6; - - for (i = start; i < end; ++i) { - *target++ = *new_pal++; - *target++ = *new_pal++; - *target++ = *new_pal++; - *(uint16 *)between = ((uint16) *pal++) << 8; - between += 2; - *(uint16 *)between = ((uint16) *pal++) << 8; - between += 2; - *(uint16 *)between = ((uint16) *pal++) << 8; - between += 2; - } - - _palManipCounter = time; -} - - -void ScummEngine::palManipulate() { - byte *target, *pal, *between; - int i, j; - - if (!_palManipCounter || !_palManipPalette || !_palManipIntermediatePal) - return; - - target = _palManipPalette + _palManipStart * 3; - pal = _currentPalette + _palManipStart * 3; - between = _palManipIntermediatePal + _palManipStart * 6; - - for (i = _palManipStart; i < _palManipEnd; ++i) { - j = (*((uint16 *)between) += ((*target++ << 8) - *((uint16 *)between)) / _palManipCounter); - *pal++ = j >> 8; - between += 2; - j = (*((uint16 *)between) += ((*target++ << 8) - *((uint16 *)between)) / _palManipCounter); - *pal++ = j >> 8; - between += 2; - j = (*((uint16 *)between) += ((*target++ << 8) - *((uint16 *)between)) / _palManipCounter); - *pal++ = j >> 8; - between += 2; - } - setDirtyColors(_palManipStart, _palManipEnd); - _palManipCounter--; -} - -void ScummEngine::setupShadowPalette(int slot, int redScale, int greenScale, int blueScale, int startColor, int endColor) { - byte *table; - int i; - byte *curpal; - - if (slot < 0 || slot >= NUM_SHADOW_PALETTE) - error("setupShadowPalette: invalid slot %d", slot); - - if (startColor < 0 || startColor > 255 || endColor < 0 || startColor > 255 || endColor < startColor) - error("setupShadowPalette: invalid range from %d to %d", startColor, endColor); - - table = _shadowPalette + slot * 256; - for (i = 0; i < 256; i++) - table[i] = i; - - table += startColor; - curpal = _currentPalette + startColor * 3; - for (i = startColor; i <= endColor; i++) { - *table++ = remapPaletteColor((curpal[0] * redScale) >> 8, - (curpal[1] * greenScale) >> 8, - (curpal[2] * blueScale) >> 8, - (uint) - 1); - curpal += 3; - } -} - -static inline uint colorWeight(int red, int green, int blue) { - return 3 * red * red + 6 * green * green + 2 * blue * blue; -} - - -void ScummEngine::setupShadowPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor) { - const byte *basepal = getPalettePtr(_curPalIndex); - const byte *pal = basepal; - const byte *compareptr; - byte *table = _shadowPalette; - int i; - - // This is a correction of the patch supplied for BUG #588501. - // It has been tested in all four known rooms where unkRoomFunc3 is used: - // - // 1) FOA Room 53: subway departing Knossos for Atlantis. - // 2) FOA Room 48: subway crashing into the Atlantis entrance area - // 3) FOA Room 82: boat/sub shadows while diving near Thera - // 4) FOA Room 23: the big machine room inside Atlantis - // - // The implementation behaves well in all tests. - // Pixel comparisons show that the resulting palette entries being - // derived from the shadow palette generated here occassionally differ - // slightly from the ones derived in the LEC executable. - // Not sure yet why, but the differences are VERY minor. - // - // There seems to be no explanation for why this function is called - // from within Room 23 (the big machine), as it has no shadow effects - // and thus doesn't result in any visual differences. - - for (i = 0; i <= 255; i++) { - int r = (int) (*pal++ * redScale) >> 8; - int g = (int) (*pal++ * greenScale) >> 8; - int b = (int) (*pal++ * blueScale) >> 8; - - // The following functionality is similar to remapPaletteColor, except - // 1) we have to work off the original CLUT rather than the current palette, and - // 2) the target shadow palette entries must be bounded to the upper and lower - // bounds provided by the opcode. (This becomes significant in Room 48, but - // is not an issue in all other known case studies.) - int j; - int ar, ag, ab; - uint sum, bestsum, bestitem = 0; - - if (r > 255) - r = 255; - if (g > 255) - g = 255; - if (b > 255) - b = 255; - - bestsum = (uint)-1; - - r &= ~3; - g &= ~3; - b &= ~3; - - compareptr = basepal + startColor * 3; - for (j = startColor; j <= endColor; j++, compareptr += 3) { - ar = compareptr[0] & ~3; - ag = compareptr[1] & ~3; - ab = compareptr[2] & ~3; - if (ar == r && ag == g && ab == b) { - bestitem = j; - break; - } - - sum = colorWeight(ar - r, ag - g, ab - b); - - if (sum < bestsum) { - bestsum = sum; - bestitem = j; - } - } - *table++ = bestitem; - } -} - -/** This function create the specialPalette used for semi-transparency in SamnMax */ -void ScummEngine::createSpecialPalette(int16 from, int16 to, int16 redScale, int16 greenScale, int16 blueScale, - int16 startColor, int16 endColor) { - const byte *palPtr, *curPtr; - const byte *searchPtr; - - uint bestResult; - uint currentResult; - - byte currentIndex; - - int i, j; - - palPtr = getPalettePtr(_curPalIndex); - - for (i = 0; i < 256; i++) - _proc_special_palette[i] = i; - - curPtr = palPtr + startColor * 3; - - for (i = startColor; i < endColor; i++) { - int r = (int) (*curPtr++ * redScale) >> 8; - int g = (int) (*curPtr++ * greenScale) >> 8; - int b = (int) (*curPtr++ * blueScale) >> 8; - - if (r > 255) - r = 255; - if (g > 255) - g = 255; - if (b > 255) - b = 255; - - searchPtr = palPtr + from * 3; - bestResult = (uint)-1; - currentIndex = (byte) from; - - for (j = from; j <= to; j++) { - int ar = (*searchPtr++); - int ag = (*searchPtr++); - int ab = (*searchPtr++); - - currentResult = colorWeight(ar - r, ag - g, ab - b); - - if (currentResult < bestResult) { - _proc_special_palette[i] = currentIndex; - bestResult = currentResult; - } - currentIndex++; - } - } -} - -void ScummEngine::darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor) { - if (_roomResource == 0) // FIXME - HACK to get COMI demo working - return; - - if (startColor <= endColor) { - const byte *cptr; - byte *cur; - int j; - int color; - - cptr = getPalettePtr(_curPalIndex) + startColor * 3; - cur = _currentPalette + startColor * 3; - - for (j = startColor; j <= endColor; j++) { - // FIXME: Hack to fix Amiga palette adjustments - if ((_features & GF_AMIGA && _version == 5) && (j >= 16 && j < 81)) { - cptr += 3; - cur += 3; - continue; - } - - color = *cptr++; - color = color * redScale / 0xFF; - if (color > 255) - color = 255; - *cur++ = color; - - color = *cptr++; - color = color * greenScale / 0xFF; - if (color > 255) - color = 255; - *cur++ = color; - - color = *cptr++; - color = color * blueScale / 0xFF; - if (color > 255) - color = 255; - *cur++ = color; - } - setDirtyColors(startColor, endColor); - } -} - -static int HSL2RGBHelper(int n1, int n2, int hue) { - if (hue > 360) - hue = hue - 360; - else if (hue < 0) - hue = hue + 360; - - if (hue < 60) - return n1 + (n2 - n1) * hue / 60; - if (hue < 180) - return n2; - if (hue < 240) - return n1 + (n2 - n1) * (240 - hue) / 60; - return n1; -} - -/** - * This function scales the HSL (Hue, Saturation and Lightness) - * components of the palette colors. It's used in CMI when Guybrush - * walks from the beach towards the swamp. - */ -void ScummEngine::desaturatePalette(int hueScale, int satScale, int lightScale, int startColor, int endColor) { - - if (startColor <= endColor) { - const byte *cptr; - byte *cur; - int j; - - cptr = getPalettePtr(_curPalIndex) + startColor * 3; - cur = _currentPalette + startColor * 3; - - for (j = startColor; j <= endColor; j++) { - int R = *cptr++; - int G = *cptr++; - int B = *cptr++; - - // RGB to HLS (Foley and VanDam) - - const int min = MIN(R, MIN(G, B)); - const int max = MAX(R, MAX(G, B)); - const int diff = (max - min); - const int sum = (max + min); - - if (diff != 0) { - int H, S, L; - - if (sum <= 255) - S = 255 * diff / sum; - else - S = 255 * diff / (255 * 2 - sum); - - if (R == max) - H = 60 * (G - B) / diff; - else if (G == max) - H = 120 + 60 * (B - R) / diff; - else - H = 240 + 60 * (R - G) / diff; - - if (H < 0) - H = H + 360; - - // Scale the result - - H = (H * hueScale) / 255; - S = (S * satScale) / 255; - L = (sum * lightScale) / 255; - - // HLS to RGB (Foley and VanDam) - - int m1, m2; - if (L <= 255) - m2 = L * (255 + S) / (255 * 2); - else - m2 = L * (255 - S) / (255 * 2) + S; - - m1 = L - m2; - - R = HSL2RGBHelper(m1, m2, H + 120); - G = HSL2RGBHelper(m1, m2, H); - B = HSL2RGBHelper(m1, m2, H - 120); - } else { - // Maximal color = minimal color -> R=G=B -> it's a grayscale. - R = G = B = (R * lightScale) / 255; - } - - *cur++ = R; - *cur++ = G; - *cur++ = B; - } - - setDirtyColors(startColor, endColor); - } -} - - -int ScummEngine::remapPaletteColor(int r, int g, int b, uint threshold) { - int i; - int ar, ag, ab; - uint sum, bestsum, bestitem = 0; - byte *pal = _currentPalette; - - if (r > 255) - r = 255; - if (g > 255) - g = 255; - if (b > 255) - b = 255; - - bestsum = (uint) - 1; - - r &= ~3; - g &= ~3; - b &= ~3; - - for (i = 0; i < 256; i++, pal += 3) { - ar = pal[0] & ~3; - ag = pal[1] & ~3; - ab = pal[2] & ~3; - if (ar == r && ag == g && ab == b) - return i; - - sum = colorWeight(ar - r, ag - g, ab - b); - - if (sum < bestsum) { - bestsum = sum; - bestitem = i; - } - } - - if (threshold != (uint) - 1 && bestsum > colorWeight(threshold, threshold, threshold)) { - // Best match exceeded threshold. Try to find an unused palette entry and - // use it for our purpose. - pal = _currentPalette + (256 - 2) * 3; - for (i = 254; i > 48; i--, pal -= 3) { - if (pal[0] >= 252 && pal[1] >= 252 && pal[2] >= 252) { - setPalColor(i, r, g, b); - return i; - } - } - } - - return bestitem; -} - -void ScummEngine::swapPalColors(int a, int b) { - byte *ap, *bp; - byte t; - - if ((uint) a >= 256 || (uint) b >= 256) - error("swapPalColors: invalid values, %d, %d", a, b); - - ap = &_currentPalette[a * 3]; - bp = &_currentPalette[b * 3]; - - t = ap[0]; - ap[0] = bp[0]; - bp[0] = t; - t = ap[1]; - ap[1] = bp[1]; - bp[1] = t; - t = ap[2]; - ap[2] = bp[2]; - bp[2] = t; - - setDirtyColors(a, a); - setDirtyColors(b, b); -} - -void ScummEngine::copyPalColor(int dst, int src) { - byte *dp, *sp; - - if ((uint) dst >= 256 || (uint) src >= 256) - error("copyPalColor: invalid values, %d, %d", dst, src); - - dp = &_currentPalette[dst * 3]; - sp = &_currentPalette[src * 3]; - - dp[0] = sp[0]; - dp[1] = sp[1]; - dp[2] = sp[2]; - - setDirtyColors(dst, dst); -} - -void ScummEngine::setPalColor(int idx, int r, int g, int b) { - _currentPalette[idx * 3 + 0] = r; - _currentPalette[idx * 3 + 1] = g; - _currentPalette[idx * 3 + 2] = b; - setDirtyColors(idx, idx); -} - -void ScummEngine::setPalette(int palindex) { - const byte *pals; - - _curPalIndex = palindex; - pals = getPalettePtr(_curPalIndex); - setPaletteFromPtr(pals); -} - -const byte *ScummEngine::findPalInPals(const byte *pal, int idx) { - const byte *offs; - uint32 size; - - pal = findResource(MKID('WRAP'), pal); - if (pal == NULL) - return NULL; - - offs = findResourceData(MKID('OFFS'), pal); - if (offs == NULL) - return NULL; - - size = getResourceDataSize(offs) / 4; - - if ((uint32)idx >= (uint32)size) - return NULL; - - return offs + READ_LE_UINT32(offs + idx * sizeof(uint32)); -} - -const byte *ScummEngine::getPalettePtr(int palindex) { - const byte *cptr; - - cptr = getResourceAddress(rtRoom, _roomResource); - assert(cptr); - if (_CLUT_offs) { - cptr += _CLUT_offs; - } else { - cptr = findPalInPals(cptr + _PALS_offs, palindex); - } - assert(cptr); - return cptr; -} - -void ScummEngine::updatePalette() { - if (_palDirtyMax == -1) - return; - - bool noir_mode = (_gameId == GID_SAMNMAX && readVar(0x8000)); - int first = _palDirtyMin; - int num = _palDirtyMax - first + 1; - int i; - - byte palette_colors[1024]; - byte *p = palette_colors; - - for (i = _palDirtyMin; i <= _palDirtyMax; i++) { - byte *data; - - if (_features & GF_SMALL_HEADER && _version > 2) - data = _currentPalette + _shadowPalette[i] * 3; - else - data = _currentPalette + i * 3; - - // Sam & Max film noir mode. Convert the colours to grayscale - // before uploading them to the backend. - - if (noir_mode) { - int r, g, b; - byte brightness; - - r = data[0]; - g = data[1]; - b = data[2]; - - brightness = (byte)((0.299 * r + 0.587 * g + 0.114 * b) + 0.5); - - *p++ = brightness; - *p++ = brightness; - *p++ = brightness; - *p++ = 0; - } else { - *p++ = data[0]; - *p++ = data[1]; - *p++ = data[2]; - *p++ = 0; - } - } - - _system->set_palette(palette_colors, first, num); - - _palDirtyMax = -1; - _palDirtyMin = 256; -} - -#pragma mark - -#pragma mark --- Cursor --- -#pragma mark - - -void ScummEngine::setupCursor() { - _cursor.animate = 1; - if (_gameId == GID_TENTACLE && res.roomno[rtRoom][60]) { - // HACK: For DOTT we manually set the default cursor. See also bug #786994 - setCursorImg(697, 60, 1); - makeCursorColorTransparent(1); - } -} - -void ScummEngine::grabCursor(int x, int y, int w, int h) { - VirtScreen *vs = findVirtScreen(y); - - if (vs == NULL) { - warning("grabCursor: invalid Y %d", y); - return; - } - - grabCursor(vs->screenPtr + (y - vs->topline) * vs->width + x, w, h); - -} - -void ScummEngine::grabCursor(byte *ptr, int width, int height) { - 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 += _screenWidth; - } - - updateCursor(); -} - -void ScummEngine::useIm01Cursor(const byte *im, int w, int h) { - VirtScreen *vs = &virtscr[0]; - byte *buf, *dst; - const byte *src; - int i; - - w *= 8; - h *= 8; - - dst = buf = (byte *) malloc(w * h); - src = vs->screenPtr + vs->xstart; - - for (i = 0; i < h; i++) { - memcpy(dst, src, w); - dst += w; - src += vs->width; - } - - drawBox(0, 0, w - 1, h - 1, 0xFF); - - vs->alloctwobuffers = false; - gdi.disableZBuffer(); - gdi.drawBitmap(im, vs, _screenStartStrip, 0, w, h, 0, w / 8, 0); - vs->alloctwobuffers = true; - gdi.enableZBuffer(); - - grabCursor(vs->screenPtr + vs->xstart, w, h); - - src = buf; - dst = vs->screenPtr + vs->xstart; - - for (i = 0; i < h; i++) { - memcpy(dst, src, w); - dst += vs->width; - src += w; - } - - free(buf); -} - -void ScummEngine::setCursor(int cursor) { - if (cursor >= 0 && cursor <= 3) - _currentCursor = cursor; - else - warning("setCursor(%d)", cursor); -} - -void ScummEngine::setCursorHotspot(int x, int y) { - _cursor.hotspotX = x; - _cursor.hotspotY = y; - // FIXME this hacks around offset cursor in the humongous games - if (_features & GF_HUMONGOUS) { - _cursor.hotspotX += 15; - _cursor.hotspotY += 15; - } -} - -void ScummEngine::updateCursor() { - _system->set_mouse_cursor(_grabbedCursor, _cursor.width, _cursor.height, - _cursor.hotspotX, _cursor.hotspotY); -} - -void ScummEngine::animateCursor() { - if (_cursor.animate) { - if (!(_cursor.animateIndex & 0x1)) { - decompressDefaultCursor((_cursor.animateIndex >> 1) & 3); - } - _cursor.animateIndex++; - } -} - -void ScummEngine::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 (_version == 8) { - im += 16; - } else { - im += 18; - } - decompressBomp(_grabbedCursor, im, width, height); - - updateCursor(); -} - -void ScummEngine::decompressDefaultCursor(int idx) { - int i, j; - byte color; - - memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor)); - - if (_version == 1) - color = default_v1_cursor_colors[idx]; - else - color = default_cursor_colors[idx]; - - // FIXME: None of the stock cursors are right for Loom. Why is that? - - if (_gameId == GID_LOOM || _gameId == GID_LOOM256) { - int w = 0; - - _cursor.width = 8; - _cursor.height = 8; - _cursor.hotspotX = 0; - _cursor.hotspotY = 0; - - for (i = 0; i < 8; i++) { - w += (i >= 6) ? -2 : 1; - for (j = 0; j < w; j++) - _grabbedCursor[i * 8 + j] = color; - } - } else if (_version <= 2) { - _cursor.width = 23; - _cursor.height = 21; - _cursor.hotspotX = 11; - _cursor.hotspotY = 10; - - byte *hotspot = _grabbedCursor + _cursor.hotspotY * _cursor.width + _cursor.hotspotX; - - // Crosshair, slightly assymetric - - 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; - } else { - byte currentCursor = _currentCursor; - -#ifdef __PALM_OS__ - if (_gameId == GID_ZAK256 && currentCursor == 0) - currentCursor = 4; -#endif - - _cursor.width = 16; - _cursor.height = 16; - _cursor.hotspotX = default_cursor_hotspots[2 * currentCursor]; - _cursor.hotspotY = default_cursor_hotspots[2 * currentCursor + 1]; - - for (i = 0; i < 16; i++) { - for (j = 0; j < 16; j++) { - if (default_cursor_images[currentCursor][i] & (1 << j)) - _grabbedCursor[16 * i + 15 - j] = color; - } - } - } - - updateCursor(); -} - -void ScummEngine::makeCursorColorTransparent(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(); -} - } // End of namespace Scumm #ifdef __PALM_OS__ diff --git a/scumm/module.mk b/scumm/module.mk index 013f613d2a..547cb120c5 100644 --- a/scumm/module.mk +++ b/scumm/module.mk @@ -10,6 +10,7 @@ MODULE_OBJS := \ scumm/camera.o \ scumm/charset.o \ scumm/costume.o \ + scumm/cursor.o \ scumm/debugger.o \ scumm/dialogs.o \ scumm/gfx.o \ @@ -22,6 +23,7 @@ MODULE_OBJS := \ scumm/midiparser_eup.o \ scumm/nut_renderer.o \ scumm/object.o \ + scumm/palette.o \ scumm/player_mod.o \ scumm/player_v1.o \ scumm/player_v2.o \ diff --git a/scumm/palette.cpp b/scumm/palette.cpp new file mode 100644 index 0000000000..101f372a3e --- /dev/null +++ b/scumm/palette.cpp @@ -0,0 +1,921 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2003 The ScummVM project + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + * + */ + +#include "stdafx.h" +#include "common/util.h" +#include "scumm/scumm.h" +#include "scumm/intern.h" +#include "scumm/resource.h" + + +namespace Scumm { + +void ScummEngine::setupAmigaPalette() { + setPalColor( 0, 0, 0, 0); + setPalColor( 1, 0, 0, 187); + setPalColor( 2, 0, 187, 0); + setPalColor( 3, 0, 187, 187); + setPalColor( 4, 187, 0, 0); + setPalColor( 5, 187, 0, 187); + setPalColor( 6, 187, 119, 0); + setPalColor( 7, 187, 187, 187); + setPalColor( 8, 119, 119, 119); + setPalColor( 9, 119, 119, 255); + setPalColor(10, 0, 255, 0); + setPalColor(11, 0, 255, 255); + setPalColor(12, 255, 136, 136); + setPalColor(13, 255, 0, 255); + setPalColor(14, 255, 255, 0); + setPalColor(15, 255, 255, 255); +} + +void ScummEngine::setupEGAPalette() { + setPalColor( 0, 0, 0, 0); + setPalColor( 1, 0, 0, 168); + setPalColor( 2, 0, 168, 0); + setPalColor( 3, 0, 168, 168); + setPalColor( 4, 168, 0, 0); + setPalColor( 5, 168, 0, 168); + setPalColor( 6, 168, 84, 0); + setPalColor( 7, 168, 168, 168); + setPalColor( 8, 84, 84, 84); + setPalColor( 9, 84, 84, 252); + setPalColor(10, 84, 252, 84); + setPalColor(11, 84, 252, 252); + setPalColor(12, 252, 84, 84); + setPalColor(13, 252, 84, 252); + setPalColor(14, 252, 252, 84); + setPalColor(15, 252, 252, 252); +} + +void ScummEngine::setupV1ManiacPalette() { + setPalColor( 0, 0, 0, 0); + setPalColor( 1, 252, 252, 252); + setPalColor( 2, 168, 0, 0); + setPalColor( 3, 0, 168, 168); + setPalColor( 4, 168, 0, 168); + setPalColor( 5, 0, 168, 0); + setPalColor( 6, 0, 0, 168); + setPalColor( 7, 252, 252, 84); + setPalColor( 8, 252, 84, 84); + setPalColor( 9, 168, 84, 0); + setPalColor(10, 252, 84, 84); + setPalColor(11, 84, 84, 84); + setPalColor(12, 168, 168, 168); + setPalColor(13, 84, 252, 84); + setPalColor(14, 84, 84, 252); + setPalColor(15, 84, 84, 84); + setPalColor(16, 252, 84, 252); +} + +void ScummEngine::setupV1ZakPalette() { + setPalColor( 0, 0, 0, 0); + setPalColor( 1, 252, 252, 252); + setPalColor( 2, 168, 0, 0); + setPalColor( 3, 0, 168, 168); + setPalColor( 4, 168, 0, 168); + setPalColor( 5, 0, 168, 0); + setPalColor( 6, 0, 0, 168); + setPalColor( 7, 252, 252, 84); + setPalColor( 8, 252, 84, 84); + setPalColor( 9, 168, 84, 0); + setPalColor(10, 252, 84, 84); + setPalColor(11, 84, 84, 84); + setPalColor(12, 168, 168, 168); + setPalColor(13, 84, 252, 84); + setPalColor(14, 84, 84, 252); + setPalColor(15, 168, 168, 168); + setPalColor(16, 252, 84, 252); +} + +void ScummEngine::setPaletteFromPtr(const byte *ptr) { + int i; + byte *dest, r, g, b; + int numcolor; + + if (_features & GF_SMALL_HEADER) { + if (_features & GF_OLD256) + numcolor = 256; + else + numcolor = READ_LE_UINT16(ptr + 6) / 3; + ptr += 8; + } else { + numcolor = getResourceDataSize(ptr) / 3; + } + + checkRange(256, 0, numcolor, "Too many colors (%d) in Palette"); + + dest = _currentPalette; + + for (i = 0; i < numcolor; i++) { + r = *ptr++; + g = *ptr++; + b = *ptr++; + + // This comparison might look weird, but it's what the disassembly (DOTT) says! + // FIXME: Fingolfin still thinks it looks weird: the value 252 = 4*63 clearly comes from + // the days 6/6/6 palettes were used, OK. But it breaks MonkeyVGA, so I had to add a + // check for that. And somebody before me added a check for V7 games, turning this + // off there, too... I wonder if it hurts other games, too? What exactly is broken + // if we remove this patch? + // Since it also causes problems in Zak256, I am turning it off for all V4 games and older. + if ((_version <= 4) || (_version >= 7) || (i <= 15 || r < 252 || g < 252 || b < 252)) { + *dest++ = r; + *dest++ = g; + *dest++ = b; + } else { + dest += 3; + } + } + setDirtyColors(0, numcolor - 1); +} + +void ScummEngine::setPaletteFromRes() { + byte *ptr; + ptr = getResourceAddress(rtRoom, _roomResource) + _CLUT_offs; + setPaletteFromPtr(ptr); +} + +void ScummEngine::setDirtyColors(int min, int max) { + if (_palDirtyMin > min) + _palDirtyMin = min; + if (_palDirtyMax < max) + _palDirtyMax = max; +} + +void ScummEngine::initCycl(const byte *ptr) { + int j; + ColorCycle *cycl; + + memset(_colorCycle, 0, sizeof(_colorCycle)); + + if (_features & GF_SMALL_HEADER) { + cycl = _colorCycle; + ptr += 6; + for (j = 0; j < 16; ++j, ++cycl) { + uint16 delay = READ_BE_UINT16(ptr); + ptr += 2; + byte start = *ptr++; + byte end = *ptr++; + + if (!delay || delay == 0x0aaa || start >= end) + continue; + + cycl->counter = 0; + cycl->delay = 16384 / delay; + cycl->flags = 2; + cycl->start = start; + cycl->end = end; + } + } else { + while ((j = *ptr++) != 0) { + if (j < 1 || j > 16) { + error("Invalid color cycle index %d", j); + } + cycl = &_colorCycle[j - 1]; + + ptr += 2; + cycl->counter = 0; + cycl->delay = 16384 / READ_BE_UINT16(ptr); + ptr += 2; + cycl->flags = READ_BE_UINT16(ptr); + ptr += 2; + cycl->start = *ptr++; + cycl->end = *ptr++; + } + } +} + +void ScummEngine::stopCycle(int i) { + ColorCycle *cycl; + + checkRange(16, 0, i, "Stop Cycle %d Out Of Range"); + if (i != 0) { + _colorCycle[i - 1].delay = 0; + return; + } + + for (i = 0, cycl = _colorCycle; i < 16; i++, cycl++) + cycl->delay = 0; +} + +/** + * Cycle the colors in the given palette in the intervael [cycleStart, cycleEnd] + * either one step forward or backward. + */ +static void doCyclePalette(byte *palette, int cycleStart, int cycleEnd, int size, bool forward) { + byte *start = palette + cycleStart * size; + byte *end = palette + cycleEnd * size; + int num = cycleEnd - cycleStart; + byte tmp[6]; + + assert(size <= 6); + + if (forward) { + memmove(tmp, end, size); + memmove(start + size, start, num * size); + memmove(start, tmp, size); + } else { + memmove(tmp, start, size); + memmove(start, start + size, num * size); + memmove(end, tmp, size); + } +} + +/** + * Adjust an 'indirect' color palette for the color cycling performed on its + * master palette. An indirect palette is a palette which contains indices + * pointing into another palette - it provides a level of indirection to map + * palette colors to other colors. Now when the target palette is cycled, the + * indirect palette suddenly point at the wrong color(s). This function takes + * care of adjusting an indirect palette by searching through it and replacing + * all indices that are in the cycle range by the new (cycled) index. + * + * Finally, the palette entries still have to be cycled normally. + */ +static void doCycleIndirectPalette(byte *palette, int cycleStart, int cycleEnd, bool forward) { + int num = cycleEnd - cycleStart + 1; + int i; + int offset = forward ? 1 : num - 1; + + for (i = 0; i < 256; i++) { + if (cycleStart <= palette[i] && palette[i] <= cycleEnd) { + palette[i] = (palette[i] - cycleStart + offset) % num + cycleStart; + } + } + + doCyclePalette(palette, cycleStart, cycleEnd, 1, forward); +} + + +void ScummEngine::cyclePalette() { + ColorCycle *cycl; + int valueToAdd; + int i, j; + + if (VAR_TIMER == 0xFF) { + // FIXME - no idea if this is right :-/ + // Needed for both V2 and V8 at this time + valueToAdd = VAR(VAR_TIMER_NEXT); + } else { + valueToAdd = VAR(VAR_TIMER); + if (valueToAdd < VAR(VAR_TIMER_NEXT)) + valueToAdd = VAR(VAR_TIMER_NEXT); + } + + if (!_colorCycle) // FIXME + return; + + for (i = 0, cycl = _colorCycle; i < 16; i++, cycl++) { + if (!cycl->delay || cycl->start > cycl->end) + continue; + cycl->counter += valueToAdd; + if (cycl->counter >= cycl->delay) { + cycl->counter %= cycl->delay; + + setDirtyColors(cycl->start, cycl->end); + moveMemInPalRes(cycl->start, cycl->end, cycl->flags & 2); + + if (!(_features & GF_SMALL_HEADER && _version > 2)) + doCyclePalette(_currentPalette, cycl->start, cycl->end, 3, !(cycl->flags & 2)); + + // Also cycle the other, indirect palettes + if (_proc_special_palette) { + doCycleIndirectPalette(_proc_special_palette, cycl->start, cycl->end, !(cycl->flags & 2)); + } + + if (_shadowPalette) { + if (_version >= 7) { + for (j = 0; j < NUM_SHADOW_PALETTE; j++) + doCycleIndirectPalette(_shadowPalette + j * 256, cycl->start, cycl->end, !(cycl->flags & 2)); + } else { + doCycleIndirectPalette(_shadowPalette, cycl->start, cycl->end, !(cycl->flags & 2)); + } + } + } + } +} + +/** + * Perform color cycling on the palManipulate data, too, otherwise + * color cycling will be disturbed by the palette fade. + */ +void ScummEngine::moveMemInPalRes(int start, int end, byte direction) { + if (!_palManipCounter) + return; + + doCyclePalette(_palManipPalette, start, end, 3, !direction); + doCyclePalette(_palManipIntermediatePal, start, end, 6, !direction); +} + +void ScummEngine::palManipulateInit(int resID, int start, int end, int time) { + byte *pal, *target, *between; + byte *string1, *string2, *string3; + int i; + + string1 = getStringAddress(resID); + string2 = getStringAddress(resID + 1); + string3 = getStringAddress(resID + 2); + if (!string1 || !string2 || !string3) { + warning("palManipulateInit(%d,%d,%d,%d): Cannot obtain string resources %d, %d and %d", + resID, start, end, time, resID, resID + 1, resID + 2); + return; + } + + string1 += start; + string2 += start; + string3 += start; + + _palManipStart = start; + _palManipEnd = end; + _palManipCounter = 0; + + if (!_palManipPalette) + _palManipPalette = (byte *)calloc(0x300, 1); + if (!_palManipIntermediatePal) + _palManipIntermediatePal = (byte *)calloc(0x600, 1); + + pal = _currentPalette + start * 3; + target = _palManipPalette + start * 3; + between = _palManipIntermediatePal + start * 6; + + for (i = start; i < end; ++i) { + *target++ = *string1++; + *target++ = *string2++; + *target++ = *string3++; + *(uint16 *)between = ((uint16) *pal++) << 8; + between += 2; + *(uint16 *)between = ((uint16) *pal++) << 8; + between += 2; + *(uint16 *)between = ((uint16) *pal++) << 8; + between += 2; + } + + _palManipCounter = time; +} + +void ScummEngine_v6::palManipulateInit(int resID, int start, int end, int time) { + byte *pal, *target, *between; + const byte *new_pal; + int i; + + new_pal = getPalettePtr(resID); + + new_pal += start*3; + + _palManipStart = start; + _palManipEnd = end; + _palManipCounter = 0; + + if (!_palManipPalette) + _palManipPalette = (byte *)calloc(0x300, 1); + if (!_palManipIntermediatePal) + _palManipIntermediatePal = (byte *)calloc(0x600, 1); + + pal = _currentPalette + start * 3; + target = _palManipPalette + start * 3; + between = _palManipIntermediatePal + start * 6; + + for (i = start; i < end; ++i) { + *target++ = *new_pal++; + *target++ = *new_pal++; + *target++ = *new_pal++; + *(uint16 *)between = ((uint16) *pal++) << 8; + between += 2; + *(uint16 *)between = ((uint16) *pal++) << 8; + between += 2; + *(uint16 *)between = ((uint16) *pal++) << 8; + between += 2; + } + + _palManipCounter = time; +} + + +void ScummEngine::palManipulate() { + byte *target, *pal, *between; + int i, j; + + if (!_palManipCounter || !_palManipPalette || !_palManipIntermediatePal) + return; + + target = _palManipPalette + _palManipStart * 3; + pal = _currentPalette + _palManipStart * 3; + between = _palManipIntermediatePal + _palManipStart * 6; + + for (i = _palManipStart; i < _palManipEnd; ++i) { + j = (*((uint16 *)between) += ((*target++ << 8) - *((uint16 *)between)) / _palManipCounter); + *pal++ = j >> 8; + between += 2; + j = (*((uint16 *)between) += ((*target++ << 8) - *((uint16 *)between)) / _palManipCounter); + *pal++ = j >> 8; + between += 2; + j = (*((uint16 *)between) += ((*target++ << 8) - *((uint16 *)between)) / _palManipCounter); + *pal++ = j >> 8; + between += 2; + } + setDirtyColors(_palManipStart, _palManipEnd); + _palManipCounter--; +} + +void ScummEngine::setupShadowPalette(int slot, int redScale, int greenScale, int blueScale, int startColor, int endColor) { + byte *table; + int i; + byte *curpal; + + if (slot < 0 || slot >= NUM_SHADOW_PALETTE) + error("setupShadowPalette: invalid slot %d", slot); + + if (startColor < 0 || startColor > 255 || endColor < 0 || startColor > 255 || endColor < startColor) + error("setupShadowPalette: invalid range from %d to %d", startColor, endColor); + + table = _shadowPalette + slot * 256; + for (i = 0; i < 256; i++) + table[i] = i; + + table += startColor; + curpal = _currentPalette + startColor * 3; + for (i = startColor; i <= endColor; i++) { + *table++ = remapPaletteColor((curpal[0] * redScale) >> 8, + (curpal[1] * greenScale) >> 8, + (curpal[2] * blueScale) >> 8, + (uint) - 1); + curpal += 3; + } +} + +static inline uint colorWeight(int red, int green, int blue) { + return 3 * red * red + 6 * green * green + 2 * blue * blue; +} + + +void ScummEngine::setupShadowPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor) { + const byte *basepal = getPalettePtr(_curPalIndex); + const byte *pal = basepal; + const byte *compareptr; + byte *table = _shadowPalette; + int i; + + // This is a correction of the patch supplied for BUG #588501. + // It has been tested in all four known rooms where unkRoomFunc3 is used: + // + // 1) FOA Room 53: subway departing Knossos for Atlantis. + // 2) FOA Room 48: subway crashing into the Atlantis entrance area + // 3) FOA Room 82: boat/sub shadows while diving near Thera + // 4) FOA Room 23: the big machine room inside Atlantis + // + // The implementation behaves well in all tests. + // Pixel comparisons show that the resulting palette entries being + // derived from the shadow palette generated here occassionally differ + // slightly from the ones derived in the LEC executable. + // Not sure yet why, but the differences are VERY minor. + // + // There seems to be no explanation for why this function is called + // from within Room 23 (the big machine), as it has no shadow effects + // and thus doesn't result in any visual differences. + + for (i = 0; i <= 255; i++) { + int r = (int) (*pal++ * redScale) >> 8; + int g = (int) (*pal++ * greenScale) >> 8; + int b = (int) (*pal++ * blueScale) >> 8; + + // The following functionality is similar to remapPaletteColor, except + // 1) we have to work off the original CLUT rather than the current palette, and + // 2) the target shadow palette entries must be bounded to the upper and lower + // bounds provided by the opcode. (This becomes significant in Room 48, but + // is not an issue in all other known case studies.) + int j; + int ar, ag, ab; + uint sum, bestsum, bestitem = 0; + + if (r > 255) + r = 255; + if (g > 255) + g = 255; + if (b > 255) + b = 255; + + bestsum = (uint)-1; + + r &= ~3; + g &= ~3; + b &= ~3; + + compareptr = basepal + startColor * 3; + for (j = startColor; j <= endColor; j++, compareptr += 3) { + ar = compareptr[0] & ~3; + ag = compareptr[1] & ~3; + ab = compareptr[2] & ~3; + if (ar == r && ag == g && ab == b) { + bestitem = j; + break; + } + + sum = colorWeight(ar - r, ag - g, ab - b); + + if (sum < bestsum) { + bestsum = sum; + bestitem = j; + } + } + *table++ = bestitem; + } +} + +/** This function create the specialPalette used for semi-transparency in SamnMax */ +void ScummEngine::createSpecialPalette(int16 from, int16 to, int16 redScale, int16 greenScale, int16 blueScale, + int16 startColor, int16 endColor) { + const byte *palPtr, *curPtr; + const byte *searchPtr; + + uint bestResult; + uint currentResult; + + byte currentIndex; + + int i, j; + + palPtr = getPalettePtr(_curPalIndex); + + for (i = 0; i < 256; i++) + _proc_special_palette[i] = i; + + curPtr = palPtr + startColor * 3; + + for (i = startColor; i < endColor; i++) { + int r = (int) (*curPtr++ * redScale) >> 8; + int g = (int) (*curPtr++ * greenScale) >> 8; + int b = (int) (*curPtr++ * blueScale) >> 8; + + if (r > 255) + r = 255; + if (g > 255) + g = 255; + if (b > 255) + b = 255; + + searchPtr = palPtr + from * 3; + bestResult = (uint)-1; + currentIndex = (byte) from; + + for (j = from; j <= to; j++) { + int ar = (*searchPtr++); + int ag = (*searchPtr++); + int ab = (*searchPtr++); + + currentResult = colorWeight(ar - r, ag - g, ab - b); + + if (currentResult < bestResult) { + _proc_special_palette[i] = currentIndex; + bestResult = currentResult; + } + currentIndex++; + } + } +} + +void ScummEngine::darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor) { + if (_roomResource == 0) // FIXME - HACK to get COMI demo working + return; + + if (startColor <= endColor) { + const byte *cptr; + byte *cur; + int j; + int color; + + cptr = getPalettePtr(_curPalIndex) + startColor * 3; + cur = _currentPalette + startColor * 3; + + for (j = startColor; j <= endColor; j++) { + // FIXME: Hack to fix Amiga palette adjustments + if ((_features & GF_AMIGA && _version == 5) && (j >= 16 && j < 81)) { + cptr += 3; + cur += 3; + continue; + } + + color = *cptr++; + color = color * redScale / 0xFF; + if (color > 255) + color = 255; + *cur++ = color; + + color = *cptr++; + color = color * greenScale / 0xFF; + if (color > 255) + color = 255; + *cur++ = color; + + color = *cptr++; + color = color * blueScale / 0xFF; + if (color > 255) + color = 255; + *cur++ = color; + } + setDirtyColors(startColor, endColor); + } +} + +static int HSL2RGBHelper(int n1, int n2, int hue) { + if (hue > 360) + hue = hue - 360; + else if (hue < 0) + hue = hue + 360; + + if (hue < 60) + return n1 + (n2 - n1) * hue / 60; + if (hue < 180) + return n2; + if (hue < 240) + return n1 + (n2 - n1) * (240 - hue) / 60; + return n1; +} + +/** + * This function scales the HSL (Hue, Saturation and Lightness) + * components of the palette colors. It's used in CMI when Guybrush + * walks from the beach towards the swamp. + */ +void ScummEngine::desaturatePalette(int hueScale, int satScale, int lightScale, int startColor, int endColor) { + + if (startColor <= endColor) { + const byte *cptr; + byte *cur; + int j; + + cptr = getPalettePtr(_curPalIndex) + startColor * 3; + cur = _currentPalette + startColor * 3; + + for (j = startColor; j <= endColor; j++) { + int R = *cptr++; + int G = *cptr++; + int B = *cptr++; + + // RGB to HLS (Foley and VanDam) + + const int min = MIN(R, MIN(G, B)); + const int max = MAX(R, MAX(G, B)); + const int diff = (max - min); + const int sum = (max + min); + + if (diff != 0) { + int H, S, L; + + if (sum <= 255) + S = 255 * diff / sum; + else + S = 255 * diff / (255 * 2 - sum); + + if (R == max) + H = 60 * (G - B) / diff; + else if (G == max) + H = 120 + 60 * (B - R) / diff; + else + H = 240 + 60 * (R - G) / diff; + + if (H < 0) + H = H + 360; + + // Scale the result + + H = (H * hueScale) / 255; + S = (S * satScale) / 255; + L = (sum * lightScale) / 255; + + // HLS to RGB (Foley and VanDam) + + int m1, m2; + if (L <= 255) + m2 = L * (255 + S) / (255 * 2); + else + m2 = L * (255 - S) / (255 * 2) + S; + + m1 = L - m2; + + R = HSL2RGBHelper(m1, m2, H + 120); + G = HSL2RGBHelper(m1, m2, H); + B = HSL2RGBHelper(m1, m2, H - 120); + } else { + // Maximal color = minimal color -> R=G=B -> it's a grayscale. + R = G = B = (R * lightScale) / 255; + } + + *cur++ = R; + *cur++ = G; + *cur++ = B; + } + + setDirtyColors(startColor, endColor); + } +} + + +int ScummEngine::remapPaletteColor(int r, int g, int b, uint threshold) { + int i; + int ar, ag, ab; + uint sum, bestsum, bestitem = 0; + byte *pal = _currentPalette; + + if (r > 255) + r = 255; + if (g > 255) + g = 255; + if (b > 255) + b = 255; + + bestsum = (uint) - 1; + + r &= ~3; + g &= ~3; + b &= ~3; + + for (i = 0; i < 256; i++, pal += 3) { + ar = pal[0] & ~3; + ag = pal[1] & ~3; + ab = pal[2] & ~3; + if (ar == r && ag == g && ab == b) + return i; + + sum = colorWeight(ar - r, ag - g, ab - b); + + if (sum < bestsum) { + bestsum = sum; + bestitem = i; + } + } + + if (threshold != (uint) - 1 && bestsum > colorWeight(threshold, threshold, threshold)) { + // Best match exceeded threshold. Try to find an unused palette entry and + // use it for our purpose. + pal = _currentPalette + (256 - 2) * 3; + for (i = 254; i > 48; i--, pal -= 3) { + if (pal[0] >= 252 && pal[1] >= 252 && pal[2] >= 252) { + setPalColor(i, r, g, b); + return i; + } + } + } + + return bestitem; +} + +void ScummEngine::swapPalColors(int a, int b) { + byte *ap, *bp; + byte t; + + if ((uint) a >= 256 || (uint) b >= 256) + error("swapPalColors: invalid values, %d, %d", a, b); + + ap = &_currentPalette[a * 3]; + bp = &_currentPalette[b * 3]; + + t = ap[0]; + ap[0] = bp[0]; + bp[0] = t; + t = ap[1]; + ap[1] = bp[1]; + bp[1] = t; + t = ap[2]; + ap[2] = bp[2]; + bp[2] = t; + + setDirtyColors(a, a); + setDirtyColors(b, b); +} + +void ScummEngine::copyPalColor(int dst, int src) { + byte *dp, *sp; + + if ((uint) dst >= 256 || (uint) src >= 256) + error("copyPalColor: invalid values, %d, %d", dst, src); + + dp = &_currentPalette[dst * 3]; + sp = &_currentPalette[src * 3]; + + dp[0] = sp[0]; + dp[1] = sp[1]; + dp[2] = sp[2]; + + setDirtyColors(dst, dst); +} + +void ScummEngine::setPalColor(int idx, int r, int g, int b) { + _currentPalette[idx * 3 + 0] = r; + _currentPalette[idx * 3 + 1] = g; + _currentPalette[idx * 3 + 2] = b; + setDirtyColors(idx, idx); +} + +void ScummEngine::setPalette(int palindex) { + const byte *pals; + + _curPalIndex = palindex; + pals = getPalettePtr(_curPalIndex); + setPaletteFromPtr(pals); +} + +const byte *ScummEngine::findPalInPals(const byte *pal, int idx) { + const byte *offs; + uint32 size; + + pal = findResource(MKID('WRAP'), pal); + if (pal == NULL) + return NULL; + + offs = findResourceData(MKID('OFFS'), pal); + if (offs == NULL) + return NULL; + + size = getResourceDataSize(offs) / 4; + + if ((uint32)idx >= (uint32)size) + return NULL; + + return offs + READ_LE_UINT32(offs + idx * sizeof(uint32)); +} + +const byte *ScummEngine::getPalettePtr(int palindex) { + const byte *cptr; + + cptr = getResourceAddress(rtRoom, _roomResource); + assert(cptr); + if (_CLUT_offs) { + cptr += _CLUT_offs; + } else { + cptr = findPalInPals(cptr + _PALS_offs, palindex); + } + assert(cptr); + return cptr; +} + +void ScummEngine::updatePalette() { + if (_palDirtyMax == -1) + return; + + bool noir_mode = (_gameId == GID_SAMNMAX && readVar(0x8000)); + int first = _palDirtyMin; + int num = _palDirtyMax - first + 1; + int i; + + byte palette_colors[1024]; + byte *p = palette_colors; + + for (i = _palDirtyMin; i <= _palDirtyMax; i++) { + byte *data; + + if (_features & GF_SMALL_HEADER && _version > 2) + data = _currentPalette + _shadowPalette[i] * 3; + else + data = _currentPalette + i * 3; + + // Sam & Max film noir mode. Convert the colours to grayscale + // before uploading them to the backend. + + if (noir_mode) { + int r, g, b; + byte brightness; + + r = data[0]; + g = data[1]; + b = data[2]; + + brightness = (byte)((0.299 * r + 0.587 * g + 0.114 * b) + 0.5); + + *p++ = brightness; + *p++ = brightness; + *p++ = brightness; + *p++ = 0; + } else { + *p++ = data[0]; + *p++ = data[1]; + *p++ = data[2]; + *p++ = 0; + } + } + + _system->set_palette(palette_colors, first, num); + + _palDirtyMax = -1; + _palDirtyMin = 256; +} + +} // End of namespace Scumm |