From 89c8f002c0a1a4ce1058214c35300b9d553765ca Mon Sep 17 00:00:00 2001 From: elasota Date: Mon, 12 Jun 2023 20:03:24 -0400 Subject: [PATCH] MTROPOLIS: Better fix for corrupted mToon graphics in MTI --- engines/mtropolis/assets.cpp | 55 ++++++++++++++++++++++-------------- engines/mtropolis/assets.h | 2 +- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/engines/mtropolis/assets.cpp b/engines/mtropolis/assets.cpp index 0284cf58bf6..896c57bd2c1 100644 --- a/engines/mtropolis/assets.cpp +++ b/engines/mtropolis/assets.cpp @@ -208,7 +208,7 @@ void CachedMToon::decompressFrames(const Common::Array &data) { } template -bool CachedMToon::decompressMToonRLE(const RleFrame &frame, const Common::Array &coefsArray, Graphics::ManagedSurface &surface, bool isBottomUp, uint hackFlags) { +bool CachedMToon::decompressMToonRLE(const RleFrame &frame, const Common::Array &coefsArray, Graphics::ManagedSurface &surface, bool isBottomUp, bool isKeyFrame, uint hackFlags) { assert(sizeof(TNumber) == surface.format.bytesPerPixel); size_t size = coefsArray.size(); @@ -247,19 +247,13 @@ bool CachedMToon::decompressMToonRLE(const RleFrame &frame, const Common::Array< // Vertical skip uint32 skipAmount = transparentCountCode - TTransparentRowSkipMask; - // Not sure why this special code exists, but several mToons in MTI use it, such as the pants and shoe pull-outs - // in the treasure chest in the first area, and the Hispaniola TV in the MPZ-1000. - // - // The pants graphic depends on avoiding the x=0 here. - if (skipAmount != (TTransparentRowSkipMask - 2)) { - y += skipAmount; - x = 0; - if (y < h) { - rowData = static_cast(surface.getBasePtr(0, isBottomUp ? (h - 1 - y) : y)); - continue; - } else { - break; - } + y += skipAmount; + x = 0; + if (y < h) { + rowData = static_cast(surface.getBasePtr(0, isBottomUp ? (h - 1 - y) : y)); + continue; + } else { + break; } } else { // Horizontal skip @@ -278,7 +272,7 @@ bool CachedMToon::decompressMToonRLE(const RleFrame &frame, const Common::Array< size -= numLiterals; x += numLiterals; } else { - // Literals + // Run const size_t numCopies = rleCode; if (numCopies > remainingInRow || size == 0) return false; @@ -288,6 +282,21 @@ bool CachedMToon::decompressMToonRLE(const RleFrame &frame, const Common::Array< coefs++; size--; x += numCopies; + + if (size >= 2) { + // Handle some strange cases in MTI that appear to be caused by some kind of mToon + // encoder RLE flush problem: Numerous mToons have a 0-length RLE run after a max-length + // run. In most cases, the repeated value is 0, which has no effect, but in some cases + // this causes decode problems because the value is non-zero and gets decoded as a skip. + // + // In particular, it causes problems with the MPZ-1000 Hispaniola TV, the shoe and pants + // pull-outs in the chest in the first area, and the target animations in the Hispaniola + // cannon minigame. + if (numCopies == (TLiteralMask - 1) && coefs[0] == 0 && coefs[1] == repeatedValue) { + coefs += 2; + size -= 2; + } + } } if (x == w) { @@ -310,13 +319,15 @@ void CachedMToon::decompressRLEFrameToImage(size_t frameIndex, Graphics::Managed bool isBottomUp = (_metadata->imageFormat == MToonMetadata::kImageFormatWindows); + bool isKeyFrame = _metadata->frames[frameIndex].isKeyFrame; + bool decompressedOK = false; if (_rleOptimizedFormat.bytesPerPixel == 4) { - decompressedOK = decompressMToonRLE(_rleData[frameIndex], _rleData[frameIndex].data32, surface, isBottomUp, _hackFlags); + decompressedOK = decompressMToonRLE(_rleData[frameIndex], _rleData[frameIndex].data32, surface, isBottomUp, isKeyFrame, _hackFlags); } else if (_rleOptimizedFormat.bytesPerPixel == 2) { - decompressedOK = decompressMToonRLE(_rleData[frameIndex], _rleData[frameIndex].data16, surface, isBottomUp, _hackFlags); + decompressedOK = decompressMToonRLE(_rleData[frameIndex], _rleData[frameIndex].data16, surface, isBottomUp, isKeyFrame, _hackFlags); } else if (_rleOptimizedFormat.bytesPerPixel == 1) { - decompressedOK = decompressMToonRLE(_rleData[frameIndex], _rleData[frameIndex].data8, surface, isBottomUp, _hackFlags); + decompressedOK = decompressMToonRLE(_rleData[frameIndex], _rleData[frameIndex].data8, surface, isBottomUp, isKeyFrame, _hackFlags); } else error("Unknown mToon encoding"); @@ -652,12 +663,14 @@ void CachedMToon::getOrRenderFrame(uint32 prevFrame, uint32 targetFrame, Common: bool isBottomUp = (_metadata->imageFormat == MToonMetadata::kImageFormatWindows); for (size_t i = firstFrameToRender; i <= targetFrame; i++) { + bool isKeyFrame = _metadata->frames[i].isKeyFrame; + if (_rleOptimizedFormat.bytesPerPixel == 1) - decompressMToonRLE(_rleData[i], _rleData[i].data8, *surface, isBottomUp, _hackFlags); + decompressMToonRLE(_rleData[i], _rleData[i].data8, *surface, isBottomUp, isKeyFrame, _hackFlags); else if (_rleOptimizedFormat.bytesPerPixel == 2) - decompressMToonRLE(_rleData[i], _rleData[i].data16, *surface, isBottomUp, _hackFlags); + decompressMToonRLE(_rleData[i], _rleData[i].data16, *surface, isBottomUp, isKeyFrame, _hackFlags); else if (_rleOptimizedFormat.bytesPerPixel == 4) - decompressMToonRLE(_rleData[i], _rleData[i].data32, *surface, isBottomUp, _hackFlags); + decompressMToonRLE(_rleData[i], _rleData[i].data32, *surface, isBottomUp, isKeyFrame, _hackFlags); } } } diff --git a/engines/mtropolis/assets.h b/engines/mtropolis/assets.h index 1a4b7218acb..85e17331eb3 100644 --- a/engines/mtropolis/assets.h +++ b/engines/mtropolis/assets.h @@ -152,7 +152,7 @@ private: void rleReformat(RleFrame &frame, const Common::Array &srcData, const Graphics::PixelFormat &srcFormatRef, Common::Array &destData, const Graphics::PixelFormat &destFormatRef, uint hackFlags); template - static bool decompressMToonRLE(const RleFrame &frame, const Common::Array &coefsArray, Graphics::ManagedSurface &surface, bool isBottomUp, uint hackFlags); + static bool decompressMToonRLE(const RleFrame &frame, const Common::Array &coefsArray, Graphics::ManagedSurface &surface, bool isBottomUp, bool isKeyFrame, uint hackFlags); Common::Array _rleData; bool _isRLETemporalCompressed;