diff options
Diffstat (limited to 'engines/sci/graphics/celobj32.cpp')
-rw-r--r-- | engines/sci/graphics/celobj32.cpp | 790 |
1 files changed, 487 insertions, 303 deletions
diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index f8bce26a2c..fe0fbf7cc4 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -21,22 +21,22 @@ */ #include "sci/resource.h" +#include "sci/engine/features.h" #include "sci/engine/seg_manager.h" #include "sci/engine/state.h" #include "sci/graphics/celobj32.h" #include "sci/graphics/frameout.h" #include "sci/graphics/palette32.h" -#include "sci/graphics/picture.h" -#include "sci/graphics/view.h" +#include "sci/graphics/remap32.h" +#include "sci/graphics/text32.h" +#include "sci/engine/workarounds.h" namespace Sci { #pragma mark CelScaler + 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; @@ -44,19 +44,17 @@ void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) { } } - int i = 1 - _activeIndex; + const int i = 1 - _activeIndex; _activeIndex = i; 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; } } @@ -64,7 +62,7 @@ void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) { void CelScaler::buildLookupTable(int *table, const Ratio &ratio, const int size) { int value = 0; int remainder = 0; - int num = ratio.getNumerator(); + const int num = ratio.getNumerator(); for (int i = 0; i < size; ++i) { *table++ = value; remainder += ratio.getDenominator(); @@ -82,12 +80,13 @@ const CelScalerTable *CelScaler::getScalerTable(const Ratio &scaleX, const Ratio #pragma mark - #pragma mark CelObj +bool CelObj::_drawBlackLines = false; void CelObj::init() { + CelObj::deinit(); + _drawBlackLines = false; _nextCacheId = 1; - delete _scaler; _scaler = new CelScaler(); - delete _cache; _cache = new CelCache; _cache->resize(100); } @@ -95,6 +94,11 @@ void CelObj::init() { void CelObj::deinit() { delete _scaler; _scaler = nullptr; + if (_cache != nullptr) { + for (CelCache::iterator it = _cache->begin(); it != _cache->end(); ++it) { + delete it->celObj; + } + } delete _cache; _cache = nullptr; } @@ -102,27 +106,45 @@ void CelObj::deinit() { #pragma mark - #pragma mark CelObj - Scalers -template <bool FLIP, typename READER> +template<bool FLIP, typename READER> struct SCALER_NoScale { +#ifndef NDEBUG + const byte *_rowEdge; +#endif const byte *_row; READER _reader; const int16 _lastIndex; + const int16 _sourceX; + const int16 _sourceY; - SCALER_NoScale(const CelObj &celObj, const int16 maxWidth) : - _reader(celObj, maxWidth), - _lastIndex(maxWidth - 1) {} + SCALER_NoScale(const CelObj &celObj, const int16 maxWidth, const Common::Point &scaledPosition) : + _row(nullptr), + _reader(celObj, FLIP ? celObj._width : maxWidth), + _lastIndex(celObj._width - 1), + _sourceX(scaledPosition.x), + _sourceY(scaledPosition.y) {} - inline void setSource(const int16 x, const int16 y) { - _row = _reader.getRow(y); + inline void setTarget(const int16 x, const int16 y) { + _row = _reader.getRow(y - _sourceY); if (FLIP) { - _row += _lastIndex - x; +#ifndef NDEBUG + _rowEdge = _row - 1; +#endif + _row += _lastIndex - (x - _sourceX); + assert(_row > _rowEdge); } else { - _row += x; +#ifndef NDEBUG + _rowEdge = _row + _lastIndex + 1; +#endif + _row += x - _sourceX; + assert(_row < _rowEdge); } } inline byte read() { + assert(_row != _rowEdge); + if (FLIP) { return *_row--; } else { @@ -133,69 +155,140 @@ struct SCALER_NoScale { template<bool FLIP, typename READER> struct SCALER_Scale { +#ifndef NDEBUG + int16 _minX; + int16 _maxX; +#endif const byte *_row; READER _reader; - const CelScalerTable *_table; int16 _x; - const uint16 _lastIndex; - - SCALER_Scale(const CelObj &celObj, const int16 maxWidth, const Ratio scaleX, const Ratio scaleY) : + 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 // wide as the source data it requires if downscaling, // so just always make the reader decompress an entire // line of source data when scaling - _reader(celObj, celObj._width), - _table(CelObj::_scaler->getScalerTable(scaleX, scaleY)), - _lastIndex(maxWidth - 1) {} + _reader(celObj, celObj._width) { + // In order for scaling ratios to apply equally across objects that + // start at different positions on the screen (like the cels of a + // picture), the pixels that are read from the source bitmap must all + // use the same pattern of division. In other words, cels must follow + // a global scaling pattern as if they were always drawn starting at an + // even multiple of the scaling ratio, even if they are not. + // + // To get the correct source pixel when reading out through the scaler, + // the engine creates a lookup table for each axis that translates + // directly from target positions to the indexes of source pixels using + // the global cadence for the given scaling ratio. + // + // Note, however, that not all games use the global scaling mode. + // + // SQ6 definitely uses the global scaling mode (an easy visual + // comparison is to leave Implants N' Stuff and then look at Roger); + // Torin definitely does not (scaling subtitle backgrounds will cause it + // to attempt a read out of bounds and crash). They are both SCI + // "2.1mid" games, so currently the common denominator looks to be that + // games which use global scaling are the ones that use low-resolution + // script coordinates too. + + const CelScalerTable *table = CelObj::_scaler->getScalerTable(scaleX, scaleY); + + if (g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth == kLowResX) { + const int16 unscaledX = (scaledPosition.x / scaleX).toInt(); + if (FLIP) { + const int lastIndex = celObj._width - 1; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = lastIndex - (table->valuesX[x] - unscaledX); + } + } else { + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = table->valuesX[x] - unscaledX; + } + } - inline void setSource(const int16 x, const int16 y) { - _row = _reader.getRow(_table->valuesY[y]); - if (FLIP) { - _x = _lastIndex - x; + const int16 unscaledY = (scaledPosition.y / scaleY).toInt(); + for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { + _valuesY[y] = table->valuesY[y] - unscaledY; + } } else { - _x = x; + if (FLIP) { + const int lastIndex = celObj._width - 1; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = lastIndex - table->valuesX[x - scaledPosition.x]; + } + } else { + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = table->valuesX[x - scaledPosition.x]; + } + } + + for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { + _valuesY[y] = table->valuesY[y - scaledPosition.y]; + } } } + inline void setTarget(const int16 x, const int16 y) { + _row = _reader.getRow(_valuesY[y]); + _x = x; + assert(_x >= _minX && _x <= _maxX); + } + inline byte read() { - if (FLIP) { - return _row[_table->valuesX[_x--]]; - } else { - return _row[_table->valuesX[_x++]]; - } + assert(_x >= _minX && _x <= _maxX); + return _row[_valuesX[_x++]]; } }; +template<bool FLIP, typename READER> +int16 SCALER_Scale<FLIP, READER>::_valuesX[kCelScalerTableSize]; +template<bool FLIP, typename READER> +int16 SCALER_Scale<FLIP, READER>::_valuesY[kCelScalerTableSize]; + #pragma mark - #pragma mark CelObj - Resource readers struct READER_Uncompressed { private: - byte *_pixels; +#ifndef NDEBUG + const int16 _sourceHeight; +#endif + const byte *_pixels; const int16 _sourceWidth; public: READER_Uncompressed(const CelObj &celObj, const int16) : +#ifndef NDEBUG + _sourceHeight(celObj._height), +#endif _sourceWidth(celObj._width) { - byte *resource = celObj.getResPointer(); + const byte *resource = celObj.getResPointer(); _pixels = resource + READ_SCI11ENDIAN_UINT32(resource + celObj._celHeaderOffset + 24); } inline const byte *getRow(const int16 y) const { + assert(y >= 0 && y < _sourceHeight); return _pixels + y * _sourceWidth; } }; struct READER_Compressed { private: - byte *_resource; - byte _buffer[1024]; + const byte *const _resource; + byte _buffer[kCelScalerTableSize]; uint32 _controlOffset; uint32 _dataOffset; uint32 _uncompressedDataOffset; int16 _y; const int16 _sourceHeight; - const uint8 _transparentColor; + const uint8 _skipColor; const int16 _maxWidth; public: @@ -203,27 +296,28 @@ public: _resource(celObj.getResPointer()), _y(-1), _sourceHeight(celObj._height), - _transparentColor(celObj._transparentColor), + _skipColor(celObj._skipColor), _maxWidth(maxWidth) { - assert(_maxWidth <= celObj._width); + assert(maxWidth <= celObj._width); - byte *celHeader = _resource + celObj._celHeaderOffset; + const byte *const celHeader = _resource + celObj._celHeaderOffset; _dataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 24); _uncompressedDataOffset = READ_SCI11ENDIAN_UINT32(celHeader + 28); _controlOffset = READ_SCI11ENDIAN_UINT32(celHeader + 32); } inline const byte *getRow(const int16 y) { + assert(y >= 0 && y < _sourceHeight); if (y != _y) { // compressed data segment for row - byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4); + const byte *row = _resource + _dataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + y * 4); // uncompressed data segment for row - byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4); + const byte *literal = _resource + _uncompressedDataOffset + READ_SCI11ENDIAN_UINT32(_resource + _controlOffset + _sourceHeight * 4 + y * 4); uint8 length; - for (int i = 0; i < _maxWidth; i += length) { - byte controlByte = *row++; + for (int16 i = 0; i < _maxWidth; i += length) { + const byte controlByte = *row++; length = controlByte; // Run-length encoded @@ -233,7 +327,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); @@ -256,6 +350,10 @@ public: #pragma mark - #pragma mark CelObj - Remappers +/** + * Pixel mapper for a CelObj with transparent pixels and no + * remapping data. + */ struct MAPPER_NoMD { inline void draw(byte *target, const byte pixel, const uint8 skipColor) const { if (pixel != skipColor) { @@ -263,162 +361,137 @@ struct MAPPER_NoMD { } } }; + +/** + * Pixel mapper for a CelObj with no transparent pixels and + * no remapping data. + */ struct MAPPER_NoMDNoSkip { inline void draw(byte *target, const byte pixel, const uint8) const { *target = pixel; } }; +/** + * Pixel mapper for a CelObj with transparent pixels, + * remapping data, and remapping enabled. + */ +struct MAPPER_Map { + inline void draw(byte *target, const byte pixel, const uint8 skipColor) const { + if (pixel != skipColor) { + // NOTE: For some reason, SSCI never checks if the source + // pixel is *above* the range of remaps. + if (pixel < g_sci->_gfxRemap32->getStartColor()) { + *target = pixel; + } else if (g_sci->_gfxRemap32->remapEnabled(pixel)) { + *target = g_sci->_gfxRemap32->remapColor(pixel, *target); + } + } + } +}; + +/** + * Pixel mapper for a CelObj with transparent pixels, + * remapping data, and remapping disabled. + */ +struct MAPPER_NoMap { + inline void draw(byte *target, const byte pixel, const uint8 skipColor) const { + // NOTE: For some reason, SSCI never checks if the source + // pixel is *above* the range of remaps. + if (pixel != skipColor && pixel < g_sci->_gfxRemap32->getStartColor()) { + *target = pixel; + } + } +}; + void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect) const { - const Buffer &priorityMap = g_sci->_gfxFrameout->getPriorityMap(); const Common::Point &scaledPosition = screenItem._scaledPosition; const Ratio &scaleX = screenItem._ratioX; const Ratio &scaleY = screenItem._ratioY; + _drawBlackLines = screenItem._drawBlackLines; if (_remap) { - if (g_sci->_gfxFrameout->_hasRemappedScreenItem) { - const uint8 priority = MAX((int16)0, MIN((int16)255, screenItem._priority)); - - // NOTE: In the original engine code, there was a second branch for - // _remap here that would then call the following functions if _remap was false: - // - // drawHzFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // drawNoFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // drawUncompHzFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // drawUncompNoFlip(Buffer &, Buffer &, Common::Rect &, Common::Point &, uint8) - // scaleDraw(Buffer &, Buffer &, Ratio &, Ratio &, Common::Rect &, Common::Point &, uint8) - // scaleDrawUncomp(Buffer &, Buffer &, Ratio &, Ratio &, Common::Rect &, Common::Point &, uint8) - // - // However, obviously, _remap cannot be false here. This dead code branch existed in - // at least SCI2/GK1 and SCI2.1/SQ6. - + // NOTE: In the original code this check was `g_Remap_numActiveRemaps && _remap`, + // but since we are already in a `_remap` branch, there is no reason to check it + // again + if (g_sci->_gfxRemap32->getRemapCount()) { if (scaleX.isOne() && scaleY.isOne()) { if (_compressionType == kCelCompressionNone) { if (_drawMirrored) { - drawUncompHzFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompHzFlipMap(target, targetRect, scaledPosition); } else { - drawUncompNoFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompNoFlipMap(target, targetRect, scaledPosition); } } else { if (_drawMirrored) { - drawHzFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawHzFlipMap(target, targetRect, scaledPosition); } else { - drawNoFlipMap(target, priorityMap, targetRect, scaledPosition, priority); + drawNoFlipMap(target, targetRect, scaledPosition); } } } else { if (_compressionType == kCelCompressionNone) { - scaleDrawUncompMap(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + scaleDrawUncompMap(target, scaleX, scaleY, targetRect, scaledPosition); } else { - scaleDrawMap(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + scaleDrawMap(target, scaleX, scaleY, targetRect, scaledPosition); } } } else { - // NOTE: In the original code this check was `g_Remap_numActiveRemaps && _remap`, - // but since we are already in a `_remap` branch, there is no reason to check it - // again - if (/* TODO: g_Remap_numActiveRemaps */ false) { - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { - if (_drawMirrored) { - drawUncompHzFlipMap(target, targetRect, scaledPosition); - } else { - drawUncompNoFlipMap(target, targetRect, scaledPosition); - } + if (scaleX.isOne() && scaleY.isOne()) { + if (_compressionType == kCelCompressionNone) { + if (_drawMirrored) { + drawUncompHzFlip(target, targetRect, scaledPosition); } else { - if (_drawMirrored) { - drawHzFlipMap(target, targetRect, scaledPosition); - } else { - drawNoFlipMap(target, targetRect, scaledPosition); - } + drawUncompNoFlip(target, targetRect, scaledPosition); } } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncompMap(target, scaleX, scaleY, targetRect, scaledPosition); + if (_drawMirrored) { + drawHzFlip(target, targetRect, scaledPosition); } else { - scaleDrawMap(target, scaleX, scaleY, targetRect, scaledPosition); + drawNoFlip(target, targetRect, scaledPosition); } } } else { - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { - if (_drawMirrored) { - drawUncompHzFlip(target, targetRect, scaledPosition); - } else { - drawUncompNoFlip(target, targetRect, scaledPosition); - } - } else { - if (_drawMirrored) { - drawHzFlip(target, targetRect, scaledPosition); - } else { - drawNoFlip(target, targetRect, scaledPosition); - } - } + if (_compressionType == kCelCompressionNone) { + scaleDrawUncomp(target, scaleX, scaleY, targetRect, scaledPosition); } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncomp(target, scaleX, scaleY, targetRect, scaledPosition); - } else { - scaleDraw(target, scaleX, scaleY, targetRect, scaledPosition); - } + scaleDraw(target, scaleX, scaleY, targetRect, scaledPosition); } } } } else { - if (g_sci->_gfxFrameout->_hasRemappedScreenItem) { - const uint8 priority = MAX((int16)0, MIN((int16)255, screenItem._priority)); - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { + if (scaleX.isOne() && scaleY.isOne()) { + if (_compressionType == kCelCompressionNone) { + if (_transparent) { if (_drawMirrored) { - drawUncompHzFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompHzFlipNoMD(target, targetRect, scaledPosition); } else { - drawUncompNoFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompNoFlipNoMD(target, targetRect, scaledPosition); } } else { if (_drawMirrored) { - drawHzFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompHzFlipNoMDNoSkip(target, targetRect, scaledPosition); } else { - drawNoFlipNoMD(target, priorityMap, targetRect, scaledPosition, priority); + drawUncompNoFlipNoMDNoSkip(target, targetRect, scaledPosition); } } } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncompNoMD(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + if (_drawMirrored) { + drawHzFlipNoMD(target, targetRect, scaledPosition); } else { - scaleDrawNoMD(target, priorityMap, scaleX, scaleY, targetRect, scaledPosition, priority); + drawNoFlipNoMD(target, targetRect, scaledPosition); } } } else { - if (scaleX.isOne() && scaleY.isOne()) { - if (_compressionType == kCelCompressionNone) { - if (_transparent) { - if (_drawMirrored) { - drawUncompHzFlipNoMD(target, targetRect, scaledPosition); - } else { - drawUncompNoFlipNoMD(target, targetRect, scaledPosition); - } - } else { - if (_drawMirrored) { - drawUncompHzFlipNoMDNoSkip(target, targetRect, scaledPosition); - } else { - drawUncompNoFlipNoMDNoSkip(target, targetRect, scaledPosition); - } - } - } else { - if (_drawMirrored) { - drawHzFlipNoMD(target, targetRect, scaledPosition); - } else { - drawNoFlipNoMD(target, targetRect, scaledPosition); - } - } + if (_compressionType == kCelCompressionNone) { + scaleDrawUncompNoMD(target, scaleX, scaleY, targetRect, scaledPosition); } else { - if (_compressionType == kCelCompressionNone) { - scaleDrawUncompNoMD(target, scaleX, scaleY, targetRect, scaledPosition); - } else { - scaleDrawNoMD(target, scaleX, scaleY, targetRect, scaledPosition); - } + scaleDrawNoMD(target, scaleX, scaleY, targetRect, scaledPosition); } } } + + _drawBlackLines = false; } void CelObj::draw(Buffer &target, const ScreenItem &screenItem, const Common::Rect &targetRect, bool mirrorX) { @@ -496,46 +569,42 @@ uint8 CelObj::readPixel(uint16 x, const uint16 y, bool mirrorX) const { void CelObj::submitPalette() const { if (_hunkPaletteOffset) { - Palette palette; - - byte *res = getResPointer(); - // NOTE: In SCI engine this uses HunkPalette::Init. - // TODO: Use a better size value - g_sci->_gfxPalette32->createFromData(res + _hunkPaletteOffset, 999999, &palette); + const HunkPalette palette(getResPointer() + _hunkPaletteOffset); g_sci->_gfxPalette32->submit(palette); } } #pragma mark - #pragma mark CelObj - Caching + int CelObj::_nextCacheId = 1; CelCache *CelObj::_cache = nullptr; -int CelObj::searchCache(const CelInfo32 &celInfo, int *nextInsertIndex) const { +int CelObj::searchCache(const CelInfo32 &celInfo, int *const nextInsertIndex) const { + *nextInsertIndex = -1; int oldestId = _nextCacheId + 1; - int oldestIndex = -1; + int oldestIndex = 0; for (int i = 0, len = _cache->size(); i < len; ++i) { CelCacheEntry &entry = (*_cache)[i]; - if (entry.celObj != nullptr) { - if (entry.celObj->_info == celInfo) { - entry.id = ++_nextCacheId; - return i; + if (entry.celObj == nullptr) { + if (*nextInsertIndex == -1) { + *nextInsertIndex = i; } - - if (oldestId > entry.id) { - oldestId = entry.id; - oldestIndex = i; - } - } else if (oldestIndex == -1) { + } else if (entry.celObj->_info == celInfo) { + entry.id = ++_nextCacheId; + return i; + } else if (oldestId > entry.id) { + oldestId = entry.id; oldestIndex = i; } } - // NOTE: Unlike the original SCI engine code, the out-param - // here is only updated if there was not a cache hit. - *nextInsertIndex = oldestIndex; + if (*nextInsertIndex == -1) { + *nextInsertIndex = oldestIndex; + } + return -1; } @@ -557,7 +626,7 @@ void CelObj::putCopyInCache(const int cacheIndex) const { #pragma mark - #pragma mark CelObj - Drawing -template <typename MAPPER, typename SCALER> +template<typename MAPPER, typename SCALER, bool DRAW_BLACK_LINES> struct RENDERER { MAPPER &_mapper; SCALER &_scaler; @@ -569,18 +638,21 @@ struct RENDERER { _skipColor(skipColor) {} inline void draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - const int16 sourceX = targetRect.left - scaledPosition.x; - const int16 sourceY = targetRect.top - scaledPosition.y; - byte *targetPixel = (byte *)target.getPixels() + target.screenWidth * targetRect.top + targetRect.left; const int16 skipStride = target.screenWidth - targetRect.width(); const int16 targetWidth = targetRect.width(); const int16 targetHeight = targetRect.height(); - for (int y = 0; y < targetHeight; ++y) { - _scaler.setSource(sourceX, sourceY + y); + for (int16 y = 0; y < targetHeight; ++y) { + if (DRAW_BLACK_LINES && (y % 2) == 0) { + memset(targetPixel, 0, targetWidth); + targetPixel += targetWidth + skipStride; + continue; + } + + _scaler.setTarget(targetRect.left, targetRect.top + y); - for (int x = 0; x < targetWidth; ++x) { + for (int16 x = 0; x < targetWidth; ++x) { _mapper.draw(targetPixel++, _scaler.read(), _skipColor); } @@ -589,75 +661,91 @@ struct RENDERER { } }; -template <typename MAPPER, typename SCALER> +template<typename MAPPER, typename SCALER> void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { MAPPER mapper; - SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width()); - RENDERER<MAPPER, SCALER> renderer(mapper, scaler, _transparentColor); + SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width(), scaledPosition); + RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _skipColor); renderer.draw(target, targetRect, scaledPosition); } -template <typename MAPPER, typename SCALER> +template<typename MAPPER, typename SCALER> void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio &scaleX, const Ratio &scaleY) const { MAPPER mapper; - SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width(), scaleX, scaleY); - RENDERER<MAPPER, SCALER> renderer(mapper, scaler, _transparentColor); - renderer.draw(target, targetRect, scaledPosition); -} - -void dummyFill(Buffer &target, const Common::Rect &targetRect) { - target.fillRect(targetRect, 250); + SCALER scaler(*this, targetRect, scaledPosition, scaleX, scaleY); + if (_drawBlackLines) { + RENDERER<MAPPER, SCALER, true> renderer(mapper, scaler, _skipColor); + renderer.draw(target, targetRect, scaledPosition); + } else { + RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _skipColor); + renderer.draw(target, targetRect, scaledPosition); + } } void CelObj::drawHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawHzFlip"); - dummyFill(target, targetRect); + render<MAPPER_NoMap, SCALER_NoScale<true, READER_Compressed> >(target, targetRect, scaledPosition); } + void CelObj::drawNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawNoFlip"); - dummyFill(target, targetRect); + render<MAPPER_NoMap, SCALER_NoScale<false, READER_Compressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompNoFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawUncompNoFlip"); - dummyFill(target, targetRect); + render<MAPPER_NoMap, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompHzFlip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawUncompHzFlip"); - dummyFill(target, targetRect); + render<MAPPER_NoMap, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::scaleDraw(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("scaleDraw"); - dummyFill(target, targetRect); + if (_drawMirrored) { + render<MAPPER_NoMap, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + } else { + render<MAPPER_NoMap, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + } } + void CelObj::scaleDrawUncomp(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("scaleDrawUncomp"); - dummyFill(target, targetRect); + if (_drawMirrored) { + render<MAPPER_NoMap, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + } else { + render<MAPPER_NoMap, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + } } + void CelObj::drawHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawHzFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<true, READER_Compressed> >(target, targetRect, scaledPosition); } + void CelObj::drawNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawNoFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<false, READER_Compressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompNoFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawUncompNoFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompHzFlipMap(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("drawUncompHzFlipMap"); - dummyFill(target, targetRect); + render<MAPPER_Map, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::scaleDrawMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("scaleDrawMap"); - dummyFill(target, targetRect); + if (_drawMirrored) { + render<MAPPER_Map, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + } else { + render<MAPPER_Map, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + } } + void CelObj::scaleDrawUncompMap(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - debug("scaleDrawUncompMap"); - dummyFill(target, targetRect); + if (_drawMirrored) { + render<MAPPER_Map, SCALER_Scale<true, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + } else { + render<MAPPER_Map, SCALER_Scale<false, READER_Uncompressed> >(target, targetRect, scaledPosition, scaleX, scaleY); + } } void CelObj::drawNoFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { @@ -675,19 +763,20 @@ void CelObj::drawUncompNoFlipNoMD(Buffer &target, const Common::Rect &targetRect void CelObj::drawUncompNoFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { render<MAPPER_NoMDNoSkip, SCALER_NoScale<false, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompHzFlipNoMD(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { render<MAPPER_NoMD, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition); } + void CelObj::drawUncompHzFlipNoMDNoSkip(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { render<MAPPER_NoMDNoSkip, SCALER_NoScale<true, READER_Uncompressed> >(target, targetRect, scaledPosition); } void CelObj::scaleDrawNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { - if (_drawMirrored) { + if (_drawMirrored) render<MAPPER_NoMD, SCALER_Scale<true, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); - } else { + else render<MAPPER_NoMD, SCALER_Scale<false, READER_Compressed> >(target, targetRect, scaledPosition, scaleX, scaleY); - } } void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition) const { @@ -698,22 +787,66 @@ void CelObj::scaleDrawUncompNoMD(Buffer &target, const Ratio &scaleX, const Rati } } -// TODO: These functions may all be vestigial. -void CelObj::drawHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompNoFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompHzFlipMap(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawUncompMap(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompNoFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::drawUncompHzFlipNoMD(Buffer &target, const Buffer &priorityMap, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} -void CelObj::scaleDrawUncompNoMD(Buffer &target, const Buffer &priorityMap, const Ratio &scaleX, const Ratio &scaleY, const Common::Rect &targetRect, const Common::Point &scaledPosition, const uint8 priority) const {} - #pragma mark - #pragma mark CelObjView + +int16 CelObjView::getNumLoops(const GuiResourceId viewId) { + const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false); + + if (!resource) { + return 0; + } + + assert(resource->size >= 3); + return resource->data[2]; +} + +int16 CelObjView::getNumCels(const GuiResourceId viewId, int16 loopNo) { + const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false); + + if (!resource) { + return 0; + } + + const byte *const data = resource->data; + + const uint16 loopCount = data[2]; + + // 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. For example, GK1 room 808 does this, and + // gets stuck in an infinite loop because the game script expects this + // method to return a non-zero value. + // This bug is triggered in basically every SCI32 game and appears to be + // universally fixable simply by always using the next lowest loop instead. + if (loopNo == loopCount) { + const SciCallOrigin origin = g_sci->getEngineState()->getCurrentCallOrigin(); + debugC(kDebugLevelWorkarounds, "Workaround: kNumCels loop %d -> loop %d in view %u, %s", loopNo, loopNo - 1, viewId, origin.toString().c_str()); + --loopNo; + } + + if (loopNo > loopCount || loopNo < 0) { + return 0; + } + + const uint16 viewHeaderSize = READ_SCI11ENDIAN_UINT16(data); + const uint8 loopHeaderSize = data[12]; + const uint8 viewHeaderFieldSize = 2; + +#ifndef NDEBUG + const byte *const dataMax = data + resource->size; +#endif + const byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * loopNo); + assert(loopHeader + 3 <= dataMax); + + if ((int8)loopHeader[0] != -1) { + loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * (int8)loopHeader[0]); + assert(loopHeader >= data && loopHeader + 3 <= dataMax); + } + + return loopHeader[2]; +} + CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int16 celNo) { _info.type = kCelTypeView; _info.resourceId = viewId; @@ -724,10 +857,14 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int _transparent = true; int cacheInsertIndex; - int cacheIndex = searchCache(_info, &cacheInsertIndex); + const int cacheIndex = searchCache(_info, &cacheInsertIndex); if (cacheIndex != -1) { CelCacheEntry &entry = (*_cache)[cacheIndex]; - *this = *dynamic_cast<CelObjView *>(entry.celObj); + const CelObjView *const cachedCelObj = dynamic_cast<CelObjView *>(entry.celObj); + if (cachedCelObj == nullptr) { + error("Expected a CelObjView in cache slot %d", cacheIndex); + } + *this = *cachedCelObj; entry.id = ++_nextCacheId; return; } @@ -736,34 +873,33 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int // generates view resource metadata for both SCI16 and SCI32 // implementations - Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false); + const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, viewId), false); // NOTE: SCI2.1/SQ6 just silently returns here. if (!resource) { - warning("View resource %d not loaded", viewId); - return; + error("View resource %d not found", viewId); } - byte *data = resource->data; + 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 = 320; - _scaledHeight = 200; + _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; } } - uint16 loopCount = data[2]; + const uint16 loopCount = data[2]; if (_info.loopNo >= loopCount) { _info.loopNo = loopCount - 1; } @@ -771,14 +907,14 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int // NOTE: This is the actual check, in the actual location, // from SCI engine. if (loopNo < 0) { - error("Loop is less than 0!"); + error("Loop is less than 0"); } const uint16 viewHeaderSize = READ_SCI11ENDIAN_UINT16(data); const uint8 loopHeaderSize = data[12]; const uint8 viewHeaderFieldSize = 2; - byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo); + const byte *loopHeader = data + viewHeaderFieldSize + viewHeaderSize + (loopHeaderSize * _info.loopNo); if ((int8)loopHeader[0] != -1) { if (loopHeader[1] == 1) { @@ -793,16 +929,32 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int _info.celNo = celCount - 1; } + // A celNo can be negative and still valid. At least PQ4CD uses this strange + // arrangement to load its high-resolution main menu resource. In PQ4CD, the + // low-resolution menu is at view 23, loop 9, cel 0, and the high-resolution + // menu is at view 2300, loop 0, cel 0. View 2300 is specially crafted to + // have 2 loops, with the second loop having 0 cels. When in high-resolution + // mode, the game scripts only change the view resource ID from 23 to 2300, + // leaving loop 9 and cel 0 the same. The code in CelObjView constructor + // auto-corrects loop 9 to loop 1, and then auto-corrects the cel number + // from 0 to -1, which effectively causes loop 0, cel 0 to be read. + if (_info.celNo < 0 && _info.loopNo == 0) { + error("Cel is less than 0 on loop 0"); + } + _hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 8); _celHeaderOffset = READ_SCI11ENDIAN_UINT32(loopHeader + 12) + (data[13] * _info.celNo); - byte *celHeader = data + _celHeaderOffset; + const byte *const celHeader = data + _celHeaderOffset; _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); + if (g_sci->_features->usesAlternateSelectors() && _mirrorX) { + _origin.x = _width - _origin.x - 1; + } + _origin.y = _height - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6) - 1; + _skipColor = celHeader[8]; _compressionType = (CelCompressionType)celHeader[9]; if (_compressionType != kCelCompressionNone && _compressionType != kCelCompressionRLE) { @@ -825,10 +977,14 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int } bool CelObjView::analyzeUncompressedForRemap() const { - byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24); + const byte *pixels = getResPointer() + READ_SCI11ENDIAN_UINT32(getResPointer() + _celHeaderOffset + 24); for (int i = 0; i < _width * _height; ++i) { - uint8 pixel = pixels[i]; - if (/* TODO: pixel >= Remap::minRemapColor && pixel <= Remap::maxRemapColor */ false && pixel != _transparentColor) { + const byte pixel = pixels[i]; + if ( + pixel >= g_sci->_gfxRemap32->getStartColor() && + pixel <= g_sci->_gfxRemap32->getEndColor() && + pixel != _skipColor + ) { return true; } } @@ -836,7 +992,20 @@ bool CelObjView::analyzeUncompressedForRemap() const { } bool CelObjView::analyzeForRemap() const { - // TODO: Implement decompression and analysis + READER_Compressed reader(*this, _width); + for (int y = 0; y < _height; y++) { + const byte *const curRow = reader.getRow(y); + for (int x = 0; x < _width; x++) { + const byte pixel = curRow[x]; + if ( + pixel >= g_sci->_gfxRemap32->getStartColor() && + pixel <= g_sci->_gfxRemap32->getEndColor() && + pixel != _skipColor + ) { + return true; + } + } + } return false; } @@ -850,11 +1019,16 @@ CelObjView *CelObjView::duplicate() const { } byte *CelObjView::getResPointer() const { - return g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false)->data; + Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _info.resourceId), false); + if (resource == nullptr) { + error("Failed to load view %d from resource manager", _info.resourceId); + } + return resource->data; } #pragma mark - #pragma mark CelObjPic + CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { _info.type = kCelTypePic; _info.resourceId = picId; @@ -866,23 +1040,26 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { _remap = false; int cacheInsertIndex; - int cacheIndex = searchCache(_info, &cacheInsertIndex); + const int cacheIndex = searchCache(_info, &cacheInsertIndex); if (cacheIndex != -1) { CelCacheEntry &entry = (*_cache)[cacheIndex]; - *this = *dynamic_cast<CelObjPic *>(entry.celObj); + const CelObjPic *const cachedCelObj = dynamic_cast<CelObjPic *>(entry.celObj); + if (cachedCelObj == nullptr) { + error("Expected a CelObjPic in cache slot %d", cacheIndex); + } + *this = *cachedCelObj; entry.id = ++_nextCacheId; return; } - Resource *resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, picId), false); + const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, picId), false); // NOTE: SCI2.1/SQ6 just silently returns here. if (!resource) { - warning("Pic resource %d not loaded", picId); - return; + error("Pic resource %d not found", picId); } - byte *data = resource->data; + const byte *const data = resource->data; _celCount = data[2]; @@ -893,39 +1070,39 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { _celHeaderOffset = READ_SCI11ENDIAN_UINT16(data) + (READ_SCI11ENDIAN_UINT16(data + 4) * _info.celNo); _hunkPaletteOffset = READ_SCI11ENDIAN_UINT32(data + 6); - byte *celHeader = data + _celHeaderOffset; + const byte *const celHeader = data + _celHeaderOffset; _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); _relativePosition.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 40); - uint16 sizeFlag1 = READ_SCI11ENDIAN_UINT16(data + 10); - uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12); + const uint16 sizeFlag1 = READ_SCI11ENDIAN_UINT16(data + 10); + const uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12); if (sizeFlag2) { - _scaledWidth = sizeFlag1; - _scaledHeight = sizeFlag2; + _xResolution = sizeFlag1; + _yResolution = sizeFlag2; } else if (sizeFlag1 == 0) { - _scaledWidth = 320; - _scaledHeight = 200; + _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) { // NOTE: This is correct according to SCI2.1/SQ6/DOS; // the engine re-reads the byte value as a word value - uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10); + const uint16 flags = READ_SCI11ENDIAN_UINT16(celHeader + 10); _transparent = flags & 1 ? true : false; _remap = flags & 2 ? true : false; } else { @@ -940,11 +1117,11 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { } bool CelObjPic::analyzeUncompressedForSkip() const { - byte *resource = getResPointer(); - byte *pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24); + const byte *const resource = getResPointer(); + 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; } } @@ -953,7 +1130,7 @@ bool CelObjPic::analyzeUncompressedForSkip() const { } void CelObjPic::draw(Buffer &target, const Common::Rect &targetRect, const Common::Point &scaledPosition, const bool mirrorX) { - Ratio square; + const Ratio square; _drawMirrored = mirrorX; drawTo(target, targetRect, scaledPosition, square, square); } @@ -963,33 +1140,39 @@ CelObjPic *CelObjPic::duplicate() const { } byte *CelObjPic::getResPointer() const { - return g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, _info.resourceId), false)->data; + const Resource *const resource = g_sci->getResMan()->findResource(ResourceId(kResourceTypePic, _info.resourceId), false); + if (resource == nullptr) { + error("Failed to load pic %d from resource manager", _info.resourceId); + } + return resource->data; } #pragma mark - #pragma mark CelObjMem -CelObjMem::CelObjMem(const reg_t bitmap) { + +CelObjMem::CelObjMem(const reg_t bitmapObject) { _info.type = kCelTypeMem; - _info.bitmap = bitmap; + _info.bitmap = bitmapObject; _mirrorX = false; _compressionType = kCelCompressionNone; _celHeaderOffset = 0; _transparent = true; - byte *bitmapData = g_sci->getEngineState()->_segMan->getHunkPointer(bitmap); - if (bitmapData == nullptr || READ_SCI11ENDIAN_UINT32(bitmapData + 28) != 46) { - error("Invalid Text bitmap %04x:%04x", PRINT_REG(bitmap)); + SciBitmap *bitmap = g_sci->getEngineState()->_segMan->lookupBitmap(bitmapObject); + + // NOTE: SSCI did no error checking here at all. + if (!bitmap) { + error("Bitmap %04x:%04x not found", PRINT_REG(bitmapObject)); } - _width = READ_SCI11ENDIAN_UINT16(bitmapData); - _height = READ_SCI11ENDIAN_UINT16(bitmapData + 2); - _displace.x = READ_SCI11ENDIAN_UINT16(bitmapData + 4); - _displace.y = READ_SCI11ENDIAN_UINT16(bitmapData + 6); - _transparentColor = bitmapData[8]; - _scaledWidth = READ_SCI11ENDIAN_UINT16(bitmapData + 36); - _scaledHeight = READ_SCI11ENDIAN_UINT16(bitmapData + 38); - _hunkPaletteOffset = READ_SCI11ENDIAN_UINT16(bitmapData + 20); - _remap = (READ_SCI11ENDIAN_UINT16(bitmapData + 10) & 2) ? true : false; + _width = bitmap->getWidth(); + _height = bitmap->getHeight(); + _origin = bitmap->getOrigin(); + _skipColor = bitmap->getSkipColor(); + _xResolution = bitmap->getXResolution(); + _yResolution = bitmap->getYResolution(); + _hunkPaletteOffset = bitmap->getHunkPaletteOffset(); + _remap = bitmap->getRemap(); } CelObjMem *CelObjMem::duplicate() const { @@ -997,18 +1180,19 @@ CelObjMem *CelObjMem::duplicate() const { } byte *CelObjMem::getResPointer() const { - return g_sci->getEngineState()->_segMan->getHunkPointer(_info.bitmap); + return g_sci->getEngineState()->_segMan->lookupBitmap(_info.bitmap)->getRawData(); } #pragma mark - #pragma mark CelObjColor + 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; @@ -1036,4 +1220,4 @@ CelObjColor *CelObjColor::duplicate() const { byte *CelObjColor::getResPointer() const { error("Unsupported method"); } -} +} // End of namespace Sci |