aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics/celobj32.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci/graphics/celobj32.cpp')
-rw-r--r--engines/sci/graphics/celobj32.cpp146
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;