Move LCWCompression support to Mods.Cnc

This commit is contained in:
reaperrr
2019-12-09 01:57:06 +01:00
committed by teinarss
parent c687600d66
commit d835090d0b
3 changed files with 4 additions and 1 deletions

View File

@@ -0,0 +1,167 @@
#region Copyright & License Information
/*
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.IO;
using OpenRA.Mods.Common.FileFormats;
namespace OpenRA.Mods.Cnc.FileFormats
{
// Lempel - Castle - Welch algorithm (aka Format80)
public static class LCWCompression
{
static void ReplicatePrevious(byte[] dest, int destIndex, int srcIndex, int count)
{
if (srcIndex > destIndex)
throw new NotImplementedException("srcIndex > destIndex {0} {1}".F(srcIndex, destIndex));
for (var i = 0; i < count; i++)
{
if (destIndex - srcIndex == 1)
dest[destIndex + i] = dest[destIndex - 1];
else
dest[destIndex + i] = dest[srcIndex + i];
}
}
public static int DecodeInto(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;
ReplicatePrevious(dest, destIndex, destIndex - rpos, count);
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++];
}
}
}
}
static int CountSame(byte[] src, int offset, int maxCount)
{
maxCount = Math.Min(src.Length - offset, maxCount);
if (maxCount <= 0)
return 0;
var first = src[offset++];
var count = 1;
while (count < maxCount && src[offset++] == first)
count++;
return count;
}
static void WriteCopyBlocks(byte[] src, int offset, int count, MemoryStream output)
{
while (count > 0)
{
var writeNow = Math.Min(count, 0x3F);
output.WriteByte((byte)(0x80 | writeNow));
output.Write(src, offset, writeNow);
count -= writeNow;
offset += writeNow;
}
}
// Quick and dirty LCW encoder version 2
// Uses raw copy and RLE compression
public static byte[] Encode(byte[] src)
{
using (var ms = new MemoryStream())
{
var offset = 0;
var left = src.Length;
var blockStart = 0;
while (offset < left)
{
var repeatCount = CountSame(src, offset, 0xFFFF);
if (repeatCount >= 4)
{
// Write what we haven't written up to now
WriteCopyBlocks(src, blockStart, offset - blockStart, ms);
// Command 4: Repeat byte n times
ms.WriteByte(0xFE);
// Low byte
ms.WriteByte((byte)(repeatCount & 0xFF));
// High byte
ms.WriteByte((byte)(repeatCount >> 8));
// Value to repeat
ms.WriteByte(src[offset]);
offset += repeatCount;
blockStart = offset;
}
else
offset++;
}
// Write what we haven't written up to now
WriteCopyBlocks(src, blockStart, offset - blockStart, ms);
// Write terminator
ms.WriteByte(0x80);
return ms.ToArray();
}
}
}
}

View File

@@ -12,6 +12,7 @@
using System;
using System.IO;
using OpenRA.Graphics;
using OpenRA.Mods.Cnc.FileFormats;
using OpenRA.Mods.Common.FileFormats;
using OpenRA.Primitives;

View File

@@ -14,6 +14,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using OpenRA.Mods.Cnc.FileFormats;
using OpenRA.Mods.Common.FileFormats;
using OpenRA.Mods.Common.UtilityCommands;
using OpenRA.Primitives;