libdgf/LibDgf/Aqualead/AlLzDecoder.cs
2021-02-16 01:04:29 -07:00

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()
{
}
}
}