Mother2GbaTranslation/tools/RenderStaffCredits/LZ77.cs

168 lines
5.6 KiB
C#

using System.Collections.Generic;
namespace GBA
{
class LZ77
{
public static int Decompress(byte[] data, int address, out byte[] output)
{
output = null;
int start = address;
if (data[address++] != 0x10) return -1; // Check for LZ77 signature
// Read the block length
int length = data[address++];
length += (data[address++] << 8);
length += (data[address++] << 16);
output = new byte[length];
int bPos = 0;
while (bPos < length)
{
byte ch = data[address++];
for (int i = 0; i < 8; i++)
{
switch ((ch >> (7 - i)) & 1)
{
case 0:
// Direct copy
if (bPos >= length) break;
output[bPos++] = data[address++];
break;
case 1:
// Compression magic
int t = (data[address++] << 8);
t += data[address++];
int n = ((t >> 12) & 0xF) + 3; // Number of bytes to copy
int o = (t & 0xFFF);
// Copy n bytes from bPos-o to the output
for (int j = 0; j < n; j++)
{
if (bPos >= length) break;
output[bPos] = output[bPos - o - 1];
bPos++;
}
break;
default:
break;
}
}
}
return address - start;
}
public static byte[] Compress(byte[] data)
{
return Compress(data, 0, data.Length);
}
public static byte[] Compress(byte[] data, int address, int length)
{
int start = address;
List<byte> obuf = new List<byte>();
List<byte> tbuf = new List<byte>();
int control = 0;
// Let's start by encoding the signature and the length
obuf.Add(0x10);
obuf.Add((byte)(length & 0xFF));
obuf.Add((byte)((length >> 8) & 0xFF));
obuf.Add((byte)((length >> 16) & 0xFF));
while ((address - start) < length)
{
tbuf.Clear();
control = 0;
for (int i = 0; i < 8; i++)
{
bool found = false;
// First byte should be raw
if (address == start)
{
tbuf.Add(data[address++]);
found = true;
}
else if ((address - start) >= length)
{
break;
}
else
{
// We're looking for the longest possible string
// The farthest possible distance from the current address is 0x1000
int max_length = -1;
int max_distance = -1;
for (int k = 1; k <= 0x1000; k++)
{
if ((address - k) < start) break;
int l = 0;
for (; l < 18; l++)
{
if (((address - start + l) >= length) ||
(data[address - k + l] != data[address + l]))
{
if (l > max_length)
{
max_length = l;
max_distance = k;
}
break;
}
}
// Corner case: we matched all 18 bytes. This is
// the maximum length, so don't bother continuing
if (l == 18)
{
max_length = 18;
max_distance = k;
break;
}
}
if (max_length >= 3)
{
address += max_length;
// We hit a match, so add it to the output
int t = (max_distance - 1) & 0xFFF;
t |= (((max_length - 3) & 0xF) << 12);
tbuf.Add((byte)((t >> 8) & 0xFF));
tbuf.Add((byte)(t & 0xFF));
// Set the control bit
control |= (1 << (7 - i));
found = true;
}
}
if (!found)
{
// If we didn't find any strings, copy the byte to the output
tbuf.Add(data[address++]);
}
}
// Flush the temp buffer
obuf.Add((byte)(control & 0xFF));
obuf.AddRange(tbuf.ToArray());
}
return obuf.ToArray();
}
}
}