namespace Age.Engine.Sys4;
/// Eushully's 4 KiB-ring LZSS stream used by SYS4 catalogs and AGF sections.
public static class LzssDecoder
{
public static byte[] Decode(ReadOnlySpan source, int expectedSize, string name = "LZSS")
{
if (expectedSize < 0) throw new InvalidDataException($"{name}: negative expanded size");
var frame = new byte[0x1000];
var result = new byte[expectedSize];
int framePos = 0xfee, input = 0, output = 0;
while (output < expectedSize)
{
if (input >= source.Length) throw new InvalidDataException($"{name}: stream ended early");
int control = source[input++];
for (int bit = 1; bit <= 0x80 && output < expectedSize; bit <<= 1)
{
if ((control & bit) != 0)
{
if (input >= source.Length) throw new InvalidDataException($"{name}: truncated literal");
byte value = source[input++];
result[output++] = value;
frame[framePos] = value;
framePos = (framePos + 1) & 0xfff;
}
else
{
if (input > source.Length - 2) throw new InvalidDataException($"{name}: truncated back-reference");
int lo = source[input++], hi = source[input++];
int readPos = ((hi & 0xf0) << 4) | lo;
int length = 3 + (hi & 0x0f);
for (int j = 0; j < length && output < expectedSize; j++)
{
byte value = frame[readPos++ & 0xfff];
result[output++] = value;
frame[framePos] = value;
framePos = (framePos + 1) & 0xfff;
}
}
}
}
return result;
}
}