diff --git a/Source/Amiga/Graphics_Amiga.cpp b/Source/Amiga/Graphics_Amiga.cpp index 6bb66364..0b4f34f4 100644 --- a/Source/Amiga/Graphics_Amiga.cpp +++ b/Source/Amiga/Graphics_Amiga.cpp @@ -496,31 +496,56 @@ void cGraphics_Amiga::PaletteSet(cSurface* pTarget) { sImage cGraphics_Amiga::DecodeIFF(const std::string& pFilename) { sImage Result; auto File = g_Resource->fileGet(pFilename); - auto DataPtr = File->data(); + if (!File || File->empty()) + return Result; - if (!DataPtr || readBEDWord(DataPtr) != 'FORM') + const uint8* DataBegin = File->data(); + const uint8* DataPtr = DataBegin; + const uint8* DataEnd = DataBegin + File->size(); + auto ReadAvailable = [&](size_t pSize) -> bool { + return (DataEnd >= DataPtr) && (static_cast(DataEnd - DataPtr) >= pSize); + }; + auto SkipBytes = [&](size_t pSize) -> bool { + if (!ReadAvailable(pSize)) + return false; + DataPtr += pSize; + return true; + }; + + if (!ReadAvailable(12) || readBEDWord(DataPtr) != 'FORM') return Result; DataPtr += 4; - size_t FileSize = readBEDWord(DataPtr); + uint32 FormSize = readBEDWord(DataPtr); DataPtr += 4; if (readBEDWord(DataPtr) != 'ILBM') return Result; DataPtr += 4; - FileSize -= 4; + size_t FileSize = std::min(FormSize >= 4 ? FormSize - 4 : 0, static_cast(DataEnd - DataPtr)); uint32 Header = 0; uint32 Size = 0; + bool GotHeader = false; while (FileSize > 8) { + if (!ReadAvailable(8)) + return Result; + Header = readBEDWord(DataPtr); DataPtr += 4; FileSize -= 4; Size = readBEDWord(DataPtr); DataPtr += 4; FileSize -= 4; + size_t ChunkSize = Size; + size_t ChunkPaddedSize = ChunkSize + (ChunkSize & 1); + if (ChunkPaddedSize > FileSize || !ReadAvailable(ChunkPaddedSize)) + return Result; switch (Header) { case 'BMHD': + if (ChunkSize < 20) + return Result; + Result.mDimension.mWidth = readBEWord(DataPtr); DataPtr += 2; Result.mDimension.mHeight = readBEWord(DataPtr); DataPtr += 2; DataPtr += 2; DataPtr += 2; // X, Y @@ -529,68 +554,73 @@ sImage cGraphics_Amiga::DecodeIFF(const std::string& pFilename) { DataPtr++; // Compression DataPtr += 2; ++DataPtr; ++DataPtr; ++DataPtr; DataPtr += 2; DataPtr += 2; - FileSize -= Size; + if (ChunkSize > 20) + DataPtr += ChunkSize - 20; + if (ChunkSize & 1) + ++DataPtr; + FileSize -= ChunkPaddedSize; + GotHeader = true; break; case 'BODY': { - // + 2 is because the amiga does writing after the end of the image - Result.mData->resize(Result.mDimension.WidthByHeight() * (Result.mPlanes + 2)); - - int16 Width = Result.mDimension.mWidth + 0x0F; - Width >>= 4; - Width <<= 1; - - int16 Height = Result.mDimension.mHeight - 1; - - uint8* pDataDest = Result.mData->data(); - - for (int16 Y = Height; Y >= 0; --Y) { - uint8* DataDest = pDataDest; - - for (int8 Plane = 0; Plane < Result.mPlanes; ++Plane) { + if (!GotHeader || Result.mDimension.mWidth == 0 || Result.mDimension.mHeight == 0 || Result.mPlanes == 0) + return Result; - for (int16 X = Width; X > 0;) { - --FileSize; - int8 d0 = *DataPtr++; + // + 2 is because the amiga does writing after the end of the image + const size_t Width = ((static_cast(Result.mDimension.mWidth) + 0x0F) >> 4) << 1; + const size_t Height = Result.mDimension.mHeight; + const size_t Planes = Result.mPlanes; + const size_t DataSize = Width * Height * Planes; + if (Width == 0 || Height == 0 || Planes > 8 || Width > (SIZE_MAX / Height) || (Width * Height) > (SIZE_MAX / Planes)) + return Result; + Result.mData->assign(DataSize, 0); + + for (size_t Y = 0; Y < Height; ++Y) { + for (size_t Plane = 0; Plane < Planes; ++Plane) { + uint8* DataDest = Result.mData->data() + Plane * (Width * Height) + Y * Width; + size_t X = Width; + + while (X > 0) { + if (ChunkSize == 0 || !ReadAvailable(1)) + return Result; + int8 d0 = static_cast(*DataPtr++); + --ChunkSize; if (d0 < 0) { if (d0 == -128) continue; - int8 d1 = -d0; - - --FileSize; - d0 = *DataPtr++; + size_t Count = static_cast(-d0) + 1; + if (Count > X || ChunkSize == 0 || !ReadAvailable(1)) + return Result; + d0 = static_cast(*DataPtr++); + --ChunkSize; - do { - *DataDest++ = d0; - --X; - } while (d1-- > 0); + memset(DataDest, static_cast(d0), Count); + DataDest += Count; + X -= Count; continue; } else { - - do { - *DataDest++ = *DataPtr++; - --X; - --FileSize; - } while (d0-- > 0); - + size_t Count = static_cast(d0) + 1; + if (Count > X || ChunkSize < Count || !ReadAvailable(Count)) + return Result; + memcpy(DataDest, DataPtr, Count); + DataDest += Count; + DataPtr += Count; + ChunkSize -= Count; + X -= Count; } } - - // Move the destination back to start of row - DataDest -= Width; - - // Move it to the next plane - DataDest += (Width * Result.mDimension.mHeight); } - - // Next Row - pDataDest += Width; } + if (!SkipBytes(ChunkSize)) + return Result; + if (Size & 1) + ++DataPtr; + FileSize -= ChunkPaddedSize; break; } @@ -615,21 +645,23 @@ sImage cGraphics_Amiga::DecodeIFF(const std::string& pFilename) { d0 = (int16)d0; Final += d0; - Result.mPalette[i].mRed = ((Final >> 8) & 0xF) << 2; - Result.mPalette[i].mGreen = ((Final >> 4) & 0xF) << 2; - Result.mPalette[i].mBlue = ((Final >> 0) & 0xF) << 2; - - FileSize -= 3; + if (i < Result.mPalette.size()) { + Result.mPalette[i].mRed = ((Final >> 8) & 0xF) << 2; + Result.mPalette[i].mGreen = ((Final >> 4) & 0xF) << 2; + Result.mPalette[i].mBlue = ((Final >> 0) & 0xF) << 2; + } } + if (!SkipBytes(ChunkSize - (Size / 3) * 3)) + return Result; + if (Size & 1) + ++DataPtr; + FileSize -= ChunkPaddedSize; break; default: - if (Size & 1) - ++Size; - - DataPtr += Size; - FileSize -= Size; + DataPtr += ChunkPaddedSize; + FileSize -= ChunkPaddedSize; break; } }