151 lines
3.5 KiB
C#
151 lines
3.5 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2013 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. For more information,
|
|
* see COPYING.
|
|
*/
|
|
#endregion
|
|
|
|
using System;
|
|
using System.IO;
|
|
|
|
namespace OpenRA.FileFormats
|
|
{
|
|
class FastByteReader
|
|
{
|
|
readonly byte[] src;
|
|
int offset = 0;
|
|
|
|
public FastByteReader(byte[] src)
|
|
{
|
|
this.src = src;
|
|
}
|
|
|
|
public bool Done() { return offset >= src.Length; }
|
|
public byte ReadByte() { return src[offset++]; }
|
|
public int ReadWord()
|
|
{
|
|
var x = ReadByte();
|
|
return x | (ReadByte() << 8);
|
|
}
|
|
|
|
public void CopyTo(byte[] dest, int offset, int count)
|
|
{
|
|
Array.Copy(src, this.offset, dest, offset, count);
|
|
this.offset += count;
|
|
}
|
|
|
|
public int Remaining() { return src.Length - offset; }
|
|
}
|
|
|
|
public static class Format80
|
|
{
|
|
static void ReplicatePrevious(byte[] dest, int destIndex, int srcIndex, int count)
|
|
{
|
|
if (srcIndex > destIndex)
|
|
throw new NotImplementedException("srcIndex > destIndex {0} {1}".F(srcIndex, destIndex));
|
|
|
|
if (destIndex - srcIndex == 1)
|
|
{
|
|
for (var i = 0; i < count; i++)
|
|
dest[destIndex + i] = dest[destIndex - 1];
|
|
}
|
|
else
|
|
{
|
|
for (var i = 0; i < count; i++)
|
|
dest[destIndex + i] = dest[srcIndex + i];
|
|
}
|
|
}
|
|
|
|
public static int DecodeInto(byte[] src, byte[] dest)
|
|
{
|
|
var ctx = new FastByteReader(src);
|
|
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;
|
|
|
|
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)
|
|
{
|
|
// case 5
|
|
var count = ctx.ReadWord();
|
|
var srcIndex = 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++];
|
|
}
|
|
else
|
|
{
|
|
// case 3
|
|
var count = count3 + 3;
|
|
var srcIndex = 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++];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static byte[] Encode(byte[] src)
|
|
{
|
|
/* quick & dirty format80 encoder -- only uses raw copy operator, terminated with a zero-run. */
|
|
/* this does not produce good compression, but it's valid format80 */
|
|
|
|
var ctx = new FastByteReader(src);
|
|
var ms = new MemoryStream();
|
|
|
|
do
|
|
{
|
|
var len = Math.Min(ctx.Remaining(), 0x3F);
|
|
ms.WriteByte((byte)(0x80 | len));
|
|
while (len-- > 0)
|
|
ms.WriteByte(ctx.ReadByte());
|
|
}
|
|
while (!ctx.Done());
|
|
|
|
ms.WriteByte(0x80); // terminator -- 0-length run.
|
|
|
|
return ms.ToArray();
|
|
}
|
|
}
|
|
}
|