206 lines
4.8 KiB
C#
206 lines
4.8 KiB
C#
#region Copyright & License Information
|
|
/*
|
|
* Copyright 2007-2015 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;
|
|
|
|
public FastByteReader(byte[] src, int offset = 0)
|
|
{
|
|
this.src = src;
|
|
this.offset = offset;
|
|
}
|
|
|
|
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, 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;
|
|
|
|
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 = 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++];
|
|
}
|
|
else
|
|
{
|
|
// case 3
|
|
var count = 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 Format80 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();
|
|
}
|
|
}
|
|
}
|
|
}
|