scummvm/engines/scumm/palette.cpp
2020-05-05 00:37:32 +02:00

1468 lines
42 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/config-manager.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "common/util.h"
#include "graphics/palette.h"
#include "scumm/resource.h"
#include "scumm/scumm.h"
#include "scumm/scumm_v6.h"
#include "scumm/scumm_v8.h"
#include "scumm/util.h"
#include "scumm/charset.h"
namespace Scumm {
uint8 *ScummEngine::getHEPaletteSlot(uint16 palSlot) {
assertRange(0, palSlot, _numPalettes, "palette");
if (_game.heversion >= 99) {
if (palSlot)
return _hePalettes + palSlot * _hePaletteSlot + 768;
else
return _hePalettes + _hePaletteSlot + 768;
}
return NULL;
}
uint16 ScummEngine::get16BitColor(uint8 r, uint8 g, uint8 b) {
return _outputPixelFormat.RGBToColor(r, g, b);
}
void ScummEngine::resetPalette() {
static const byte tableC64Palette[] = {
#if 1 // VICE-based palette. See bug #2847001
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x7E, 0x35, 0x2B, 0x6E, 0xB7, 0xC1,
0x7F, 0x3B, 0xA6, 0x5C, 0xA0, 0x35, 0x33, 0x27, 0x99, 0xCB, 0xD7, 0x65,
0x85, 0x53, 0x1C, 0x50, 0x3C, 0x00, 0xB4, 0x6B, 0x61, 0x4A, 0x4A, 0x4A,
0x75, 0x75, 0x75, 0xA3, 0xE7, 0x7C, 0x70, 0x64, 0xD6, 0xA3, 0xA3, 0xA3,
#else
0x00, 0x00, 0x00, 0xFD, 0xFE, 0xFC, 0xBE, 0x1A, 0x24, 0x30, 0xE6, 0xC6,
0xB4, 0x1A, 0xE2, 0x1F, 0xD2, 0x1E, 0x21, 0x1B, 0xAE, 0xDF, 0xF6, 0x0A,
0xB8, 0x41, 0x04, 0x6A, 0x33, 0x04, 0xFE, 0x4A, 0x57, 0x42, 0x45, 0x40,
0x70, 0x74, 0x6F, 0x59, 0xFE, 0x59, 0x5F, 0x53, 0xFE, 0xA4, 0xA7, 0xA2,
#endif
// Use 17 color table for v1 games to allow correct color for inventory and
// sentence line. Original games used some kind of dynamic color table
// remapping between rooms.
0x7F, 0x3B, 0xA6
};
/** This is Mesen's NTSC palette and the new default palette for ScummVM.
* It was chosen due to the accuracy of Mesen when it comes to emulating a NES
*/
static const byte tableNESNTSCPalette[] = {
0x66, 0x66, 0x66, 0x00, 0x2A, 0x88, 0x14, 0x12, 0xA7, 0x3B, 0x00, 0xA4,
0x5C, 0x00, 0x7E, 0x6E, 0x00, 0x40, 0x6C, 0x06, 0x00, 0x56, 0x1D, 0x00,
0x33, 0x35, 0x00, 0x0B, 0x48, 0x00, 0x00, 0x52, 0x00, 0x00, 0x4F, 0x08,
0x00, 0x40, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xAD, 0xAD, 0xAD, 0x15, 0x5F, 0xD9, 0x42, 0x40, 0xFF, 0x75, 0x27, 0xFE,
0xA0, 0x1A, 0xCC, 0xB7, 0x1E, 0x7B, 0xB5, 0x31, 0x20, 0x99, 0x4E, 0x00,
0x6B, 0x6D, 0x00, 0x38, 0x87, 0x00, 0x0C, 0x93, 0x00, 0x00, 0x8F, 0x32,
0x00, 0x7C, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFE, 0xFF, 0x64, 0xB0, 0xFF, 0x92, 0x90, 0xFF, 0xC6, 0x76, 0xFF,
0xF3, 0x6A, 0xFF, 0xFE, 0x6E, 0xCC, 0xFE, 0x81, 0x70, 0xEA, 0x9E, 0x22,
0xBC, 0xBE, 0x00, 0x88, 0xD8, 0x00, 0x5C, 0xE4, 0x30, 0x45, 0xE0, 0x82,
0x48, 0xCD, 0xDE, 0x4F, 0x4F, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFE, 0xFF, 0xC0, 0xDF, 0xFF, 0xD3, 0xD2, 0xFF, 0xE8, 0xC8, 0xFF,
0xFB, 0xC2, 0xFF, 0xFE, 0xC4, 0xEA, 0xFE, 0xCC, 0xC5, 0xF7, 0xD8, 0xA5,
0xE4, 0xE5, 0x94, 0xCF, 0xEF, 0x96, 0xBD, 0xF4, 0xAB, 0xB3, 0xF3, 0xCC,
0xB5, 0xEB, 0xF2, 0xB8, 0xB8, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/** This is an alternative palette based on FirebrandX's NES Classic
* palette. This palette is what Nintendo ships on the NES Classic which
* makes it somewhat of an "official" digital palette for the NES
*/
static const byte tableNESClassicPalette[] = {
0x60, 0x61, 0x5F, 0x00, 0x00, 0x83, 0x1D, 0x01, 0x95, 0x34, 0x08, 0x75,
0x51, 0x05, 0x5E, 0x56, 0x00, 0x0F, 0x4C, 0x07, 0x00, 0x37, 0x23, 0x08,
0x20, 0x3A, 0x0B, 0x0F, 0x4B, 0x0E, 0x19, 0x4C, 0x16, 0x02, 0x42, 0x1E,
0x02, 0x31, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xA9, 0xAA, 0xA8, 0x10, 0x4B, 0xBF, 0x47, 0x12, 0xD8, 0x63, 0x00, 0xCA,
0x88, 0x00, 0xA9, 0x93, 0x0B, 0x46, 0x8A, 0x2D, 0x04, 0x6F, 0x52, 0x06,
0x5C, 0x71, 0x14, 0x1B, 0x8D, 0x12, 0x19, 0x95, 0x09, 0x17, 0x84, 0x48,
0x20, 0x6B, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFB, 0xFB, 0xFB, 0x66, 0x99, 0xF8, 0x89, 0x74, 0xF9, 0xAB, 0x58, 0xF8,
0xD5, 0x57, 0xEF, 0xDE, 0x5F, 0xA9, 0xDC, 0x7F, 0x59, 0xC7, 0xA2, 0x24,
0xA7, 0xBE, 0x03, 0x75, 0xD7, 0x03, 0x60, 0xE3, 0x4F, 0x3C, 0xD6, 0x8D,
0x56, 0xC9, 0xCC, 0x41, 0x42, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFB, 0xFB, 0xFB, 0xBE, 0xD4, 0xFA, 0xC9, 0xC7, 0xF9, 0xD7, 0xBE, 0xFA,
0xE8, 0xB8, 0xF9, 0xF5, 0xBA, 0xE5, 0xF3, 0xCA, 0xC2, 0xDF, 0xCD, 0xA7,
0xD9, 0xE0, 0x9C, 0xC9, 0xEB, 0x9E, 0xC0, 0xED, 0xB8, 0xB5, 0xF4, 0xC7,
0xB9, 0xEA, 0xE9, 0xAB, 0xAB, 0xAB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const byte tableAmigaPalette[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x00, 0xBB, 0x00, 0x00, 0xBB, 0xBB,
0xBB, 0x00, 0x00, 0xBB, 0x00, 0xBB, 0xBB, 0x77, 0x00, 0xBB, 0xBB, 0xBB,
0x77, 0x77, 0x77, 0x77, 0x77, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
0xFF, 0x88, 0x88, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF
};
static const byte tableAmigaMIPalette[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x88, 0x22, 0x00, 0x66, 0x77,
0xBB, 0x66, 0x66, 0xAA, 0x22, 0xAA, 0x88, 0x55, 0x22, 0x77, 0x77, 0x77,
0x33, 0x33, 0x33, 0x22, 0x55, 0xDD, 0x22, 0xDD, 0x44, 0x00, 0xCC, 0xFF,
0xFF, 0x99, 0x99, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x77, 0xFF, 0xFF, 0xFF
};
static const byte tableEGAPalette[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
};
static const byte tableV1Palette[] = {
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xAA, 0x00, 0x00, 0x00, 0xAA, 0xAA,
0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xAA, 0xFF, 0xFF, 0x55,
0xFF, 0x55, 0x55, 0xAA, 0x55, 0x00, 0xFF, 0x55, 0x55, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0x55, 0xFF, 0x55, 0x55, 0x55, 0xFF, 0x55, 0x55, 0x55,
0xFF, 0x55, 0xFF
};
static const byte tableCGAPalette[] = {
0x00, 0x00, 0x00, 0x00, 0xA8, 0xA8, 0xA8, 0x00, 0xA8, 0xA8, 0xA8, 0xA8
};
static const byte tableHercAPalette[] = {
0x00, 0x00, 0x00, 0xAE, 0x69, 0x38
};
static const byte tableHercGPalette[] = {
0x00, 0x00, 0x00, 0x00, 0xFF, 0x00
};
// Palette based on Apple IIgs Technical Notes: IIgs 2523063 Master Color Values
// Rearranged to match C64 color positions
static const byte tableApple2gsPalette[] = {
0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xDD, 0x00, 0x33, 0x44, 0xFF, 0x99,
0xDD, 0x22, 0xDD, 0x00, 0x77, 0x22, 0x00, 0x00, 0x99, 0xFF, 0xFF, 0x00,
0xFF, 0x66, 0x00, 0x88, 0x55, 0x00, 0xFF, 0x99, 0x88, 0x55, 0x55, 0x55,
0xAA, 0xAA, 0xAA, 0x11, 0xDD, 0x00, 0x22, 0x22, 0xFF, 0xAA, 0xAA, 0xAA,
0x7F, 0x3B, 0xA6
};
#ifdef USE_RGB_COLOR
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
static const byte tableTownsV3Palette[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x00, 0xA0, 0xA0,
0xA0, 0x00, 0x00, 0xA0, 0x00, 0xA0, 0xA0, 0x60, 0x00, 0xA0, 0xA0, 0xA0,
0x60, 0x60, 0x60, 0x60, 0x60, 0xE0, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0xE0,
0xE0, 0x80, 0x80, 0xE0, 0x00, 0xE0, 0xE0, 0xE0, 0x00, 0xE0, 0xE0, 0xE0
};
static const byte tableTownsLoomPalette[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x00, 0xAB, 0x00, 0x00, 0xAB, 0xAB,
0xAB, 0x00, 0x00, 0x69, 0x29, 0x45, 0x8C, 0x4D, 0x14, 0xAB, 0xAB, 0xAB,
0x57, 0x3F, 0x57, 0x57, 0x57, 0xFF, 0x57, 0xFF, 0x57, 0x57, 0xFF, 0xFF,
0xFF, 0x57, 0x57, 0xD6, 0x94, 0x40, 0xFF, 0xFF, 0x57, 0xFF, 0xFF, 0xFF
};
#endif
#endif
if (_game.version <= 1) {
if (_game.platform == Common::kPlatformApple2GS) {
setPaletteFromTable(tableApple2gsPalette, sizeof(tableApple2gsPalette) / 3);
} else if (_game.platform == Common::kPlatformC64) {
setPaletteFromTable(tableC64Palette, sizeof(tableC64Palette) / 3);
} else if (_game.platform == Common::kPlatformNES) {
if (ConfMan.getBool("mm_nes_classic_palette"))
setPaletteFromTable(tableNESClassicPalette, sizeof(tableNESClassicPalette) / 3);
else
setPaletteFromTable(tableNESNTSCPalette, sizeof(tableNESNTSCPalette) / 3);
} else {
setPaletteFromTable(tableV1Palette, sizeof(tableV1Palette) / 3);
if (_game.id == GID_ZAK)
setPalColor(15, 170, 170, 170);
}
} else if (_game.features & GF_16COLOR) {
bool setupCursor = false;
switch (_renderMode) {
case Common::kRenderEGA:
setPaletteFromTable(tableEGAPalette, sizeof(tableEGAPalette) / 3);
break;
case Common::kRenderAmiga:
setPaletteFromTable(tableAmigaPalette, sizeof(tableAmigaPalette) / 3);
break;
case Common::kRenderCGA:
setPaletteFromTable(tableCGAPalette, sizeof(tableCGAPalette) / 3);
setupCursor = true;
break;
case Common::kRenderHercA:
setPaletteFromTable(tableHercAPalette, sizeof(tableHercAPalette) / 3);
setupCursor = true;
break;
case Common::kRenderHercG:
setPaletteFromTable(tableHercGPalette, sizeof(tableHercGPalette) / 3);
setupCursor = true;
break;
default:
if ((_game.platform == Common::kPlatformAmiga) || (_game.platform == Common::kPlatformAtariST))
setPaletteFromTable(tableAmigaPalette, sizeof(tableAmigaPalette) / 3);
else
setPaletteFromTable(tableEGAPalette, sizeof(tableEGAPalette) / 3);
}
if (setupCursor) {
// Setup cursor palette
setPalColor( 7, 170, 170, 170);
setPalColor( 8, 85, 85, 85);
setPalColor(15, 255, 255, 255);
}
} else {
if ((_game.platform == Common::kPlatformAmiga) && _game.version == 4) {
// if rendermode is set to EGA we use the full palette from the resources
// else we initialize and then lock down the first 16 colors.
if (_renderMode != Common::kRenderEGA)
setPaletteFromTable(tableAmigaMIPalette, sizeof(tableAmigaMIPalette) / 3);
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
} else if (_game.platform == Common::kPlatformFMTowns) {
if (_game.id == GID_INDY4 || _game.id == GID_MONKEY2)
_townsClearLayerFlag = 0;
#ifdef USE_RGB_COLOR
else if (_game.id == GID_LOOM)
towns_setTextPaletteFromPtr(tableTownsLoomPalette);
else if (_game.version == 3)
towns_setTextPaletteFromPtr(tableTownsV3Palette);
#endif
_townsScreen->toggleLayers(_townsActiveLayerFlags);
#endif // DISABLE_TOWNS_DUAL_LAYER_MODE
}
setDirtyColors(0, 255);
}
}
void ScummEngine::setPaletteFromTable(const byte *ptr, int numcolor, int index) {
for ( ; numcolor > 0; --numcolor, ++index, ptr += 3)
setPalColor( index, ptr[0], ptr[1], ptr[2]);
}
void ScummEngine::colorPCEToRGB(uint16 color, byte *r, byte *g, byte *b) {
// 3 bits for each color component: 0xgggrrrbbb
*b = ((color) & 0x7) * 0xFF / 0x7;
*r = ((color >> 3) & 0x7) * 0xFF / 0x7;
*g = ((color >> 6) & 0x7) * 0xFF / 0x7;
}
void ScummEngine::setPCETextPalette(uint8 color) {
const uint16 CHARSET_COLORS[16] = {
0x0000, 0x0096, 0x0140, 0x0145, 0x0059, 0x002D, 0x00A8, 0x016D,
0x0092, 0x016F, 0x01CD, 0x01DF, 0x00F7, 0x00B6, 0x01B0, 0x01B6
};
byte r, g, b;
colorPCEToRGB(CHARSET_COLORS[color], &r, &g, &b);
setPalColor(15, r, g, b);
}
void ScummEngine::readPCEPalette(const byte **ptr, byte **dest, int numEntries) {
byte r, g, b;
byte msbs = 0;
for (int i = 0; i < numEntries; ++i) {
if (i % 8 == 0) {
// byte contains MSBs (bit 8) for the next 8 bytes
msbs = *(*ptr)++;
}
uint16 msb = (msbs & 0x1) << 8;
uint16 paletteEntry = msb | *(*ptr)++;
colorPCEToRGB(paletteEntry, &r, &g, &b);
*(*dest)++ = r;
*(*dest)++ = g;
*(*dest)++ = b;
msbs >>= 1;
}
}
void ScummEngine::setPCEPaletteFromPtr(const byte *ptr) {
byte *dest;
byte bgSpriteR, bgSpriteG, bgSpriteB;
byte charsetR, charsetG, charsetB;
int paletteOffset = *ptr++;
int numPalettes = *ptr++;
int firstIndex = paletteOffset * 16;
int numcolor = numPalettes * 16;
// the only color over which a background sprite
// (bit 7 of the sprite attributes) will be visible
colorPCEToRGB(READ_LE_UINT16(ptr), &bgSpriteR, &bgSpriteG, &bgSpriteB);
ptr += 2;
// CHARSET_COLORS[_curTextColor] (unused?)
colorPCEToRGB(0x01B6, &charsetR, &charsetG, &charsetB);
dest = _currentPalette + firstIndex * 3;
for (int i = 0; i < numPalettes; ++i) {
// entry 0
*dest++ = bgSpriteR;
*dest++ = bgSpriteG;
*dest++ = bgSpriteB;
// entry 1 - 14
readPCEPalette(&ptr, &dest, 14);
// entry 15
*dest++ = charsetR;
*dest++ = charsetG;
*dest++ = charsetB;
}
if (_game.features & GF_16BIT_COLOR) {
for (int i = firstIndex; i < firstIndex + numcolor; ++i)
_16BitPalette[i] = get16BitColor(_currentPalette[i * 3 + 0], _currentPalette[i * 3 + 1], _currentPalette[i * 3 + 2]);
}
setDirtyColors(firstIndex, firstIndex + numcolor - 1);
}
void ScummEngine::setPaletteFromPtr(const byte *ptr, int numcolor) {
int firstIndex = 0;
int i;
byte *dest, r, g, b;
if (numcolor < 0) {
if (_game.features & GF_SMALL_HEADER) {
if (_game.features & GF_OLD256)
numcolor = READ_LE_UINT16(ptr);
else
numcolor = READ_LE_UINT16(ptr) / 3;
ptr += 2;
} else {
numcolor = getResourceDataSize(ptr) / 3;
}
}
assertRange(0, numcolor, 256, "setPaletteFromPtr: numcolor");
dest = _currentPalette;
// Test for Amiga Monkey Island and EGA Mode unset, if true then skip the first 16 colors.
if ((_game.platform == Common::kPlatformAmiga) && _game.version == 4 && _renderMode != Common::kRenderEGA) {
firstIndex = 16;
dest += 3 * 16;
ptr += 3 * 16;
}
for (i = firstIndex; i < numcolor; i++) {
r = *ptr++;
g = *ptr++;
b = *ptr++;
// Only SCUMM 5/6 games use 6/6/6 style palettes
if (_game.version >= 5 && _game.version <= 6) {
if ((_game.heversion <= 74 && i < 15) || i == 15 || r < 252 || g < 252 || b < 252) {
*dest++ = r;
*dest++ = g;
*dest++ = b;
} else {
dest += 3;
}
} else {
*dest++ = r;
*dest++ = g;
*dest++ = b;
}
}
if (_game.heversion >= 90 || _game.version == 8) {
memcpy(_darkenPalette, _currentPalette, 768);
}
setDirtyColors(firstIndex, numcolor - 1);
}
void ScummEngine::setAmigaPaletteFromPtr(const byte *ptr) {
memcpy(_currentPalette, ptr, 768);
for (int i = 0; i < 32; ++i) {
_shadowPalette[i] = i;
_colorUsedByCycle[i] = 0;
}
amigaPaletteFindFirstUsedColor();
for (int i = 0; i < 64; ++i) {
_amigaPalette[i * 3 + 0] = _currentPalette[(i + 16) * 3 + 0] >> 4;
_amigaPalette[i * 3 + 1] = _currentPalette[(i + 16) * 3 + 1] >> 4;
_amigaPalette[i * 3 + 2] = _currentPalette[(i + 16) * 3 + 2] >> 4;
}
for (int i = 0; i < 256; ++i) {
if (i < 16 || i >= _amigaFirstUsedColor) {
mapRoomPalette(i);
mapVerbPalette(i);
} else {
int idx = (i - 16) & 31;
// We adjust our verb palette map from [0, 31] to [32, 63], since unlike
// the original we set up the verb palette at colors [32, 63].
// The original instead used two different palettes for the verb virtual
// screen and all the rest.
if (idx != 17) {
_roomPalette[i] = idx;
_verbPalette[i] = idx + 32;
} else {
// In all my tests it seems the colors 0 and 32 in
// _amigaPalette are in fact black. Thus 17 is probably black.
// For the room map the color 17 is 33 (17+16), for the verb
// map it is 65 (17+32).
_roomPalette[i] = 0;
_verbPalette[i] = 32;
}
}
}
setDirtyColors(0, 255);
}
void ScummEngine::amigaPaletteFindFirstUsedColor() {
for (_amigaFirstUsedColor = 80; _amigaFirstUsedColor < 256; ++_amigaFirstUsedColor) {
// We look for the first used color here. If all color components are
// >= 252 the color seems to be unused. Check remapPaletteColor for
// the same behavior.
if (_currentPalette[_amigaFirstUsedColor * 3 + 0] <= 251
|| _currentPalette[_amigaFirstUsedColor * 3 + 1] <= 251
|| _currentPalette[_amigaFirstUsedColor * 3 + 2] <= 251)
break;
}
}
void ScummEngine::mapRoomPalette(int idx) {
// For Color 33 (which is in fact 17+16) see the special case in
// setAmigaPaletteFromPtr.
if (idx >= 16 && idx < 48 && idx != 33)
_roomPalette[idx] = idx - 16;
else
_roomPalette[idx] = remapRoomPaletteColor(_currentPalette[idx * 3 + 0] >> 4,
_currentPalette[idx * 3 + 1] >> 4,
_currentPalette[idx * 3 + 2] >> 4);
}
static const uint8 amigaWeightTable[16] = {
0, 1, 4, 9, 16, 25, 36, 49,
64, 81, 100, 121, 144, 169, 196, 225
};
int ScummEngine::remapRoomPaletteColor(int r, int g, int b) {
int idx = 0;
uint16 minValue = 0xFFFF;
const byte *pal = _amigaPalette;
const byte *cycle = _colorUsedByCycle;
for (int i = 0; i < 32; ++i) {
if (!*cycle++ && i != 17) {
int rD = ABS<int>(*pal++ - r);
int gD = ABS<int>(*pal++ - g);
int bD = ABS<int>(*pal++ - b);
const uint16 weight = amigaWeightTable[rD] + amigaWeightTable[gD] + amigaWeightTable[bD];
if (weight < minValue) {
minValue = weight;
idx = i;
}
} else {
pal += 3;
}
}
return idx;
}
void ScummEngine::mapVerbPalette(int idx) {
// We adjust our verb palette map from [0, 31] to [32, 63], since unlike
// the original we set up the verb palette at colors [32, 63].
// The original instead used two different palettes for the verb virtual
// screen and all the rest.
// For Color 65 (which is in fact 17+32) see the special case in
// setAmigaPaletteFromPtr.
if (idx >= 48 && idx < 80 && idx != 65)
_verbPalette[idx] = idx - 16;
else
_verbPalette[idx] = remapVerbPaletteColor(_currentPalette[idx * 3 + 0] >> 4,
_currentPalette[idx * 3 + 1] >> 4,
_currentPalette[idx * 3 + 2] >> 4) + 32;
}
int ScummEngine::remapVerbPaletteColor(int r, int g, int b) {
int idx = 0;
uint16 minValue = 0xFFFF;
const byte *pal = _amigaPalette + 32 * 3;
for (int i = 0; i < 32; ++i) {
if (i != 17) {
int rD = ABS<int>(*pal++ - r);
int gD = ABS<int>(*pal++ - g);
int bD = ABS<int>(*pal++ - b);
const uint16 weight = amigaWeightTable[rD] + amigaWeightTable[gD] + amigaWeightTable[bD];
if (weight < minValue) {
minValue = weight;
idx = i;
}
} else {
pal += 3;
}
}
return idx;
}
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 (_game.features & GF_SMALL_HEADER) {
cycl = _colorCycle;
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 {
memset(_colorUsedByCycle, 0, sizeof(_colorUsedByCycle));
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++;
if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) {
cycl->start = CLIP(cycl->start - 16, 0, 31);
cycl->end = CLIP(cycl->end - 16, 0, 31);
}
for (int i = cycl->start; i <= cycl->end; ++i) {
_colorUsedByCycle[i] = 1;
}
}
}
if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) {
for (int i = 0; i < 256; ++i) {
if (i >= 16 && i < _amigaFirstUsedColor)
continue;
if (_colorUsedByCycle[_roomPalette[i]])
mapRoomPalette(i);
}
}
}
void ScummEngine::stopCycle(int i) {
ColorCycle *cycl;
assertRange(0, i, 16, "stopCycle: cycle");
if (i != 0) {
_colorCycle[i - 1].delay = 0;
if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) {
cycl = &_colorCycle[i - 1];
for (int j = cycl->start; j <= cycl->end && j < 32; ++j) {
_shadowPalette[j] = j;
_colorUsedByCycle[j] = 0;
}
}
return;
}
for (i = 0, cycl = _colorCycle; i < 16; i++, cycl++) {
cycl->delay = 0;
if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) {
for (int j = cycl->start; j <= cycl->end && j < 32; ++j) {
_shadowPalette[j] = j;
_colorUsedByCycle[j] = 0;
}
}
}
}
/**
* Cycle the colors in the given palette in the interval [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;
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_game.platform == Common::kPlatformFMTowns && !(_townsPaletteFlags & 1))
return;
#endif
valueToAdd = VAR(VAR_TIMER);
if (valueToAdd < VAR(VAR_TIMER_NEXT))
valueToAdd = VAR(VAR_TIMER_NEXT);
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 (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) {
doCyclePalette(_shadowPalette, cycl->start, cycl->end, 1, !(cycl->flags & 2));
} else {
doCyclePalette(_currentPalette, cycl->start, cycl->end, 3, !(cycl->flags & 2));
if (_shadowPalette) {
if (_game.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) {
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_game.platform == Common::kPlatformFMTowns && !(_townsPaletteFlags & 1))
return;
#endif
// This function is actually a nullsub in Indy4 Amiga.
// It might very well be a nullsub in other Amiga games, but for now I
// limit this to Indy4 Amiga, since that is the only game I can check.
if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4)
return;
byte *string1 = getStringAddress(resID);
byte *string2 = getStringAddress(resID + 1);
byte *string3 = getStringAddress(resID + 2);
if (!string1 || !string2 || !string3) {
error("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);
byte *pal = _currentPalette + start * 3;
byte *target = _palManipPalette + start * 3;
uint16 *between = (uint16 *)(_palManipIntermediatePal + start * 6);
for (int i = start; i < end; ++i) {
*target++ = *string1++;
*target++ = *string2++;
*target++ = *string3++;
*between++ = ((uint16) *pal++) << 8;
*between++ = ((uint16) *pal++) << 8;
*between++ = ((uint16) *pal++) << 8;
}
_palManipCounter = time;
}
void ScummEngine_v6::palManipulateInit(int resID, int start, int end, int time) {
const byte *new_pal;
new_pal = getPalettePtr(resID, _roomResource);
new_pal += start*3;
_palManipStart = start;
_palManipEnd = end;
_palManipCounter = 0;
if (!_palManipPalette)
_palManipPalette = (byte *)calloc(0x300, 1);
if (!_palManipIntermediatePal)
_palManipIntermediatePal = (byte *)calloc(0x600, 1);
byte *pal = _currentPalette + start * 3;
byte *target = _palManipPalette + start * 3;
uint16 *between = (uint16 *)(_palManipIntermediatePal + start * 6);
for (int i = start; i < end; ++i) {
*target++ = *new_pal++;
*target++ = *new_pal++;
*target++ = *new_pal++;
*between++ = ((uint16) *pal++) << 8;
*between++ = ((uint16) *pal++) << 8;
*between++ = ((uint16) *pal++) << 8;
}
_palManipCounter = time;
}
void ScummEngine::palManipulate() {
if (!_palManipCounter || !_palManipPalette || !_palManipIntermediatePal)
return;
byte *target = _palManipPalette + _palManipStart * 3;
byte *pal = _currentPalette + _palManipStart * 3;
uint16 *between = (uint16 *)(_palManipIntermediatePal + _palManipStart * 6);
for (int i = _palManipStart; i < _palManipEnd; ++i) {
int j;
j = (*between += ((*target++ << 8) - *between) / _palManipCounter);
*pal++ = j >> 8;
between++;
j = (*between += ((*target++ << 8) - *between) / _palManipCounter);
*pal++ = j >> 8;
between++;
j = (*between += ((*target++ << 8) - *between) / _palManipCounter);
*pal++ = j >> 8;
between++;
}
setDirtyColors(_palManipStart, _palManipEnd);
_palManipCounter--;
}
void ScummEngine::setShadowPalette(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("setShadowPalette: invalid slot %d", slot);
if (startColor < 0 || startColor > 255 || endColor < 0 || endColor > 255 || endColor < startColor)
error("setShadowPalette: 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,
-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::setShadowPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor, int start, int end) {
// This function is actually a nullsub in Indy4 Amiga.
// It might very well be a nullsub in other Amiga games, but for now I
// limit this to Indy4 Amiga, since that is the only game I can check.
if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4)
return;
const byte *basepal = getPalettePtr(_curPalIndex, _roomResource);
const byte *compareptr;
const byte *pal = basepal + start * 3;
byte *table = _shadowPalette + start;
int i;
// This is an implementation based on the original games code.
//
// The four known rooms where setShadowPalette is used in atlantis are:
//
// 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
//
// 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.
if (_game.id == GID_SAMNMAX) {
for (i = 0; i < 256; i++)
_shadowPalette[i] = i;
}
for (i = start; i < end; i++) {
int r = (int)((pal[0] >> 2) * redScale) >> 8;
int g = (int)((pal[1] >> 2) * greenScale) >> 8;
int b = (int)((pal[2] >> 2) * blueScale) >> 8;
pal += 3;
uint8 bestitem = 0;
uint bestsum = 32000;
compareptr = basepal + startColor * 3;
for (int j = startColor; j <= endColor; j++, compareptr += 3) {
int ar = compareptr[0] >> 2;
int ag = compareptr[1] >> 2;
int ab = compareptr[2] >> 2;
uint sum = ABS(ar - r) + ABS(ag - g) + ABS(ab - b);
if (sum < bestsum) {
bestsum = sum;
bestitem = j;
}
}
*table++ = bestitem;
}
}
void ScummEngine::darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor) {
if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) {
startColor = CLIP(startColor, 0, 255);
//bool remappedVerbColors = false;
bool remappedRoomColors = false;
bool cycleFlag = (blueScale >= 250 && greenScale >= 250 && redScale >= 250);
const byte *palptr = getPalettePtr(_curPalIndex, _roomResource) + startColor * 3;
for (int i = startColor; i <= endColor; ++i) {
if (i > 16 && i < 48) {
if (cycleFlag)
_colorUsedByCycle[i - 16] &= ~2;
else
_colorUsedByCycle[i - 16] |= 2;
}
_currentPalette[i * 3 + 0] = (*palptr++ * redScale) >> 8;
_currentPalette[i * 3 + 1] = (*palptr++ * greenScale) >> 8;
_currentPalette[i * 3 + 2] = (*palptr++ * blueScale) >> 8;
}
for (int i = startColor; i <= endColor; ++i) {
// Colors 33 (17+16) and 65 (17+32) will never get changed. For
// more information about these check setAmigaPaletteFromPtr.
if (i >= 16 && i < 48 && i != 33) {
remappedRoomColors = true;
_amigaPalette[(i - 16) * 3 + 0] = _currentPalette[i * 3 + 0] >> 4;
_amigaPalette[(i - 16) * 3 + 1] = _currentPalette[i * 3 + 1] >> 4;
_amigaPalette[(i - 16) * 3 + 2] = _currentPalette[i * 3 + 2] >> 4;
} else if (i >= 48 && i < 80 && i != 65) {
//remappedVerbColors = true;
_amigaPalette[(i - 16) * 3 + 0] = _currentPalette[i * 3 + 0] >> 4;
_amigaPalette[(i - 16) * 3 + 1] = _currentPalette[i * 3 + 1] >> 4;
_amigaPalette[(i - 16) * 3 + 2] = _currentPalette[i * 3 + 2] >> 4;
}
}
for (int i = 0; i < 256; ++i) {
if (i >= 16 && i < _amigaFirstUsedColor)
continue;
bool inRange = (startColor <= i && i <= endColor);
int idx = _roomPalette[i] + 16;
bool mappedInRange = (startColor <= idx && idx <= endColor);
if (inRange != mappedInRange || (remappedRoomColors && cycleFlag))
mapRoomPalette(i);
}
setDirtyColors(startColor, endColor);
} else {
int max;
if (_game.version >= 5 && _game.version <= 6 && _game.heversion <= 60) {
max = 252;
} else {
max = 255;
}
if (startColor <= endColor) {
const byte *cptr;
const byte *palptr;
int color, idx, j;
if (_game.heversion >= 90 || _game.version == 8) {
palptr = _darkenPalette;
} else {
palptr = getPalettePtr(_curPalIndex, _roomResource);
}
for (j = startColor; j <= endColor; j++) {
idx = (_game.heversion == 70) ? _HEV7ActorPalette[j] : j;
cptr = palptr + idx * 3;
if (_game.heversion == 70)
setDirtyColors(idx, idx);
color = *cptr++;
color = color * redScale / 0xFF;
if (color > max)
color = max;
_currentPalette[idx * 3 + 0] = color;
color = *cptr++;
color = color * greenScale / 0xFF;
if (color > max)
color = max;
_currentPalette[idx * 3 + 1] = color;
color = *cptr++;
color = color * blueScale / 0xFF;
if (color > max)
color = max;
_currentPalette[idx * 3 + 2] = color;
if (_game.features & GF_16BIT_COLOR)
_16BitPalette[idx] = get16BitColor(_currentPalette[idx * 3 + 0], _currentPalette[idx * 3 + 1], _currentPalette[idx * 3 + 2]);
}
if (_game.heversion != 70)
setDirtyColors(startColor, endColor);
}
}
}
#ifdef ENABLE_SCUMM_7_8
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_v8::desaturatePalette(int hueScale, int satScale, int lightScale, int startColor, int endColor) {
if (startColor <= endColor) {
const byte *cptr;
byte *cur;
int j;
cptr = _darkenPalette + 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);
}
}
#endif
int ScummEngine::remapPaletteColor(int r, int g, int b, int threshold) {
byte *pal;
int ar, ag, ab, i;
uint sum, bestsum, bestitem = 0;
int startColor = (_game.version == 8) ? 24 : 1;
if (_game.heversion >= 99)
pal = _hePalettes + 1024 + startColor * 3;
else
pal = _currentPalette + startColor * 3;
if (r > 255)
r = 255;
if (g > 255)
g = 255;
if (b > 255)
b = 255;
bestsum = 0x7FFFFFFF;
r &= ~3;
g &= ~3;
b &= ~3;
for (i = startColor; i < 255; i++, pal += 3) {
if (_game.version == 7 && _colorUsedByCycle[i])
continue;
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 != -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;
if (_game.features & GF_16BIT_COLOR) {
_16BitPalette[a] = get16BitColor(ap[0], ap[1], ap[2]);
_16BitPalette[b] = get16BitColor(bp[0], bp[1], bp[2]);
}
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];
if (_game.features & GF_16BIT_COLOR)
_16BitPalette[dst] = get16BitColor(sp[0], sp[1], sp[2]);
setDirtyColors(dst, dst);
}
void ScummEngine::setPalColor(int idx, int r, int g, int b) {
if (_game.heversion == 70)
idx = _HEV7ActorPalette[idx];
_currentPalette[idx * 3 + 0] = r;
_currentPalette[idx * 3 + 1] = g;
_currentPalette[idx * 3 + 2] = b;
if (_game.version == 8) {
_darkenPalette[idx * 3 + 0] = r;
_darkenPalette[idx * 3 + 1] = g;
_darkenPalette[idx * 3 + 2] = b;
}
if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) {
// Colors 33 (17+16) and 65 (17+32) will never get changed. For
// more information about these check setAmigaPaletteFromPtr.
if (idx < 16 || idx >= _amigaFirstUsedColor) {
mapRoomPalette(idx);
mapVerbPalette(idx);
} else if (idx >= 16 && idx < 48 && idx != 33) {
_amigaPalette[(idx - 16) * 3 + 0] = _currentPalette[idx * 3 + 0] >> 4;
_amigaPalette[(idx - 16) * 3 + 1] = _currentPalette[idx * 3 + 1] >> 4;
_amigaPalette[(idx - 16) * 3 + 2] = _currentPalette[idx * 3 + 2] >> 4;
for (int i = 0; i < 256; ++i) {
if (i >= 16 && i < _amigaFirstUsedColor)
continue;
if (idx - 16 == _roomPalette[i])
mapRoomPalette(i);
}
} else if (idx >= 48 && idx < 80 && idx != 65) {
_amigaPalette[(idx - 16) * 3 + 0] = _currentPalette[idx * 3 + 0] >> 4;
_amigaPalette[(idx - 16) * 3 + 1] = _currentPalette[idx * 3 + 1] >> 4;
_amigaPalette[(idx - 16) * 3 + 2] = _currentPalette[idx * 3 + 2] >> 4;
for (int i = 0; i < 256; ++i) {
if (i >= 16 && i < _amigaFirstUsedColor)
continue;
// We do - 16 instead of - 48 like the original, since our
// verb palette map is using [32, 63] instead of [0, 31].
if (idx - 16 == _verbPalette[i])
mapVerbPalette(i);
}
}
}
if (_game.features & GF_16BIT_COLOR)
_16BitPalette[idx] = get16BitColor(r, g, b);
setDirtyColors(idx, idx);
}
void ScummEngine::setCurrentPalette(int palindex) {
const byte *pals;
_curPalIndex = palindex;
pals = getPalettePtr(_curPalIndex, _roomResource);
if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {
setPCEPaletteFromPtr(pals);
#ifdef USE_RGB_COLOR
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
} else if (_game.platform == Common::kPlatformFMTowns) {
towns_setPaletteFromPtr(pals);
#endif
#endif
} else if (_game.id == GID_INDY4 && _game.platform == Common::kPlatformAmiga) {
setAmigaPaletteFromPtr(pals);
} else {
setPaletteFromPtr(pals);
}
}
void ScummEngine::setRoomPalette(int palindex, int room) {
const byte *roomptr = getResourceAddress(rtRoom, room);
assert(roomptr);
const byte *pals = findResource(MKTAG('P','A','L','S'), roomptr);
assert(pals);
const byte *rgbs = findPalInPals(pals, palindex);
assert(rgbs);
setPaletteFromPtr(rgbs);
}
const byte *ScummEngine::findPalInPals(const byte *pal, int idx) {
const byte *offs;
uint32 size;
pal = findResource(MKTAG('W','R','A','P'), pal);
if (pal == NULL)
return NULL;
offs = findResourceData(MKTAG('O','F','F','S'), 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, int room) {
const byte *cptr;
cptr = getResourceAddress(rtRoom, room);
assert(cptr);
if (_CLUT_offs) {
cptr += _CLUT_offs;
} else {
cptr = findPalInPals(cptr + _PALS_offs, palindex);
assert(cptr);
}
return cptr;
}
void ScummEngine::updatePalette() {
if (_game.features & GF_16BIT_COLOR)
return;
if (_palDirtyMax == -1)
return;
byte palette_colors[3 * 256];
byte *p = palette_colors;
int first;
int num;
if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) {
// Indy4 Amiga has a special palette handling scheme
first = 0;
num = 64;
for (int i = 0; i < 64; ++i) {
byte *data;
if (i < 32)
data = _amigaPalette + _shadowPalette[i] * 3;
else
data = _amigaPalette + i * 3;
*p++ = data[0] * 255 / 15;
*p++ = data[1] * 255 / 15;
*p++ = data[2] * 255 / 15;
}
// Setup colors for the mouse cursor
// Color values taken from Indy4 DOS
static const uint8 mouseCursorPalette[] = {
255, 255, 255,
171, 171, 171,
87, 87, 87
};
_system->getPaletteManager()->setPalette(mouseCursorPalette, 252, 3);
} else {
bool noir_mode = (_game.id == GID_SAMNMAX && readVar(0x8000));
int i;
first = _palDirtyMin;
num = _palDirtyMax - first + 1;
for (i = _palDirtyMin; i <= _palDirtyMax; i++) {
byte *data;
if (_game.features & GF_SMALL_HEADER && _game.version > 2)
data = _currentPalette + _shadowPalette[i] * 3;
else
data = _currentPalette + i * 3;
// Sam & Max film noir mode. Convert the colors 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;
} else {
*p++ = data[0];
*p++ = data[1];
*p++ = data[2];
}
}
}
_palDirtyMax = -1;
_palDirtyMin = 256;
#ifdef USE_RGB_COLOR
#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
if (_game.platform == Common::kPlatformFMTowns) {
p = palette_colors;
for (int i = first; i < first + num; ++i) {
_16BitPalette[i] = get16BitColor(p[0], p[1], p[2]);
p += 3;
}
return;
}
#endif
#endif
_system->getPaletteManager()->setPalette(palette_colors, first, num);
}
} // End of namespace Scumm