#include "CoreDefs.h"
#include "PLCTabReducer.h"
#include "QDPictDecoder.h"
#include "QDPictEmitContext.h"
#include "QDPictHeader.h"
#include "QDPictOpcodes.h"
#include "QDPictEmitScanlineParameters.h"
#include "IOStream.h"
#include "RGBAColor.h"
#include "Vec2i.h"

#include <vector>
#include <assert.h>

struct QDPictDecodeState
{
	PortabilityLayer::RGBAColor m_foreColor;
	PortabilityLayer::RGBAColor m_backColor;
	Point m_penPos;
	Point m_penSize;
	Point m_origin;
	uint16_t m_penMode;
	uint8_t m_penPattern[8];

	QDPictDecodeState()
	{
		m_foreColor = PortabilityLayer::RGBAColor::Create(0, 0, 0, 255);
		m_backColor = PortabilityLayer::RGBAColor::Create(255, 255, 255, 255);
		m_penPos = Point::Create(0, 0);
		m_origin = Point::Create(0, 0);
		m_penSize = Point::Create(1, 1);
		for (int i = 0; i < 8; i++)
			m_penPattern[i] = 0xff;
		m_penMode = 0;
	}
};

namespace PortabilityLayer
{
	QDPictDecoder::QDPictDecoder()
		: m_stream(nullptr)
	{
	}

