diff options
Diffstat (limited to 'engines/sci/graphics/celobj32.cpp')
-rw-r--r-- | engines/sci/graphics/celobj32.cpp | 146 |
1 files changed, 84 insertions, 62 deletions
diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index d67a4dc03c..430500ce1e 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -28,6 +28,7 @@ #include "sci/graphics/palette32.h" #include "sci/graphics/remap32.h" #include "sci/graphics/text32.h" +#include "sci/engine/workarounds.h" namespace Sci { #pragma mark CelScaler @@ -35,9 +36,6 @@ namespace Sci { CelScaler *CelObj::_scaler = nullptr; void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) { - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; - for (int i = 0; i < ARRAYSIZE(_scaleTables); ++i) { if (_scaleTables[i].scaleX == scaleX && _scaleTables[i].scaleY == scaleY) { _activeIndex = i; @@ -50,14 +48,12 @@ void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) { CelScalerTable &table = _scaleTables[i]; if (table.scaleX != scaleX) { - assert(screenWidth <= ARRAYSIZE(table.valuesX)); - buildLookupTable(table.valuesX, scaleX, screenWidth); + buildLookupTable(table.valuesX, scaleX, kCelScalerTableSize); table.scaleX = scaleX; } if (table.scaleY != scaleY) { - assert(screenHeight <= ARRAYSIZE(table.valuesY)); - buildLookupTable(table.valuesY, scaleY, screenHeight); + buildLookupTable(table.valuesY, scaleY, kCelScalerTableSize); table.scaleY = scaleY; } } @@ -159,17 +155,19 @@ struct SCALER_NoScale { template<bool FLIP, typename READER> struct SCALER_Scale { #ifndef NDEBUG + int16 _minX; int16 _maxX; #endif const byte *_row; READER _reader; int16 _x; - static int16 _valuesX[4096]; - static int16 _valuesY[4096]; + static int16 _valuesX[kCelScalerTableSize]; + static int16 _valuesY[kCelScalerTableSize]; SCALER_Scale(const CelObj &celObj, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio scaleX, const Ratio scaleY) : _row(nullptr), #ifndef NDEBUG + _minX(targetRect.left), _maxX(targetRect.right - 1), #endif // The maximum width of the scaled object may not be as @@ -221,17 +219,17 @@ struct SCALER_Scale { } else { if (FLIP) { const int lastIndex = celObj._width - 1; - for (int16 x = 0; x < targetRect.width(); ++x) { - _valuesX[targetRect.left + x] = lastIndex - table->valuesX[x]; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = lastIndex - table->valuesX[x - scaledPosition.x]; } } else { - for (int16 x = 0; x < targetRect.width(); ++x) { - _valuesX[targetRect.left + x] = table->valuesX[x]; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = table->valuesX[x - scaledPosition.x]; } } - for (int16 y = 0; y < targetRect.height(); ++y) { - _valuesY[targetRect.top + y] = table->valuesY[y]; + for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { + _valuesY[y] = table->valuesY[y - scaledPosition.y]; } } } @@ -239,19 +237,19 @@ struct SCALER_Scale { inline void setTarget(const int16 x, const int16 y) { _row = _reader.getRow(_valuesY[y]); _x = x; - assert(_x >= 0 && _x <= _maxX); + assert(_x >= _minX && _x <= _maxX); } inline byte read() { - assert(_x >= 0 && _x <= _maxX); + assert(_x >= _minX && _x <= _maxX); return _row[_valuesX[_x++]]; } }; template<bool FLIP, typename READER> -int16 SCALER_Scale<FLIP, READER>::_valuesX[4096]; +int16 SCALER_Scale<FLIP, READER>::_valuesX[kCelScalerTableSize]; template<bool FLIP, typename READER> -int16 SCALER_Scale<FLIP, READER>::_valuesY[4096]; +int16 SCALER_Scale<FLIP, READER>::_valuesY[kCelScalerTableSize]; #pragma mark - #pragma mark CelObj - Resource readers @@ -283,13 +281,13 @@ public: struct READER_Compressed { private: const byte *const _resource; - byte _buffer[4096]; + byte _buffer[kCelScalerTableSize]; uint32 _controlOffset; uint32 _dataOffset; uint32 _uncompressedDataOffset; int16 _y; const int16 _sourceHeight; - const uint8 _transparentColor; + const uint8 _skipColor; const int16 _maxWidth; public: @@ -297,7 +295,7 @@ public: _resource(celObj.getResPointer()), _y(-1), _sourceHeight(celObj._height), - _transparentColor(celObj._transparentColor), + _skipColor(celObj._skipColor), _maxWidth(maxWidth) { assert(maxWidth <= celObj._width); @@ -328,7 +326,7 @@ public: // Fill with skip color if (controlByte & 0x40) { - memset(_buffer + i, _transparentColor, length); + memset(_buffer + i, _skipColor, length); // Next value is fill color } else { memset(_buffer + i, *literal, length); @@ -570,7 +568,7 @@ uint8 CelObj::readPixel(uint16 x, const uint16 y, bool mirrorX) const { void CelObj::submitPalette() const { if (_hunkPaletteOffset) { - HunkPalette palette(getResPointer() + _hunkPaletteOffset); + const HunkPalette palette(getResPointer() + _hunkPaletteOffset); g_sci->_gfxPalette32->submit(palette); } } @@ -667,7 +665,7 @@ void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common MAPPER mapper; SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width(), scaledPosition); - RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _transparentColor); + RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _skipColor); renderer.draw(target, targetRect, scaledPosition); } @@ -677,10 +675,10 @@ void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common MAPPER mapper; SCALER scaler(*this, targetRect, scaledPosition, scaleX, scaleY); if (_drawBlackLines) { - RENDERER<MAPPER, SCALER, true> renderer(mapper, scaler, _transparentColor); + RENDERER<MAPPER, SCALER, true> renderer(mapper, scaler, _skipColor); renderer.draw(target, targetRect, scaledPosition); } else { - RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _transparentColor); + RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _skipColor); renderer.draw(target, targetRect, scaledPosition); } } @@ -812,7 +810,31 @@ int16 CelObjView::getNumCels(const GuiResourceId viewId, const int16 loopNo) { const byte *const data = resource->data; const uint16 loopCount = data[2]; - if (loopNo >= loopCount || loopNo < 0) { + + // Every version of SCI32 has a logic error in this function that causes + // random memory to be read if a script requests the cel count for one + // past the maximum loop index. At least GK1 room 800 does this, and gets + // stuck in an infinite loop because the game script expects this method + // to return a non-zero value. + // The scope of this bug means it is likely to pop up in other games, so we + // explicitly trap the bad condition here and report it so that any other + // game scripts relying on this broken behavior can be fixed as well + if (loopNo == loopCount) { + SciCallOrigin origin; + SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kNumCels_workarounds, &origin); + switch (solution.type) { + case WORKAROUND_NONE: + error("[CelObjView::getNumCels]: loop number %d is equal to loop count in view %u, %s", loopNo, viewId, origin.toString().c_str()); + case WORKAROUND_FAKE: + return (int16)solution.value; + case WORKAROUND_IGNORE: + return 0; + case WORKAROUND_STILLCALL: + break; + } + } + + if (loopNo > loopCount || loopNo < 0) { return 0; } @@ -869,20 +891,20 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int const byte *const data = resource->data; - _scaledWidth = READ_SCI11ENDIAN_UINT16(data + 14); - _scaledHeight = READ_SCI11ENDIAN_UINT16(data + 16); + _xResolution = READ_SCI11ENDIAN_UINT16(data + 14); + _yResolution = READ_SCI11ENDIAN_UINT16(data + 16); - if (_scaledWidth == 0 && _scaledHeight == 0) { + if (_xResolution == 0 && _yResolution == 0) { byte sizeFlag = data[5]; if (sizeFlag == 0) { - _scaledWidth = kLowResX; - _scaledHeight = kLowResY; + _xResolution = kLowResX; + _yResolution = kLowResY; } else if (sizeFlag == 1) { - _scaledWidth = 640; - _scaledHeight = 480; + _xResolution = 640; + _yResolution = 480; } else if (sizeFlag == 2) { - _scaledWidth = 640; - _scaledHeight = 400; + _xResolution = 640; + _yResolution = 400; } } @@ -936,9 +958,9 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int _width = READ_SCI11ENDIAN_UINT16(celHeader); _height = READ_SCI11ENDIAN_UINT16(celHeader + 2); - _displace.x = _width / 2 - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4); - _displace.y = _height - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6) - 1; - _transparentColor = celHeader[8]; + _origin.x = _width / 2 - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4); + _origin.y = _height - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6) - 1; + _skipColor = celHeader[8]; _compressionType = (CelCompressionType)celHeader[9]; if (_compressionType != kCelCompressionNone && _compressionType != kCelCompressionRLE) { @@ -967,7 +989,7 @@ bool CelObjView::analyzeUncompressedForRemap() const { if ( pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && - pixel != _transparentColor + pixel != _skipColor ) { return true; } @@ -984,7 +1006,7 @@ bool CelObjView::analyzeForRemap() const { if ( pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && - pixel != _transparentColor + pixel != _skipColor ) { return true; } @@ -1058,9 +1080,9 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { _width = READ_SCI11ENDIAN_UINT16(celHeader); _height = READ_SCI11ENDIAN_UINT16(celHeader + 2); - _displace.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4); - _displace.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6); - _transparentColor = celHeader[8]; + _origin.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4); + _origin.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6); + _skipColor = celHeader[8]; _compressionType = (CelCompressionType)celHeader[9]; _priority = READ_SCI11ENDIAN_UINT16(celHeader + 36); _relativePosition.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 38); @@ -1070,17 +1092,17 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { const uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12); if (sizeFlag2) { - _scaledWidth = sizeFlag1; - _scaledHeight = sizeFlag2; + _xResolution = sizeFlag1; + _yResolution = sizeFlag2; } else if (sizeFlag1 == 0) { - _scaledWidth = kLowResX; - _scaledHeight = kLowResY; + _xResolution = kLowResX; + _yResolution = kLowResY; } else if (sizeFlag1 == 1) { - _scaledWidth = 640; - _scaledHeight = 480; + _xResolution = 640; + _yResolution = 480; } else if (sizeFlag1 == 2) { - _scaledWidth = 640; - _scaledHeight = 400; + _xResolution = 640; + _yResolution = 400; } if (celHeader[10] & 128) { @@ -1105,7 +1127,7 @@ bool CelObjPic::analyzeUncompressedForSkip() const { const byte *const pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24); for (int i = 0; i < _width * _height; ++i) { uint8 pixel = pixels[i]; - if (pixel == _transparentColor) { + if (pixel == _skipColor) { return true; } } @@ -1151,10 +1173,10 @@ CelObjMem::CelObjMem(const reg_t bitmapObject) { _width = bitmap->getWidth(); _height = bitmap->getHeight(); - _displace = bitmap->getDisplace(); - _transparentColor = bitmap->getSkipColor(); - _scaledWidth = bitmap->getScaledWidth(); - _scaledHeight = bitmap->getScaledHeight(); + _origin = bitmap->getOrigin(); + _skipColor = bitmap->getSkipColor(); + _xResolution = bitmap->getXResolution(); + _yResolution = bitmap->getYResolution(); _hunkPaletteOffset = bitmap->getHunkPaletteOffset(); _remap = bitmap->getRemap(); } @@ -1173,10 +1195,10 @@ byte *CelObjMem::getResPointer() const { CelObjColor::CelObjColor(const uint8 color, const int16 width, const int16 height) { _info.type = kCelTypeColor; _info.color = color; - _displace.x = 0; - _displace.y = 0; - _scaledWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - _scaledHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + _origin.x = 0; + _origin.y = 0; + _xResolution = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + _yResolution = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; _hunkPaletteOffset = 0; _mirrorX = false; _remap = false; |