diff --git a/OpenRA.Mods.Common/FileFormats/VqaReader.cs b/OpenRA.Mods.Common/FileFormats/VqaReader.cs index ab8e0db610..f1da15600a 100644 --- a/OpenRA.Mods.Common/FileFormats/VqaReader.cs +++ b/OpenRA.Mods.Common/FileFormats/VqaReader.cs @@ -330,7 +330,7 @@ namespace OpenRA.Mods.Common.FileFormats Array.Clear(cbf, 0, cbf.Length); Array.Clear(cbfBuffer, 0, cbfBuffer.Length); var decodeCount = 0; - decodeCount = LCWCompression.DecodeInto(fileBuffer, cbfBuffer, decodeMode ? 1 : 0, decodeMode); + decodeCount = LCWDecodeInto(fileBuffer, cbfBuffer, decodeMode ? 1 : 0, decodeMode); if ((videoFlags & 0x10) == 16) { var p = 0; @@ -366,7 +366,7 @@ namespace OpenRA.Mods.Common.FileFormats if (type == "CBP0") cbf = (byte[])cbp.Clone(); else - LCWCompression.DecodeInto(cbp, cbf); + LCWDecodeInto(cbp, cbf); chunkBufferOffset = currentChunkBuffer = 0; } @@ -391,7 +391,7 @@ namespace OpenRA.Mods.Common.FileFormats // Frame data case "VPTZ": - LCWCompression.DecodeInto(s.ReadBytes(subchunkLength), origData); + LCWDecodeInto(s.ReadBytes(subchunkLength), origData); // This is the last subchunk return; @@ -399,9 +399,9 @@ namespace OpenRA.Mods.Common.FileFormats Array.Clear(origData, 0, origData.Length); s.ReadBytes(fileBuffer, 0, subchunkLength); if (fileBuffer[0] != 0) - vtprSize = LCWCompression.DecodeInto(fileBuffer, origData); + vtprSize = LCWDecodeInto(fileBuffer, origData); else - LCWCompression.DecodeInto(fileBuffer, origData, 1, true); + LCWDecodeInto(fileBuffer, origData, 1, true); return; case "VPTR": Array.Clear(origData, 0, origData.Length); @@ -529,5 +529,75 @@ namespace OpenRA.Mods.Common.FileFormats } } } + + // TODO: Maybe replace this with LCWCompression.DecodeInto again later + public static int LCWDecodeInto(byte[] src, byte[] dest, int srcOffset = 0, bool reverse = false) + { + var ctx = new FastByteReader(src, srcOffset); + var destIndex = 0; + while (true) + { + var i = ctx.ReadByte(); + if ((i & 0x80) == 0) + { + // case 2 + var secondByte = ctx.ReadByte(); + var count = ((i & 0x70) >> 4) + 3; + var rpos = ((i & 0xf) << 8) + secondByte; + + if (destIndex + count > dest.Length) + return destIndex; + + // Replicate previous + var srcIndex = destIndex - rpos; + if (srcIndex > destIndex) + throw new NotImplementedException("srcIndex > destIndex {0} {1}".F(srcIndex, destIndex)); + + for (var j = 0; j < count; j++) + { + if (destIndex - srcIndex == 1) + dest[destIndex + j] = dest[destIndex - 1]; + else + dest[destIndex + j] = dest[srcIndex + j]; + } + + destIndex += count; + } + else if ((i & 0x40) == 0) + { + // case 1 + var count = i & 0x3F; + if (count == 0) + return destIndex; + + ctx.CopyTo(dest, destIndex, count); + destIndex += count; + } + else + { + var count3 = i & 0x3F; + if (count3 == 0x3E) + { + // case 4 + var count = ctx.ReadWord(); + var color = ctx.ReadByte(); + + for (var end = destIndex + count; destIndex < end; destIndex++) + dest[destIndex] = color; + } + else + { + // If count3 == 0x3F it's case 5, else case 3 + var count = count3 == 0x3F ? ctx.ReadWord() : count3 + 3; + var srcIndex = reverse ? destIndex - ctx.ReadWord() : ctx.ReadWord(); + if (srcIndex >= destIndex) + throw new NotImplementedException("srcIndex >= destIndex {0} {1}".F(srcIndex, destIndex)); + + for (var end = destIndex + count; destIndex < end; destIndex++) + dest[destIndex] = dest[srcIndex++]; + } + } + } + } } }