114 lines
3.6 KiB
C#
114 lines
3.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
|
|
namespace LibDgf.Aqualead
|
|
{
|
|
public class AlLzDecoder
|
|
{
|
|
uint bitBuffer;
|
|
int numBitsRemaining;
|
|
BinaryReader br;
|
|
|
|
public byte[] Decode(Stream inStream)
|
|
{
|
|
br = new BinaryReader(inStream);
|
|
numBitsRemaining = 0;
|
|
|
|
if (new string(br.ReadChars(4)) != "ALLZ")
|
|
throw new InvalidDataException("Not an Aqualead LZ compressed file.");
|
|
byte version = br.ReadByte();
|
|
if (version > 1)
|
|
throw new InvalidDataException("Version too new.");
|
|
|
|
byte minLookbackLengthBits = br.ReadByte();
|
|
byte minLookbackPosBits = br.ReadByte();
|
|
byte minLiteralLengthBits = br.ReadByte();
|
|
uint decompressedLength = br.ReadUInt32();
|
|
|
|
byte[] output = new byte[decompressedLength];
|
|
int outPos = 0;
|
|
|
|
if (version == 0) ReadBits(1); // Consume literal bit if v0
|
|
bool hasLiteral = true;
|
|
|
|
while (true)
|
|
{
|
|
if (hasLiteral)
|
|
{
|
|
if (outPos >= decompressedLength) break;
|
|
int literalLength = (int)ReadEncodedNum(minLiteralLengthBits) + 1;
|
|
if (inStream.Read(output, outPos, literalLength) != literalLength)
|
|
throw new IOException("Could not read all bytes requested.");
|
|
outPos += literalLength;
|
|
//Console.WriteLine("Did literal");
|
|
}
|
|
|
|
if (outPos >= decompressedLength) break;
|
|
int lookbackPos = (int)ReadEncodedNum(minLookbackPosBits) + 1;
|
|
int lookbackLength = (int)ReadEncodedNum(minLookbackLengthBits) + 3;
|
|
|
|
for (int i = 0; i < lookbackLength; ++i)
|
|
{
|
|
output[outPos] = output[outPos - lookbackPos];
|
|
++outPos;
|
|
}
|
|
//Console.WriteLine("Did lookback");
|
|
|
|
if (outPos >= decompressedLength) break;
|
|
hasLiteral = ReadBits(1) == 0;
|
|
//Console.WriteLine("Has literal: " + hasLiteral);
|
|
}
|
|
|
|
br = null;
|
|
return output;
|
|
}
|
|
|
|
uint ReadEncodedNum(int minNumBits)
|
|
{
|
|
int numExtBits = CountBits();
|
|
uint num = ReadBits(minNumBits);
|
|
if (numExtBits > 0)
|
|
{
|
|
// Length encoding
|
|
// e e e e 0 | xxxx | yy
|
|
// Number of es indicate exponent and number of bits for mantissa
|
|
// Subsequently, ((2 ^ count(e)) - 1 + xxxx) | yy
|
|
num |= (uint)(ReadBits(numExtBits) + (1 << numExtBits) - 1) << minNumBits;
|
|
}
|
|
return num;
|
|
}
|
|
|
|
void EnsureBits(int numBits)
|
|
{
|
|
while (numBitsRemaining < numBits)
|
|
{
|
|
if (numBitsRemaining + 8 > 32) throw new ArgumentOutOfRangeException(nameof(numBits));
|
|
bitBuffer |= (uint)br.ReadByte() << numBitsRemaining;
|
|
numBitsRemaining += 8;
|
|
}
|
|
}
|
|
|
|
uint ReadBits(int numBits)
|
|
{
|
|
EnsureBits(numBits);
|
|
uint output = (uint)(bitBuffer & ((1 << numBits) - 1));
|
|
bitBuffer >>= numBits;
|
|
numBitsRemaining -= numBits;
|
|
return output;
|
|
}
|
|
|
|
int CountBits()
|
|
{
|
|
int i = 0;
|
|
while (ReadBits(1) != 0) ++i;
|
|
return i;
|
|
}
|
|
|
|
void WriteBits()
|
|
{
|
|
|
|
}
|
|
}
|
|
}
|