Switch-Toolbox/Switch_Toolbox_Library/Compression/Formats/LZSS.cs
2020-01-22 17:13:54 -05:00

109 lines
3.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using Toolbox.Library.IO;
using K4os.Compression.LZ4.Streams;
namespace Toolbox.Library
{
//From https://github.com/xdanieldzd/N3DSCmbViewer/blob/3c3f66cf40d9122f8d0ebeab07c4db659b426b8b/N3DSCmbViewer/LZSS.cs
//and https://github.com/lue/MM3D/blob/master/src/lzs.cpp
public class LZSS : ICompressionFormat
{
public string[] Description { get; set; } = new string[] { "LZSS Compression" };
public string[] Extension { get; set; } = new string[] { "*.lzs", "*.lzss" };
private bool hasMagic = false;
public bool Identify(Stream stream, string fileName)
{
using (var reader = new FileReader(stream, true))
{
hasMagic = reader.CheckSignature(4, "LzS\x01");
return hasMagic || Utils.GetExtension(fileName) == ".lzs";
}
}
public bool CanCompress { get; } = true;
public Stream Decompress(Stream stream)
{
byte[] arcdata = stream.ToArray();
uint decompressedSize = 0;
uint compressedSize = 0;
if (hasMagic)
{
string tag = Encoding.ASCII.GetString(arcdata, 0, 4);
uint unknown = BitConverter.ToUInt32(arcdata, 4);
decompressedSize = BitConverter.ToUInt32(arcdata, 8);
compressedSize = BitConverter.ToUInt32(arcdata, 12);
if (arcdata.Length != compressedSize + 0x10) throw new Exception("compressed size mismatch");
}
else
{
decompressedSize = BitConverter.ToUInt32(arcdata, 0);
}
List<byte> outdata = new List<byte>();
byte[] BUFFER = new byte[4096];
for (int i = 0; i < BUFFER.Length; i++) BUFFER[i] = 0;
byte flags8 = 0;
ushort writeidx = 0xFEE;
ushort readidx = 0;
uint fidx = 0x10;
if (!hasMagic)
fidx = 4;
while (fidx < arcdata.Length)
{
flags8 = arcdata[fidx];
fidx++;
for (int i = 0; i < 8; i++)
{
if ((flags8 & 1) != 0)
{
outdata.Add(arcdata[fidx]);
BUFFER[writeidx] = arcdata[fidx];
writeidx++; writeidx %= 4096;
fidx++;
}
else
{
readidx = arcdata[fidx];
fidx++;
readidx |= (ushort)((arcdata[fidx] & 0xF0) << 4);
for (int j = 0; j < (arcdata[fidx] & 0x0F) + 3; j++)
{
outdata.Add(BUFFER[readidx]);
BUFFER[writeidx] = BUFFER[readidx];
readidx++; readidx %= 4096;
writeidx++; writeidx %= 4096;
}
fidx++;
}
flags8 >>= 1;
if (fidx >= arcdata.Length) break;
}
}
if (decompressedSize != outdata.Count)
throw new Exception(string.Format("Size mismatch: got {0} bytes after decompression, expected {1}.\n", outdata.Count, decompressedSize));
return new MemoryStream(outdata.ToArray());
}
public Stream Compress(Stream stream)
{
var mem = new MemoryStream();
return stream;
}
}
}