	bool QDPictDecoder::DecodePict(IOStream *stream, QDPictEmitContext *emitContext)
	{
		QDPictHeader header;
		QDPictDecodeState decodeState;

		if (!header.Load(stream))
		{
			emitContext->ReportError(QDPictEmitContext::kMissingHeader, 0);
			return false;
		}

		emitContext->SpecifyFrame(header.GetFrame());

		const Rect constrainedFrame = emitContext->ConstrainRegion(header.GetFrame());
		Rect activeFrame = constrainedFrame;

		BERect scratchBERect;
		Rect scratchRect;
		BEUInt16_t scratchUInt16;
		BEUInt32_t scratchUInt32;

		const int pictVersion = header.GetVersion();
		if (pictVersion != 1 && pictVersion != 2)
			return false;

		const size_t opcodeSize = (pictVersion == 1) ? 1 : 2;

		uint8_t scratchBytes[64];

		for (;;)
		{
			if (stream->Read(scratchBytes, opcodeSize) != opcodeSize)
				return false;

			bool finished = false;

			uint16_t opcode = scratchBytes[0];
			if (header.GetVersion() == 2)
				opcode = (opcode << 8) | scratchBytes[1];

			int rasterOpErrorCode = 0;

			switch (opcode)
			{
			case QDOpcodes::kNoop:
				break;
			case QDOpcodes::kClipRegion:
				if (stream->Read(scratchBytes, 10) != 10 || scratchBytes[0] != 0 || scratchBytes[1] != 10)
				{
					emitContext->ReportError(QDPictEmitContext::kUnsupportedClipRegionFormat, 0);
					return false;
				}

				GP_STATIC_ASSERT(sizeof(scratchBERect) == 8);

				memcpy(&scratchBERect, scratchBytes + 2, 8);
				scratchRect = scratchBERect.ToRect();

				if (!scratchRect.IsValid())
				{
					emitContext->ReportError(QDPictEmitContext::kInvalidRegionRect, 0);
					return false;
				}

				break;
			case QDOpcodes::kShortComment:
				if (!stream->SeekCurrent(2))
				{
					emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode);
					return false;
				}
				break;
			case QDOpcodes::kLongComment:
				{
					if (stream->Read(scratchBytes, 4) != 4)
					{
						emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode);
						return false;
					}

					const uint16_t commentKind = (scratchBytes[0] << 8) | scratchBytes[1];
					const uint16_t commentSize = (scratchBytes[2] << 8) | scratchBytes[3];

					if (!stream->SeekCurrent(commentSize))
						return false;
				}
				break;
			case QDOpcodes::kBitsRect:
				rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), false, false, false, activeFrame, decodeState.m_origin, emitContext);
				if (rasterOpErrorCode)
				{
					emitContext->ReportError(QDPictEmitContext::kRasterOpFailure, rasterOpErrorCode);
					return false;
				}
				break;
			case QDOpcodes::kPackBitsRect:
				rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, false, false, activeFrame, decodeState.m_origin, emitContext);
				if (rasterOpErrorCode)
				{
					emitContext->ReportError(QDPictEmitContext::kRasterOpFailure, rasterOpErrorCode);
					return false;
				}
				break;
			case QDOpcodes::kPackBitsRgn:
				rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, true, false, activeFrame, decodeState.m_origin, emitContext);
				if (rasterOpErrorCode)
				{
					emitContext->ReportError(QDPictEmitContext::kRasterOpFailure, rasterOpErrorCode);
					return false;
				}
				break;
			case QDOpcodes::kDirectBitsRect:
				rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, false, true, activeFrame, decodeState.m_origin, emitContext);
				if (rasterOpErrorCode)
				{
					emitContext->ReportError(QDPictEmitContext::kRasterOpFailure, rasterOpErrorCode);
					return false;
				}
				break;
			case QDOpcodes::kDirectBitsRgn:
				rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, true, true, activeFrame, decodeState.m_origin, emitContext);
				if (rasterOpErrorCode)
				{
					emitContext->ReportError(QDPictEmitContext::kRasterOpFailure, rasterOpErrorCode);
					return false;
				}
				break;
			case QDOpcodes::kDefaultHilite:
				break;
				if (!stream->SeekCurrent(6))
				{
					emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode);
					return false;
				}
				break;
			case QDOpcodes::kEndOfPicture:
				finished = true;
				break;
			case QDOpcodes::kPenSize:
				{
					BEPoint point;

					if (!stream->Read(&point, 4))
					{
						emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode);
						return false;
					}

					decodeState.m_penSize = point.ToPoint();
				}
				break;
			case QDOpcodes::kPenPattern:
				{
					if (!stream->Read(decodeState.m_penPattern, 8))
					{
						emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode);
						return false;
					}
				}
				break;
			case QDOpcodes::kPenMode:
				{
					BEUInt16_t penMode;

					if (!stream->Read(&penMode, 2))
					{
						emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode);
						return false;
					}

					decodeState.m_penMode = penMode;
				}
				break;
			case QDOpcodes::kOrigin:
				{
					// NOTE: This is intentionally not the same order as Point
					BEInt16_t dxdy[2];

					if (!stream->Read(&dxdy, 4))
					{
						emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode);
						return false;
					}

					decodeState.m_origin.h += dxdy[0];
					decodeState.m_origin.v += dxdy[1];
				}
				break;
			case QDOpcodes::kOpColor:			// ??? Not sure what the difference between these two is
			case QDOpcodes::kRGBForeColor:
				{
					uint8_t rgbBytes[6];

					if (!stream->Read(rgbBytes, 6))
					{
						emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode);
						return false;
					}

					decodeState.m_foreColor = RGBAColor::Create(rgbBytes[0], rgbBytes[2], rgbBytes[4], 255);
				}
				break;
			case QDOpcodes::kRGBBackColor:
				{
					uint8_t rgbBytes[6];

					if (!stream->Read(rgbBytes, 6))
					{
						emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode);
						return false;
					}

					decodeState.m_backColor = RGBAColor::Create(rgbBytes[0], rgbBytes[2], rgbBytes[4], 255);
				}
				break;

			case QDOpcodes::kCompressedQT:
			case QDOpcodes::kUncompressedQT:
				{
					BEUInt32_t dataSize;

					if (!stream->Read(&dataSize, 4))
					{
						emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode);
						return false;
					}

					if (!emitContext->EmitQTContent(stream, dataSize, opcode == QDOpcodes::kCompressedQT))
					{
						emitContext->ReportError(QDPictEmitContext::kMalformedArguments, opcode);
						return false;
					}
				}
				break;

			default:
				// Unknown opcode
				emitContext->ReportError(QDPictEmitContext::kUnsupportedOpcode, opcode);
				return false;
			}

			if (finished)
				return true;
		}
	}

	int QDPictDecoder::ProcessRasterOp(IOStream *stream, int pictVersion, bool isPackedFlag, bool hasRegion, bool isDirect, const Rect &constraintRect, const Point &origin, QDPictEmitContext *context)
	{
		uint16_t rowSizeBytes = 0;

		bool isPixMap = false;
		if (isDirect)
		{
			isPixMap = true;
			// Skip base address
			if (!stream->SeekCurrent(4))
				return 1;

			if (stream->Read(&rowSizeBytes, sizeof(uint16_t)) != sizeof(uint16_t))
				return 2;

			ByteSwap::BigUInt16(rowSizeBytes);

			rowSizeBytes &= 0x7fff;
		}
		else
		{
			if (stream->Read(&rowSizeBytes, sizeof(uint16_t)) != sizeof(uint16_t))
				return 3;

			ByteSwap::BigUInt16(rowSizeBytes);

			if (pictVersion == 2)
			{
				if ((rowSizeBytes & 0x8000) != 0)
				{
					isPixMap = true;
					rowSizeBytes &= 0x7fff;
				}
				// ... else this is a bitmap
			}
		}

		Rect srcRect;
		Rect destRect;
		uint16_t transferMode;

		BEColorTableHeader clutHeader;
		BEColorTableItem clutItems[256];

		BEPixMap pixMapBE;

		int packType = 0;

		RGBAColor colors[256];
		size_t numColors = 0;

		if (!isPixMap)
		{
			BEBitMap bitMap;
			if (stream->Read(&bitMap, sizeof(BEBitMap)) != sizeof(BEBitMap))
				return 4;

			packType = (isPackedFlag && rowSizeBytes >= 8) ? 0 : 1;

			pixMapBE.m_bounds = bitMap.m_bounds;
			pixMapBE.m_version = 2;
			pixMapBE.m_packType = packType;
			pixMapBE.m_hRes = 72 << 16;
			pixMapBE.m_vRes = 72 << 16;
			pixMapBE.m_vRes = 72 << 16;
			pixMapBE.m_pixelType = 16;	// FIXME: Use direct instead
			pixMapBE.m_pixelSize = 1;
			pixMapBE.m_componentCount = 1;
			pixMapBE.m_componentSize = 1;
			pixMapBE.m_planeSizeBytes = 0;
			pixMapBE.m_clutHandle = 0;
			pixMapBE.m_unused = 0;

			BERect srcRectBE;
			if (stream->Read(&srcRectBE, sizeof(BERect)) != sizeof(BERect))
				return 5;

			BERect destRectBE;
			if (stream->Read(&destRectBE, sizeof(BERect)) != sizeof(BERect))
				return 6;

			srcRect = srcRectBE.ToRect();
			destRect = destRectBE.ToRect();

			if (!srcRect.IsValid())
				return 7;

			if (!destRect.IsValid())
				return 8;

			if (stream->Read(&transferMode, 2) != 2)
				return 9;

			ByteSwap::BigUInt16(transferMode);

			if (hasRegion)
			{
				BEUInt16_t regionSize;
				if (stream->Read(&regionSize, 2) != 2)
					return 10;

				if (regionSize < 2)
					return 11;

				if (!stream->SeekCurrent(regionSize - 2))
					return 12;
			}

			colors[0].r = colors[0].g = colors[0].b = 255;
			colors[0].a = 255;

			colors[1].r = colors[1].g = colors[1].b = 0;
			colors[1].a = 255;

			numColors = 0;
		}
		else
		{
			// If rowBytes < 8 or pack type == 1, data is unpacked
			// If pack type == 2, pad byte of 32-bit data is dropped (direct pixels only, 32-bit images)
			// If pack type == 3, 16-bit RLE
			// If pack type == 4, 8-bit planar component RLE

			GP_STATIC_ASSERT(sizeof(BEPixMap) == 44);

			if (stream->Read(&pixMapBE, sizeof(BEPixMap)) != sizeof(BEPixMap))
				return 13;

			if (isDirect)
			{
			}
			else
			{
				if (stream->Read(&clutHeader, sizeof(BEColorTableHeader)) != sizeof(BEColorTableHeader))
					return 14;

				const uint16_t numItemsMinusOne = clutHeader.m_numItemsMinusOne;
				if (numItemsMinusOne > 255)
					return 15;

				numColors = clutHeader.m_numItemsMinusOne + 1;

				if (stream->Read(clutItems, sizeof(BEColorTableItem) * numColors) != sizeof(BEColorTableItem) * numColors)
					return 16;

				for (size_t i = 0; i < numColors; i++)
					colors[i] = CTabReducer::DecodeClutItem(clutItems[i]);
			}

			BERect srcRectBE;
			if (stream->Read(&srcRectBE, sizeof(BERect)) != sizeof(BERect))
				return 17;

			BERect destRectBE;
			if (stream->Read(&destRectBE, sizeof(BERect)) != sizeof(BERect))
				return 18;

			srcRect = srcRectBE.ToRect();
			destRect = destRectBE.ToRect();

			if (!srcRect.IsValid() || !destRect.IsValid())
				return 19;

			if (stream->Read(&transferMode, 2) != 2)
				return 20;

			ByteSwap::BigUInt16(transferMode);

			if (!isPackedFlag && rowSizeBytes >= 8 && pixMapBE.m_packType != 1)
				return 21;

			if (hasRegion)
			{
				uint16_t regionSize;
				if (stream->Read(&regionSize, 2) != 2)
					return 22;

				ByteSwap::BigUInt16(regionSize);

				if (regionSize < 2)
					return 23;

				if (!stream->SeekCurrent(regionSize - 2))
					return 24;
			}

			const unsigned int pixelSize = pixMapBE.m_pixelSize;
			const unsigned int componentCount = pixMapBE.m_componentCount;
			const unsigned int componentSize = pixMapBE.m_componentSize;

			packType = pixMapBE.m_packType;
			if (isDirect)
			{
				if (packType == 0)
				{
					switch (pixelSize)
					{
					case 16:
						packType = 3;
						break;
					case 32:
						packType = 4;
						break;
					default:
						break;
					}
				}

				if (packType == 4)
				{
					if (pixMapBE.m_componentCount != 3)
						return 25;

					if (pixMapBE.m_componentSize != 8)
						return 26;

					if (pixelSize != 32)
						return 27;
				}
				else if (packType == 3)
				{
					if (componentCount != 3)
						return 28;
					if (componentSize != 5)
						return 29;

					if (pixelSize != 16)
						return 30;
				}
				else
				{
					switch (pixelSize)
					{
					case 32:
						if (componentCount != 3 || componentSize != 8)
							return 31;
						break;
					case 16:
						if (componentCount != 3 || componentSize != 5)
							return 32;
						break;
					case 8:
						if (componentCount != 1 || componentSize != 8)
							return 33;
						break;
					default:
						return 34;
					}
				}
			}
			else
			{
				switch (pixMapBE.m_componentSize)
				{
				case 1:
				case 2:
				case 4:
				case 8:
					break;
				default:
					return 35;
				}

				if (componentSize != pixelSize)
					return 36;
			}

			if (packType > 4)
				return 37;
		}

		const int componentSize = pixMapBE.m_componentSize;

		// We only support rect moves that are the same size
		if (srcRect.right - srcRect.left != destRect.right - destRect.left)
			return 38;

		if (srcRect.bottom - srcRect.top != destRect.bottom - destRect.top)
			return 39;

		// Offset by origin
		destRect.left -= origin.h;
		destRect.right -= origin.v;
		destRect.top -= origin.v;
		destRect.bottom -= origin.v;

		const Rect pixMapBounds = pixMapBE.m_bounds.ToRect();
		if (!pixMapBounds.IsValid())
			return 40;

		if (srcRect.left < pixMapBounds.left || srcRect.right > pixMapBounds.right || srcRect.top < pixMapBounds.top || srcRect.bottom > pixMapBounds.bottom)
			return 41;

		const Vec2i pixMapOriginRelativeToSrcRect = Vec2i(pixMapBounds.left, pixMapBounds.top) - Vec2i(srcRect.left, srcRect.top);
		const Vec2i pixMapOriginRelativeToDestRect = pixMapOriginRelativeToSrcRect;

		const Vec2i pixMapOrigin = pixMapOriginRelativeToDestRect + Vec2i(destRect.left, destRect.top);
		const Vec2i pixMapBottomRight = pixMapOrigin + Vec2i(pixMapBounds.right, pixMapBounds.bottom) - Vec2i(pixMapBounds.left, pixMapBounds.top);

		const Rect constrainedDestRect = constraintRect.Intersect(destRect).MakeValid();

		bool skipAll = false;
		if (!constrainedDestRect.IsValid())
			skipAll = true;

		if (rowSizeBytes < 8)
			packType = 1;

		if (!isDirect)
		{
			if (packType != 0 && packType != 1)
				return 42;
		}

		// NOT CURRENTLY SUPPORTED
		if (packType == 2)
			return 43;

		size_t decompressedRowSize = rowSizeBytes;

		const size_t minimumRowSize = (static_cast<size_t>(pixMapBounds.right - pixMapBounds.left) * static_cast<uint16_t>(pixMapBE.m_pixelSize) + 7) / 8;
		if (decompressedRowSize < minimumRowSize)
			return 56;

		if (packType == 4)
		{
			if ((rowSizeBytes & 3) != 0)
				return 55;

			// Fudge decompressed row size in planar RGB case
			decompressedRowSize = (rowSizeBytes / 4) * 3;
		}

		// Max compressed size is 4
		const size_t maxCompressedSize = decompressedRowSize + (decompressedRowSize / 128) + 4;

		uint8_t *decompressedScanlineBuffer = nullptr;
		uint8_t *compressedScanlineBuffer = nullptr;

		if (!context->AllocTempBuffers(decompressedScanlineBuffer, decompressedRowSize, compressedScanlineBuffer, maxCompressedSize))
			return 57;

		bool started = false;

		QDPictEmitScanlineParameters params;
		params.m_scanlineOriginX = pixMapOrigin.m_x;
		params.m_firstY = constrainedDestRect.top;
		params.m_constrainedRegionLeft = constrainedDestRect.left;
		params.m_constrainedRegionRight = constrainedDestRect.right;
		params.m_colors = colors;
		params.m_numColors = numColors;
		params.m_planarSeparation = pixMapBottomRight.m_x - pixMapOrigin.m_x;

		for (int32_t y = pixMapOrigin.m_y; y < pixMapBottomRight.m_y; y++)
		{
			bool isLineValid = !skipAll;
			if (y < constrainedDestRect.top || y >= constrainedDestRect.bottom)
				isLineValid = false;

			if (packType == 0 || packType > 2)
			{
				// RLE
				uint16_t lineByteCount;
				if (rowSizeBytes > 250)
				{
					if (stream->Read(&lineByteCount, 2) != 2)
						return 44;
					ByteSwap::BigUInt16(lineByteCount);
				}
				else
				{
					uint8_t lineByteCountSmall;
					if (stream->Read(&lineByteCountSmall, 1) != 1)
						return 45;
					lineByteCount = lineByteCountSmall;
				}

				if (!isLineValid)
				{
					if (!stream->SeekCurrent(lineByteCount))
						return 46;
					continue;
				}

				if (lineByteCount > maxCompressedSize)
					return 58;

				if (stream->Read(compressedScanlineBuffer, lineByteCount) != lineByteCount)
					return 47;

				if (packType == 3)
				{
					// 16-bit RLE
					if (!UnpackBits16(decompressedScanlineBuffer, decompressedRowSize, compressedScanlineBuffer, lineByteCount))
						return 48;

					if (!started)
					{
						context->Start(QDPictBlitSourceType_RGB15, params);
						started = true;
					}

					context->BlitScanlineAndAdvance(decompressedScanlineBuffer);
				}
				else
				{
					// 8-bit RLE
					if (!UnpackBits8(decompressedScanlineBuffer, decompressedRowSize, compressedScanlineBuffer, lineByteCount))
						return 49;

					if (!started)
					{
						if (packType == 0)
						{
							switch (componentSize)
							{
							case 1:
								context->Start(isPixMap ? QDPictBlitSourceType_Indexed1Bit : QDPictBlitSourceType_1Bit, params);
								break;
							case 2:
								context->Start(QDPictBlitSourceType_Indexed2Bit, params);
								break;
							case 4:
								context->Start(QDPictBlitSourceType_Indexed4Bit, params);
								break;
							case 8:
								context->Start(QDPictBlitSourceType_Indexed8Bit, params);
								break;
							default:
								return 50;	// ???
							}
						}
						else
						{
							assert(packType == 4);
							context->Start(QDPictBlitSourceType_RGB24_Multiplane, params);
						}
						started = true;
					}

					context->BlitScanlineAndAdvance(decompressedScanlineBuffer);
				}
			}
			else if (packType == 1)
			{
				if (!isLineValid)
				{
					if (!stream->SeekCurrent(rowSizeBytes))
						return 59;
					continue;
				}

				if (stream->Read(decompressedScanlineBuffer, rowSizeBytes) != rowSizeBytes)
					return 51;

				if (!started)
				{
					switch (componentSize)
					{
					case 1:
						context->Start(isPixMap ? QDPictBlitSourceType_Indexed1Bit : QDPictBlitSourceType_1Bit, params);
						break;
					case 2:
						context->Start(QDPictBlitSourceType_Indexed2Bit, params);
						break;
					case 4:
						context->Start(QDPictBlitSourceType_Indexed4Bit, params);
						break;
					case 8:
						context->Start(QDPictBlitSourceType_Indexed8Bit, params);
						break;
					default:
						return 52;	// ???
					}

					started = true;
				}

				context->BlitScanlineAndAdvance(decompressedScanlineBuffer);
			}
			else
				return 53;
		}

		// Either undocumented behavior or non-compliant PICT resources, not sure
		if (isPixMap && (stream->Tell() & 1) != 0)
		{
			if (!stream->SeekCurrent(1))
				return 54;
		}

		return 0;
	}

	bool QDPictDecoder::UnpackBits8(uint8_t *dest, size_t destSize, const uint8_t *src, size_t srcSize)
	{
		while (srcSize > 0)
		{
			int8_t headerByte = *reinterpret_cast<const int8_t*>(src);
			src++;
			srcSize--;

			if (headerByte >= 0)
			{
				const size_t litCount = headerByte + 1;
				if (destSize < litCount || srcSize < litCount)
					return false;

				memcpy(dest, src, litCount);

				src += litCount;
				srcSize -= litCount;
				dest += litCount;
				destSize -= litCount;
			}
			else
			{
				const size_t repCount = static_cast<size_t>(1 - headerByte);
				if (srcSize < 1)
					return false;

				if (destSize < repCount)
					return false;

				const uint8_t repValue = *src;

				memset(dest, repValue, repCount);

				src++;
				srcSize--;
				dest += repCount;
				destSize -= repCount;
			}
		}

		return (destSize == 0);
	}

	bool QDPictDecoder::UnpackBits16(uint8_t *dest8, size_t destSize, const uint8_t *src, size_t srcSize)
	{
		uint16_t *dest16 = reinterpret_cast<uint16_t*>(dest8);
		destSize /= 2;

		while (srcSize > 0)
		{
			int8_t headerByte = *reinterpret_cast<const int8_t*>(src);
			src++;
			srcSize--;

			if (headerByte >= 0)
			{
				const size_t litCount = headerByte + 1;
				if (destSize < litCount || srcSize < litCount * 2)
					return false;

				memcpy(dest16, src, 2 * litCount);
				for (size_t i = 0; i < litCount; i++)
					ByteSwap::BigUInt16(dest16[i]);

				src += litCount * 2;
				srcSize -= litCount * 2;
				dest16 += litCount;
				destSize -= litCount;
			}
			else
			{
				const size_t repCount = static_cast<size_t>(1 - headerByte);
				if (srcSize < 2)
					return false;

				if (destSize < repCount)
					return false;

				const uint16_t repValue = (src[0] << 8) | src[1];

				for (size_t i = 0; i < repCount; i++)
					dest16[i] = repValue;

				src += 2;
				srcSize -= 2;
				dest16 += repCount;
				destSize -= repCount;
			}
		}

		return (destSize == 0);
	}
}
