From 4ea4172cbad466738836f7be8ebdcad4eabd0bb9 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Fri, 26 Aug 2011 05:51:08 +0200 Subject: SCUMM: Implement proper Indy4 Amiga palette handling. This should fix incorrect text colors in some scenes. --- engines/scumm/charset.cpp | 26 ++- engines/scumm/charset.h | 3 + engines/scumm/costume.cpp | 12 +- engines/scumm/cursor.cpp | 5 + engines/scumm/gfx.cpp | 30 ++++ engines/scumm/palette.cpp | 409 ++++++++++++++++++++++++++++++++++++--------- engines/scumm/room.cpp | 7 +- engines/scumm/saveload.cpp | 7 + engines/scumm/scumm.cpp | 6 + engines/scumm/scumm.h | 11 ++ 10 files changed, 427 insertions(+), 89 deletions(-) diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index 8558da397e..f7b98e2451 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -767,6 +767,13 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { _textScreenID = vs->number; } + // We need to know the virtual screen we draw on for Indy 4 Amiga, since + // it selects the palette map according to this. We furthermore can not + // use _textScreenID here, since that will cause inventory graphics + // glitches. + if (_vm->_game.platform == Common::kPlatformAmiga && _vm->_game.id == GID_INDY4) + _drawScreen = vs->number; + printCharIntern(is2byte, _charPtr, _origWidth, _origHeight, _width, _height, vs, ignoreCharsetMask); _left += _origWidth; @@ -917,12 +924,27 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co numbits = 8; byte *cmap = _vm->_charsetColorMap; + // Indy4 Amiga always uses the room or verb palette map to match colors to + // the currently setup palette, thus we need to select it over here too. + // Done like the original interpreter. + byte *amigaMap = 0; + if (_vm->_game.platform == Common::kPlatformAmiga && _vm->_game.id == GID_INDY4) { + if (_drawScreen == kVerbVirtScreen) + amigaMap = _vm->_verbPalette; + else + amigaMap = _vm->_roomPalette; + } + for (y = 0; y < height && y + drawTop < s.h; y++) { for (x = 0; x < width; x++) { color = (bits >> (8 - bpp)) & 0xFF; - if (color && y + drawTop >= 0) - *dst = cmap[color]; + if (color && y + drawTop >= 0) { + if (amigaMap) + *dst = amigaMap[cmap[color]]; + else + *dst = cmap[color]; + } dst++; bits <<= bpp; numbits -= bpp; diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h index b23ec996f5..fabb82b389 100644 --- a/engines/scumm/charset.h +++ b/engines/scumm/charset.h @@ -119,6 +119,9 @@ protected: int _offsX, _offsY; const byte *_charPtr; + // On which virtual screen will be drawn right now + VirtScreenNumber _drawScreen; + public: CharsetRendererClassic(ScummEngine *vm) : CharsetRendererCommon(vm) {} diff --git a/engines/scumm/costume.cpp b/engines/scumm/costume.cpp index 4ca4988605..eb3cc3262c 100644 --- a/engines/scumm/costume.cpp +++ b/engines/scumm/costume.cpp @@ -545,6 +545,13 @@ void ClassicCostumeRenderer::proc3_ami(Codec1 &v1) { oldXpos = v1.x; oldScaleIndexX = _scaleIndexX; + // Indy4 Amiga always uses the room map to match colors to the currently + // setup palette in the actor code in the original, thus we need to do this + // mapping over here too. + byte *amigaMap = 0; + if (_vm->_game.platform == Common::kPlatformAmiga && _vm->_game.id == GID_INDY4) + amigaMap = _vm->_roomPalette; + do { len = *src++; color = len >> v1.shr; @@ -556,7 +563,10 @@ void ClassicCostumeRenderer::proc3_ami(Codec1 &v1) { masked = (y < 0 || y >= _out.h) || (v1.x < 0 || v1.x >= _out.w) || (v1.mask_ptr && (mask[0] & maskbit)); if (color && !masked) { - *dst = _palette[color]; + if (amigaMap) + *dst = amigaMap[_palette[color]]; + else + *dst = _palette[color]; } if (_scaleX == 255 || v1.scaletable[_scaleIndexX] < _scaleX) { diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp index 6739282c9d..1c79ac7a5a 100644 --- a/engines/scumm/cursor.cpp +++ b/engines/scumm/cursor.cpp @@ -610,6 +610,11 @@ void ScummEngine_v5::setBuiltinCursor(int idx) { WRITE_UINT16(_grabbedCursor + i * 2, 0xFF); } else { color = default_cursor_colors[idx]; + // Indy4 Amiga always uses the room or verb palette map to match colors to + // the currently setup palette, thus we need to select it over here too. + // This is guesswork! + if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) + color = _roomPalette[color]; memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor)); } diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp index 8a32b963cd..a22aa1802f 100644 --- a/engines/scumm/gfx.cpp +++ b/engines/scumm/gfx.cpp @@ -1025,6 +1025,16 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) { if (rect.left > vs->w) return; + // Indy4 Amiga always uses the room or verb palette map to match colors to + // the currently setup palette, thus we need to select it over here too. + // Done like the original interpreter. + if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) { + if (vs->number == kVerbVirtScreen) + backColor = _verbPalette[backColor]; + else + backColor = _roomPalette[backColor]; + } + // Convert 'rect' to local (virtual screen) coordinates rect.top -= vs->topline; rect.bottom -= vs->topline; @@ -1235,6 +1245,16 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) { if ((vs = findVirtScreen(y)) == NULL) return; + // Indy4 Amiga always uses the room or verb palette map to match colors to + // the currently setup palette, thus we need to select it over here too. + // Done like the original interpreter. + if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) { + if (vs->number == kVerbVirtScreen) + color = _verbPalette[color]; + else + color = _roomPalette[color]; + } + if (x > x2) SWAP(x, x2); @@ -1872,6 +1892,16 @@ bool Gdi::drawStrip(byte *dstPtr, VirtScreen *vs, int x, int y, const int width, } assertRange(0, offset, smapLen-1, "screen strip"); + // Indy4 Amiga always uses the room or verb palette map to match colors to + // the currently setup palette, thus we need to select it over here too. + // Done like the original interpreter. + if (_vm->_game.platform == Common::kPlatformAmiga && _vm->_game.id == GID_INDY4) { + if (vs->number == kVerbVirtScreen) + _roomPalette = _vm->_verbPalette; + else + _roomPalette = _vm->_roomPalette; + } + return decompressBitmap(dstPtr, vs->pitch, smap_ptr + offset, height); } diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp index 30096000ce..930b287f00 100644 --- a/engines/scumm/palette.cpp +++ b/engines/scumm/palette.cpp @@ -372,6 +372,121 @@ void ScummEngine::setPaletteFromPtr(const byte *ptr, int numcolor) { 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; + } + + _amigaFirstUsedColor = 80; + for (; _amigaFirstUsedColor < 256; ++_amigaFirstUsedColor) { + if (ptr[_amigaFirstUsedColor * 3 + 0] <= 251 + || ptr[_amigaFirstUsedColor * 3 + 1] <= 251 + || ptr[_amigaFirstUsedColor * 3 + 2] <= 251) + break; + } + + 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; + if (idx != 17) { + _roomPalette[i] = idx; + _verbPalette[i] = idx + 32; + } else { + _roomPalette[i] = 0; + _verbPalette[i] = 32; + } + } + } + + setDirtyColors(0, 255); +} + +void ScummEngine::mapRoomPalette(int idx) { + 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(*pal++ - r); + int gD = ABS(*pal++ - g); + int bD = ABS(*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) { + 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(*pal++ - r); + int gD = ABS(*pal++ - g); + int bD = ABS(*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; @@ -419,11 +534,26 @@ void ScummEngine::initCycl(const byte *ptr) { 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) { @@ -432,11 +562,23 @@ void ScummEngine::stopCycle(int i) { assertRange(0, i, 16, "stopCycle: cycle"); if (i != 0) { _colorCycle[i - 1].delay = 0; + 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++) + 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; + } + } + } } /** @@ -512,14 +654,18 @@ void ScummEngine::cyclePalette() { setDirtyColors(cycl->start, cycl->end); moveMemInPalRes(cycl->start, cycl->end, cycl->flags & 2); - doCyclePalette(_currentPalette, cycl->start, cycl->end, 3, !(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++) + 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)); + } else { + doCycleIndirectPalette(_shadowPalette, cycl->start, cycl->end, !(cycl->flags & 2)); + } } } } @@ -733,62 +879,104 @@ void ScummEngine::setShadowPalette(int redScale, int greenScale, int blueScale, } void ScummEngine::darkenPalette(int redScale, int greenScale, int blueScale, int startColor, int endColor) { - int max; - if (_game.version >= 5 && _game.version <= 6 && _game.heversion <= 60) { - max = 252; - } else { - max = 255; - } + if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) { + startColor = CLIP(startColor, 0, 255); - if (startColor <= endColor) { - const byte *cptr; - const byte *palptr; - int color, idx, j; + //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) { + 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); + } - if (_game.heversion >= 90 || _game.version == 8) { - palptr = _darkenPalette; + setDirtyColors(startColor, endColor); + } else { + int max; + if (_game.version >= 5 && _game.version <= 6 && _game.heversion <= 60) { + max = 252; } else { - palptr = getPalettePtr(_curPalIndex, _roomResource); + max = 255; } - for (j = startColor; j <= endColor; j++) { - idx = (_game.heversion == 70) ? _HEV7ActorPalette[j] : j; - cptr = palptr + idx * 3; - if (_game.heversion == 70) - setDirtyColors(idx, idx); + if (startColor <= endColor) { + const byte *cptr; + const byte *palptr; + int color, idx, j; - // Original FOA Amiga version skips these colors - // Fixes bug #1206994: "FOA AMIGA: Black cursor and text in Dig Site" - if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) { - if (j < 16) { - cptr += 3; - continue; - } + if (_game.heversion >= 90 || _game.version == 8) { + palptr = _darkenPalette; + } else { + palptr = getPalettePtr(_curPalIndex, _roomResource); } - - 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]); + 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); } - if (_game.heversion != 70) - setDirtyColors(startColor, endColor); } } @@ -1007,6 +1195,37 @@ void ScummEngine::setPalColor(int idx, int r, int g, int b) { _darkenPalette[idx * 3 + 2] = b; } + if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) { + 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; + + if (idx - 16 == _verbPalette[i]) + mapVerbPalette(i); + } + } + } + if (_game.features & GF_16BIT_COLOR) _16BitPalette[idx] = get16BitColor(r, g, b); @@ -1026,6 +1245,8 @@ void ScummEngine::setCurrentPalette(int palindex) { towns_setPaletteFromPtr(pals); #endif #endif + } else if (_game.id == GID_INDY4 && _game.platform == Common::kPlatformAmiga) { + setAmigaPaletteFromPtr(pals); } else { setPaletteFromPtr(pals); } @@ -1081,42 +1302,64 @@ void ScummEngine::updatePalette() { if (_palDirtyMax == -1) return; - bool noir_mode = (_game.id == GID_SAMNMAX && readVar(0x8000)); - int first = _palDirtyMin; - int num = _palDirtyMax - first + 1; - int i; - byte palette_colors[3 * 256]; byte *p = palette_colors; + int first; + int num; - for (i = _palDirtyMin; i <= _palDirtyMax; i++) { - byte *data; + if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) { + // Indy4 Amiga has a special palette handling scheme + first = 0; + num = 64; - if (_game.features & GF_SMALL_HEADER && _game.version > 2) - data = _currentPalette + _shadowPalette[i] * 3; - else - data = _currentPalette + i * 3; + for (int i = 0; i < 64; ++i) { + byte *data; - // Sam & Max film noir mode. Convert the colors to grayscale - // before uploading them to the backend. + if (i < 32) + data = _amigaPalette + _shadowPalette[i] * 3; + else + data = _amigaPalette + i * 3; - if (noir_mode) { - int r, g, b; - byte brightness; + *p++ = data[0] * 255 / 15; + *p++ = data[1] * 255 / 15; + *p++ = data[2] * 255 / 15; + } + } else { + bool noir_mode = (_game.id == GID_SAMNMAX && readVar(0x8000)); + int i; - r = data[0]; - g = data[1]; - b = data[2]; + first = _palDirtyMin; + num = _palDirtyMax - first + 1; - brightness = (byte)((0.299 * r + 0.587 * g + 0.114 * b) + 0.5); + for (i = _palDirtyMin; i <= _palDirtyMax; i++) { + byte *data; - *p++ = brightness; - *p++ = brightness; - *p++ = brightness; - } else { - *p++ = data[0]; - *p++ = data[1]; - *p++ = data[2]; + 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]; + } } } @@ -1127,7 +1370,7 @@ void ScummEngine::updatePalette() { #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE if (_game.platform == Common::kPlatformFMTowns) { p = palette_colors; - for (i = first; i < first + num; ++i) { + for (int i = first; i < first + num; ++i) { _16BitPalette[i] = get16BitColor(p[0], p[1], p[2]); p += 3; } diff --git a/engines/scumm/room.cpp b/engines/scumm/room.cpp index 8962a0e971..63cbef8944 100644 --- a/engines/scumm/room.cpp +++ b/engines/scumm/room.cpp @@ -552,6 +552,10 @@ void ScummEngine::resetRoomSubBlocks() { } } + // We need to setup the current palette before initCycl for Indy4 Amiga. + if (_PALS_offs || _CLUT_offs) + setCurrentPalette(0); + // Color cycling // HE 7.0 games load resources but don't use them. if (_game.version >= 4 && _game.heversion <= 62) { @@ -570,9 +574,6 @@ void ScummEngine::resetRoomSubBlocks() { } } #endif - - if (_PALS_offs || _CLUT_offs) - setCurrentPalette(0); } diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index 870ec8cdf7..27fa7bc27b 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -1348,6 +1348,13 @@ void ScummEngine::saveOrLoad(Serializer *s) { memset(_colorUsedByCycle, 0, sizeof(_colorUsedByCycle)); } + // We need to restore the internal state of the Amiga palette for Indy4 + // Amiga. + // TODO: We should rather store the state in the savefile and only do this + // for old savegames. + if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) + setAmigaPaletteFromPtr(_currentPalette); + // // Save/load more global object state // diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 3b83019275..81f6af453c 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -290,6 +290,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) #endif _shadowPalette = NULL; _shadowPaletteSize = 0; + _verbPalette = NULL; memset(_currentPalette, 0, sizeof(_currentPalette)); memset(_darkenPalette, 0, sizeof(_darkenPalette)); memset(_HEV7ActorPalette, 0, sizeof(_HEV7ActorPalette)); @@ -610,6 +611,7 @@ ScummEngine::~ScummEngine() { _textSurface.free(); free(_shadowPalette); + free(_verbPalette); free(_palManipPalette); free(_palManipIntermediatePal); @@ -1408,6 +1410,10 @@ void ScummEngine::resetScumm() { _16BitPalette = (uint16 *)calloc(512, sizeof(uint16)); #endif + // Indy4 Amiga needs another palette map for the verb area. + if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4 && !_verbPalette) + _verbPalette = (uint8 *)calloc(256, 1); + #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE if (_game.platform == Common::kPlatformFMTowns) { delete _townsScreen; diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 04a175e732..8ffa893f33 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -970,6 +970,7 @@ protected: void setCurrentPalette(int pal); void setRoomPalette(int pal, int room); void setPCEPaletteFromPtr(const byte *ptr); + void setAmigaPaletteFromPtr(const byte *ptr); virtual void setPaletteFromPtr(const byte *ptr, int numcolor = -1); virtual void setPalColor(int index, int r, int g, int b); @@ -1065,6 +1066,9 @@ public: uint16 _hePaletteSlot; uint16 *_16BitPalette; + // Indy4 Amiga specific + byte *_verbPalette; + protected: int _shadowPaletteSize; byte _currentPalette[3 * 256]; @@ -1085,6 +1089,13 @@ protected: bool _enable_gs; bool _copyProtection; + uint16 _amigaFirstUsedColor; + byte _amigaPalette[3 * 64]; + void mapRoomPalette(int idx); + int remapRoomPaletteColor(int r, int g, int b); + void mapVerbPalette(int idx); + int remapVerbPaletteColor(int r, int g, int b); + public: uint16 _extraBoxFlags[65]; -- cgit v1.2.3 From 618d01c41c9e8507408b117dfa63750975fec68b Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Fri, 26 Aug 2011 06:34:59 +0200 Subject: SCUMM: Fix some range checks in Indy4 Amiga palette code. --- engines/scumm/palette.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp index 930b287f00..c00a117751 100644 --- a/engines/scumm/palette.cpp +++ b/engines/scumm/palette.cpp @@ -889,7 +889,7 @@ void ScummEngine::darkenPalette(int redScale, int greenScale, int blueScale, int const byte *palptr = getPalettePtr(_curPalIndex, _roomResource) + startColor * 3; for (int i = startColor; i <= endColor; ++i) { - if (i >= 16 && i <= 48) { + if (i >= 16 && i < 48) { if (cycleFlag) _colorUsedByCycle[i - 16] &= ~2; else @@ -916,7 +916,7 @@ void ScummEngine::darkenPalette(int redScale, int greenScale, int blueScale, int } for (int i = 0; i < 256; ++i) { - if (i >= 16 && i <= _amigaFirstUsedColor) + if (i >= 16 && i < _amigaFirstUsedColor) continue; bool inRange = (startColor <= i && i <= endColor); @@ -1196,7 +1196,7 @@ void ScummEngine::setPalColor(int idx, int r, int g, int b) { } if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) { - if (idx < 16 || idx > _amigaFirstUsedColor) { + if (idx < 16 || idx >= _amigaFirstUsedColor) { mapRoomPalette(idx); mapVerbPalette(idx); } else if (idx >= 16 && idx < 48 && idx != 33) { @@ -1205,7 +1205,7 @@ void ScummEngine::setPalColor(int idx, int r, int g, int b) { _amigaPalette[(idx - 16) * 3 + 2] = _currentPalette[idx * 3 + 2] >> 4; for (int i = 0; i < 256; ++i) { - if (i >= 16 && i <= _amigaFirstUsedColor) + if (i >= 16 && i < _amigaFirstUsedColor) continue; if (idx - 16 == _roomPalette[i]) @@ -1217,7 +1217,7 @@ void ScummEngine::setPalColor(int idx, int r, int g, int b) { _amigaPalette[(idx - 16) * 3 + 2] = _currentPalette[idx * 3 + 2] >> 4; for (int i = 0; i < 256; ++i) { - if (i >= 16 && i <= _amigaFirstUsedColor) + if (i >= 16 && i < _amigaFirstUsedColor) continue; if (idx - 16 == _verbPalette[i]) -- cgit v1.2.3 From cef09b345b73042a6459f6e7e21b3adc7ca875b2 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Fri, 26 Aug 2011 06:49:00 +0200 Subject: SCUMM: Fix bug in Indy4 Amiga's implementation of darkenPalette. --- engines/scumm/palette.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp index c00a117751..216708f098 100644 --- a/engines/scumm/palette.cpp +++ b/engines/scumm/palette.cpp @@ -884,7 +884,7 @@ void ScummEngine::darkenPalette(int redScale, int greenScale, int blueScale, int //bool remappedVerbColors = false; bool remappedRoomColors = false; - bool cycleFlag = (blueScale <= 250 && greenScale <= 250 && redScale <= 250); + bool cycleFlag = (blueScale >= 250 && greenScale >= 250 && redScale >= 250); const byte *palptr = getPalettePtr(_curPalIndex, _roomResource) + startColor * 3; @@ -923,7 +923,7 @@ void ScummEngine::darkenPalette(int redScale, int greenScale, int blueScale, int int idx = _roomPalette[i] + 16; bool mappedInRange = (startColor <= idx && idx <= endColor); - if (inRange == mappedInRange || (remappedRoomColors && cycleFlag)) + if (inRange != mappedInRange || (remappedRoomColors && cycleFlag)) mapRoomPalette(i); } -- cgit v1.2.3 From e791f904ed1e3090ca176f8b42e784602d18df08 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Fri, 26 Aug 2011 18:27:21 +0200 Subject: SCUMM: Fix Indy4 Amiga cursor. The original did not use the room nor verb palette map for the cursor, but seems to use a custom palette. I now just changed the cursor to use the colors from the DOS version of Indy4. This is rather guesswork, but the original did always show a flashing color in those colors instead of based on the screen colors. --- engines/scumm/cursor.cpp | 16 ++++++++++------ engines/scumm/palette.cpp | 10 ++++++++++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp index 1c79ac7a5a..36f06a4889 100644 --- a/engines/scumm/cursor.cpp +++ b/engines/scumm/cursor.cpp @@ -609,12 +609,16 @@ void ScummEngine_v5::setBuiltinCursor(int idx) { for (i = 0; i < 1024; i++) WRITE_UINT16(_grabbedCursor + i * 2, 0xFF); } else { - color = default_cursor_colors[idx]; - // Indy4 Amiga always uses the room or verb palette map to match colors to - // the currently setup palette, thus we need to select it over here too. - // This is guesswork! - if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) - color = _roomPalette[color]; + // Indy4 Amiga uses its own color set for the cursor image. + // This is patchwork code to make the cursor flash in correct colors. + if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) { + static const uint8 indy4AmigaColors[4] = { + 252, 252, 253, 254 + }; + color = indy4AmigaColors[idx]; + } else { + color = default_cursor_colors[idx]; + } memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor)); } diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp index 216708f098..75db90842d 100644 --- a/engines/scumm/palette.cpp +++ b/engines/scumm/palette.cpp @@ -1324,6 +1324,16 @@ void ScummEngine::updatePalette() { *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; -- cgit v1.2.3 From b4a17c702db5ff0af20ee72dca677c6eeccd4621 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Fri, 26 Aug 2011 18:35:13 +0200 Subject: SCUMM: Add some comments to our Indy4 Amiga verb palette handling. We handle the verb palette map a bit different, since we use one 64 color palette instead of two 32 color palettes for different screen areas. --- engines/scumm/palette.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp index 75db90842d..4d53b2ec74 100644 --- a/engines/scumm/palette.cpp +++ b/engines/scumm/palette.cpp @@ -400,6 +400,10 @@ void ScummEngine::setAmigaPaletteFromPtr(const byte *ptr) { 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; @@ -454,6 +458,10 @@ int ScummEngine::remapRoomPaletteColor(int r, int g, int b) { } 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. if (idx >= 48 && idx < 80 && idx != 65) _verbPalette[idx] = idx - 16; else @@ -1220,6 +1228,8 @@ void ScummEngine::setPalColor(int idx, int r, int g, int b) { 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); } -- cgit v1.2.3 From ffc09f86478a413ab92d49be327f7f84814ab268 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Fri, 26 Aug 2011 18:36:44 +0200 Subject: SCUMM: Mark some more ScummEngine members Indy4 Amiga specific. --- engines/scumm/scumm.h | 1 + 1 file changed, 1 insertion(+) diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 8ffa893f33..0af8264a58 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -1089,6 +1089,7 @@ protected: bool _enable_gs; bool _copyProtection; + // Indy4 Amiga specific uint16 _amigaFirstUsedColor; byte _amigaPalette[3 * 64]; void mapRoomPalette(int idx); -- cgit v1.2.3 From f77fc07b6b1e0137cf5ef46c8530ec8b696eefe3 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Fri, 26 Aug 2011 18:43:28 +0200 Subject: SCUMM: Save Indy4 Amiga specific palettes like the original did. For older saves I added a warning and a fallback which tries to setup everything correctly. This might cause some issues, but should hopefully be just fine. --- engines/scumm/saveload.cpp | 22 ++++++++++++++++------ engines/scumm/saveload.h | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index 27fa7bc27b..e0eba99cce 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -1322,6 +1322,9 @@ void ScummEngine::saveOrLoad(Serializer *s) { if (_shadowPaletteSize) { s->saveLoadArrayOf(_shadowPalette, _shadowPaletteSize, 1, sleByte); // _roomPalette didn't show up until V21 save games + // Note that we also save the room palette for Indy4 Amiga, since it + // is used as palette map there too, but we do so slightly a bit + // further down to group it with the other special palettes needed. if (s->getVersion() >= VER(21) && _game.version < 5) s->saveLoadArrayOf(_roomPalette, sizeof(_roomPalette), 1, sleByte); } @@ -1348,12 +1351,19 @@ void ScummEngine::saveOrLoad(Serializer *s) { memset(_colorUsedByCycle, 0, sizeof(_colorUsedByCycle)); } - // We need to restore the internal state of the Amiga palette for Indy4 - // Amiga. - // TODO: We should rather store the state in the savefile and only do this - // for old savegames. - if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) - setAmigaPaletteFromPtr(_currentPalette); + // Indy4 Amiga specific palette tables were not saved before V85 + if (_game.platform == Common::kPlatformAmiga && _game.id == GID_INDY4) { + if (s->getVersion() >= 85) { + s->saveLoadArrayOf(_roomPalette, 256, 1, sleByte); + s->saveLoadArrayOf(_verbPalette, 256, 1, sleByte); + s->saveLoadArrayOf(_amigaPalette, 3 * 64, 1, sleByte); + } else { + warning("Save with old Indiana Jones 4 Amiga palette handling detected"); + // We need to restore the internal state of the Amiga palette for Indy4 + // Amiga. This might lead to graphics glitches! + setAmigaPaletteFromPtr(_currentPalette); + } + } // // Save/load more global object state diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h index 776f40e12b..792a31d067 100644 --- a/engines/scumm/saveload.h +++ b/engines/scumm/saveload.h @@ -47,7 +47,7 @@ namespace Scumm { * only saves/loads those which are valid for the version of the savegame * which is being loaded/saved currently. */ -#define CURRENT_VER 84 +#define CURRENT_VER 85 /** * An auxillary macro, used to specify savegame versions. We use this instead -- cgit v1.2.3 From cd45d63e064c5f45e911256de75c3efef156337d Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sat, 27 Aug 2011 17:41:12 +0200 Subject: SCUMM: Added some comments about special colors in Indy4 Amiga. --- engines/scumm/palette.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp index 4d53b2ec74..9977436dc6 100644 --- a/engines/scumm/palette.cpp +++ b/engines/scumm/palette.cpp @@ -382,6 +382,9 @@ void ScummEngine::setAmigaPaletteFromPtr(const byte *ptr) { _amigaFirstUsedColor = 80; for (; _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 (ptr[_amigaFirstUsedColor * 3 + 0] <= 251 || ptr[_amigaFirstUsedColor * 3 + 1] <= 251 || ptr[_amigaFirstUsedColor * 3 + 2] <= 251) @@ -408,6 +411,10 @@ void ScummEngine::setAmigaPaletteFromPtr(const byte *ptr) { _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; } @@ -418,6 +425,8 @@ void ScummEngine::setAmigaPaletteFromPtr(const byte *ptr) { } 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 @@ -462,6 +471,8 @@ void ScummEngine::mapVerbPalette(int idx) { // 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 @@ -910,6 +921,8 @@ void ScummEngine::darkenPalette(int redScale, int greenScale, int blueScale, int } 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; @@ -1204,6 +1217,8 @@ void ScummEngine::setPalColor(int idx, int r, int g, int 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); -- cgit v1.2.3