2019-06-19 00:52:09 +00:00
|
|
|
|
using Syroot.BinaryData;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.IO.Compression;
|
|
|
|
|
using K4os.Compression.LZ4.Streams;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System;
|
2019-07-01 19:44:19 +00:00
|
|
|
|
using System.Runtime.InteropServices;
|
2019-06-19 00:52:09 +00:00
|
|
|
|
|
2019-07-16 21:35:21 +00:00
|
|
|
|
namespace Toolbox.Library.IO
|
2019-06-19 00:52:09 +00:00
|
|
|
|
{
|
|
|
|
|
public class STLibraryCompression
|
|
|
|
|
{
|
|
|
|
|
public static byte[] CompressFile(byte[] data, IFileFormat format)
|
|
|
|
|
{
|
|
|
|
|
int Alignment = 0;
|
|
|
|
|
|
|
|
|
|
if (format.IFileInfo != null)
|
|
|
|
|
Alignment = format.IFileInfo.Alignment;
|
|
|
|
|
|
2019-09-15 23:13:01 +00:00
|
|
|
|
var FileCompression = format.IFileInfo.FileCompression;
|
|
|
|
|
if (FileCompression == null) return data;
|
|
|
|
|
|
2019-11-27 14:12:22 +00:00
|
|
|
|
if (FileCompression is Yaz0)
|
|
|
|
|
((Yaz0)FileCompression).Alignment = Alignment;
|
|
|
|
|
|
2019-09-15 23:13:01 +00:00
|
|
|
|
return FileCompression.Compress(new MemoryStream(data)).ToArray();
|
2019-06-19 00:52:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ZSTD
|
|
|
|
|
{
|
2020-02-06 23:20:42 +00:00
|
|
|
|
|
2019-06-19 00:52:09 +00:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-11 20:25:20 +00:00
|
|
|
|
public class ZLIB_GZ
|
|
|
|
|
{
|
|
|
|
|
public static bool IsCompressed(Stream stream)
|
|
|
|
|
{
|
2019-09-15 23:13:01 +00:00
|
|
|
|
if (stream.Length < 32) return false;
|
|
|
|
|
|
2019-09-11 20:25:20 +00:00
|
|
|
|
using (var reader = new FileReader(stream, true))
|
|
|
|
|
{
|
2019-09-15 23:13:01 +00:00
|
|
|
|
reader.Position = 0;
|
2020-02-06 23:20:42 +00:00
|
|
|
|
reader.SetByteOrder(true);
|
2019-09-15 23:13:01 +00:00
|
|
|
|
uint chunkSize = reader.ReadUInt32();
|
2020-02-06 23:20:42 +00:00
|
|
|
|
if (chunkSize == 256)
|
|
|
|
|
reader.SetByteOrder(false);
|
|
|
|
|
|
2019-09-11 20:25:20 +00:00
|
|
|
|
uint chunkCount = reader.ReadUInt32();
|
2019-09-15 23:13:01 +00:00
|
|
|
|
uint decompressedSize = reader.ReadUInt32();
|
|
|
|
|
if (reader.BaseStream.Length > 8 + (chunkCount * 4) + 128)
|
2019-09-11 20:25:20 +00:00
|
|
|
|
{
|
2019-09-15 23:13:01 +00:00
|
|
|
|
uint[] chunkSizes = reader.ReadUInt32s((int)chunkCount);
|
2019-09-11 20:25:20 +00:00
|
|
|
|
reader.Align(128);
|
|
|
|
|
|
2019-09-15 23:13:01 +00:00
|
|
|
|
//Now search for zlibbed chunks
|
|
|
|
|
uint size = reader.ReadUInt32();
|
|
|
|
|
ushort magic = reader.ReadUInt16();
|
2019-09-11 20:25:20 +00:00
|
|
|
|
|
2019-09-15 23:13:01 +00:00
|
|
|
|
reader.Position = 0;
|
2020-02-06 23:20:42 +00:00
|
|
|
|
if (magic == 0x78da || magic == 0xda78)
|
2019-09-15 23:13:01 +00:00
|
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
2019-09-11 20:25:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reader.Position = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Stream Decompress(Stream stream)
|
|
|
|
|
{
|
|
|
|
|
using (var reader = new FileReader(stream, true))
|
|
|
|
|
{
|
2020-02-06 23:20:42 +00:00
|
|
|
|
reader.SetByteOrder(true);
|
|
|
|
|
uint chunkSize = reader.ReadUInt32();
|
|
|
|
|
if (chunkSize == 256)
|
|
|
|
|
reader.SetByteOrder(false);
|
|
|
|
|
|
2019-09-11 20:25:20 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
uint chunkCount = reader.ReadUInt32();
|
2019-09-15 23:13:01 +00:00
|
|
|
|
uint decompressedSize = reader.ReadUInt32();
|
2019-09-11 20:25:20 +00:00
|
|
|
|
uint[] chunkSizes = reader.ReadUInt32s((int)chunkCount); //Not very sure about this
|
|
|
|
|
|
|
|
|
|
reader.Align(128);
|
|
|
|
|
|
|
|
|
|
List<byte[]> DecompressedChunks = new List<byte[]>();
|
|
|
|
|
|
|
|
|
|
//Now search for zlibbed chunks
|
|
|
|
|
while (!reader.EndOfStream)
|
|
|
|
|
{
|
|
|
|
|
uint size = reader.ReadUInt32();
|
|
|
|
|
|
|
|
|
|
long pos = reader.Position;
|
|
|
|
|
ushort magic = reader.ReadUInt16();
|
|
|
|
|
|
|
|
|
|
///Check zlib magic
|
|
|
|
|
if (magic == 0x78da)
|
|
|
|
|
{
|
|
|
|
|
var data = STLibraryCompression.ZLIB.Decompress(reader.getSection((uint)pos, size));
|
|
|
|
|
DecompressedChunks.Add(data);
|
|
|
|
|
|
|
|
|
|
reader.SeekBegin(pos + size); //Seek the compressed size and align it to goto the next chunk
|
|
|
|
|
reader.Align(128);
|
|
|
|
|
}
|
|
|
|
|
else //If the magic check fails, seek back 2. This shouldn't happen, but just incase
|
|
|
|
|
reader.Seek(-2);
|
|
|
|
|
}
|
2020-02-06 23:20:42 +00:00
|
|
|
|
|
2019-09-11 20:25:20 +00:00
|
|
|
|
//Return the decompressed stream with all chunks combined
|
|
|
|
|
return new MemoryStream(Utils.CombineByteArray(DecompressedChunks.ToArray()));
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2019-09-15 23:13:01 +00:00
|
|
|
|
|
|
|
|
|
public static Stream Compress(Stream stream, bool isBigEndian = true)
|
|
|
|
|
{
|
|
|
|
|
uint decompSize = (uint)stream.Length;
|
|
|
|
|
uint[] section_sizes;
|
|
|
|
|
uint sectionCount = 0;
|
|
|
|
|
|
|
|
|
|
var mem = new MemoryStream();
|
|
|
|
|
using (var reader = new FileReader(stream, true))
|
|
|
|
|
using (var writer = new FileWriter(mem, true))
|
|
|
|
|
{
|
|
|
|
|
writer.SetByteOrder(isBigEndian);
|
|
|
|
|
|
|
|
|
|
if (!(decompSize % 0x10000 != 0))
|
|
|
|
|
sectionCount = decompSize / 0x10000;
|
|
|
|
|
else
|
|
|
|
|
sectionCount = (decompSize / 0x10000) + 1;
|
|
|
|
|
|
|
|
|
|
writer.Write(0x10000);
|
|
|
|
|
writer.Write(sectionCount);
|
|
|
|
|
writer.Write(decompSize);
|
|
|
|
|
writer.Write(new uint[sectionCount]);
|
|
|
|
|
writer.Align(128);
|
|
|
|
|
|
|
|
|
|
reader.SeekBegin(0);
|
|
|
|
|
section_sizes = new uint[sectionCount];
|
|
|
|
|
for (int i = 0; i < sectionCount; i++)
|
|
|
|
|
{
|
|
|
|
|
byte[] chunk = ZLIB.Compress(reader.ReadBytes(0x10000));
|
|
|
|
|
|
|
|
|
|
section_sizes[i] = (uint)chunk.Length;
|
|
|
|
|
|
|
|
|
|
writer.Write(chunk.Length);
|
|
|
|
|
writer.Write(chunk);
|
|
|
|
|
writer.Align(128);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.SeekBegin(12);
|
|
|
|
|
for (int i = 0; i < sectionCount; i++)
|
|
|
|
|
writer.Write(section_sizes[i] + 4);
|
|
|
|
|
}
|
|
|
|
|
return mem;
|
|
|
|
|
}
|
2019-09-11 20:25:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 00:52:09 +00:00
|
|
|
|
public class ZLIB
|
|
|
|
|
{
|
2019-09-15 23:13:01 +00:00
|
|
|
|
public static byte[] Decompress(byte[] b, bool hasMagic = true)
|
2019-06-19 00:52:09 +00:00
|
|
|
|
{
|
2019-07-01 00:03:04 +00:00
|
|
|
|
using (var br = new FileReader(new MemoryStream(b), true))
|
|
|
|
|
{
|
|
|
|
|
if (br.ReadString(4) == "ZCMP")
|
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
return DecompressZCMP(b);
|
2019-07-01 00:03:04 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var ms = new System.IO.MemoryStream();
|
2019-09-15 23:13:01 +00:00
|
|
|
|
if (hasMagic)
|
2019-09-18 21:02:25 +00:00
|
|
|
|
{
|
2019-09-15 23:13:01 +00:00
|
|
|
|
br.Position = 2;
|
2019-09-18 21:02:25 +00:00
|
|
|
|
using (var ds = new DeflateStream(new MemoryStream(br.ReadBytes((int)br.BaseStream.Length - 6)), CompressionMode.Decompress))
|
|
|
|
|
ds.CopyTo(ms);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
using (var ds = new DeflateStream(new MemoryStream(b), CompressionMode.Decompress))
|
|
|
|
|
ds.CopyTo(ms);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-01 00:03:04 +00:00
|
|
|
|
return ms.ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Byte[] DecompressZCMP(byte[] b)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("DecompressZCMP");
|
|
|
|
|
|
2019-06-19 00:52:09 +00:00
|
|
|
|
using (var br = new FileReader(new MemoryStream(b), true))
|
|
|
|
|
{
|
|
|
|
|
var ms = new System.IO.MemoryStream();
|
2019-07-01 00:03:04 +00:00
|
|
|
|
br.BaseStream.Position = 130;
|
|
|
|
|
using (var ds = new DeflateStream(new MemoryStream(br.ReadBytes((int)br.BaseStream.Length - 80)), CompressionMode.Decompress))
|
2019-06-19 00:52:09 +00:00
|
|
|
|
ds.CopyTo(ms);
|
|
|
|
|
return ms.ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static byte[] Compress(byte[] b, uint Position = 0)
|
|
|
|
|
{
|
|
|
|
|
var output = new MemoryStream();
|
|
|
|
|
output.Write(new byte[] { 0x78, 0xDA }, 0, 2);
|
|
|
|
|
|
2019-07-01 19:44:19 +00:00
|
|
|
|
using (var zipStream = new DeflateStream(output, CompressionMode.Compress, true))
|
|
|
|
|
zipStream.Write(b, 0, b.Length);
|
2019-06-19 00:52:09 +00:00
|
|
|
|
|
2019-07-01 19:44:19 +00:00
|
|
|
|
//Add this as it weirdly prevents the data getting corrupted
|
|
|
|
|
//From https://github.com/IcySon55/Kuriimu/blob/f670c2719affc1eaef8b4c40e40985881247acc7/src/Kontract/Compression/ZLib.cs
|
|
|
|
|
var adler = b.Aggregate(Tuple.Create(1, 0), (x, n) => Tuple.Create((x.Item1 + n) % 65521, (x.Item1 + x.Item2 + n) % 65521));
|
|
|
|
|
output.Write(new[] { (byte)(adler.Item2 >> 8), (byte)adler.Item2, (byte)(adler.Item1 >> 8), (byte)adler.Item1 }, 0, 4);
|
|
|
|
|
return output.ToArray();
|
2019-06-19 00:52:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void CopyStream(System.IO.Stream input, System.IO.Stream output)
|
|
|
|
|
{
|
|
|
|
|
byte[] buffer = new byte[2000];
|
|
|
|
|
int len;
|
|
|
|
|
while ((len = input.Read(buffer, 0, 2000)) > 0)
|
|
|
|
|
{
|
|
|
|
|
output.Write(buffer, 0, len);
|
|
|
|
|
}
|
|
|
|
|
output.Flush();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
public class BPE
|
2019-06-19 00:52:09 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
public static unsafe byte[] Decompress(byte[] input, uint decompressedLength)
|
2019-06-19 00:52:09 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
fixed (byte* outputPtr = new byte[decompressedLength])
|
|
|
|
|
{
|
|
|
|
|
fixed (byte* inputPtr = input)
|
|
|
|
|
{
|
|
|
|
|
Decompress(outputPtr, inputPtr, decompressedLength);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
byte[] decomp = new byte[decompressedLength];
|
|
|
|
|
Marshal.Copy((IntPtr)outputPtr, decomp, 0, decomp.Length);
|
|
|
|
|
return decomp;
|
|
|
|
|
}
|
2019-06-19 00:52:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
public static unsafe void Decompress(byte* output, byte* input, uint decompressedLength)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Mario Tennis Aces Custom compression
|
|
|
|
|
public class MTA_CUSTOM
|
|
|
|
|
{
|
2019-09-15 23:13:01 +00:00
|
|
|
|
[DllImport("Lib/LibTennis64.dll", CallingConvention = CallingConvention.Cdecl)]
|
2019-09-18 21:02:25 +00:00
|
|
|
|
static extern void DecompressBuffer(IntPtr output, IntPtr input, uint len);
|
2019-07-05 18:45:16 +00:00
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
public unsafe byte[] Decompress(byte[] input, uint decompressedLength)
|
2019-06-19 00:52:09 +00:00
|
|
|
|
{
|
2019-07-01 19:44:19 +00:00
|
|
|
|
fixed (byte* outputPtr = new byte[decompressedLength])
|
|
|
|
|
{
|
|
|
|
|
fixed (byte* inputPtr = input)
|
|
|
|
|
{
|
2019-09-15 23:13:01 +00:00
|
|
|
|
if (Environment.Is64BitProcess)
|
2019-09-18 21:02:25 +00:00
|
|
|
|
DecompressBuffer((IntPtr)outputPtr, (IntPtr)inputPtr, decompressedLength);
|
2019-09-15 23:13:01 +00:00
|
|
|
|
else
|
2019-09-18 21:02:25 +00:00
|
|
|
|
MarioTennisCmp32.DecompressBuffer((IntPtr)outputPtr, (IntPtr)inputPtr, decompressedLength);
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
2019-06-19 00:52:09 +00:00
|
|
|
|
|
2019-07-01 19:44:19 +00:00
|
|
|
|
byte[] decomp = new byte[decompressedLength];
|
|
|
|
|
Marshal.Copy((IntPtr)outputPtr, decomp, 0, decomp.Length);
|
|
|
|
|
return decomp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Thanks Simon. Code ported from
|
|
|
|
|
//https://github.com/simontime/MarioTennisAces0x50Decompressor/blob/master/decompress.c
|
2019-07-02 20:12:23 +00:00
|
|
|
|
public unsafe void Decompress(byte* output, byte* input, uint decompressedLength)
|
2019-07-01 19:44:19 +00:00
|
|
|
|
{
|
2019-06-19 00:52:09 +00:00
|
|
|
|
uint pos = 8;
|
2019-07-01 19:44:19 +00:00
|
|
|
|
byte* end = input + decompressedLength;
|
|
|
|
|
byte* data = input + pos;
|
|
|
|
|
|
2019-07-01 21:27:55 +00:00
|
|
|
|
Console.WriteLine($"decompressedLength " + decompressedLength);
|
|
|
|
|
Console.WriteLine($"pos " + pos);
|
|
|
|
|
|
|
|
|
|
if (pos < decompressedLength)
|
2019-06-19 00:52:09 +00:00
|
|
|
|
{
|
|
|
|
|
uint flag;
|
2019-07-01 19:44:19 +00:00
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
flag = 0xFF000000 * data[0];
|
|
|
|
|
if (flag != 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
data++;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
|
*output++ = *data++;
|
|
|
|
|
|
2019-09-15 23:13:01 +00:00
|
|
|
|
CheckFinished(ref data, ref end);
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flag |= 0x800000;
|
|
|
|
|
|
|
|
|
|
data++;
|
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
Console.WriteLine($"flag " + flag);
|
|
|
|
|
|
2019-07-01 19:44:19 +00:00
|
|
|
|
//IterateFlag
|
|
|
|
|
while ((flag & 0x80000000) == 0)
|
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
flag <<= 1;
|
|
|
|
|
*output++ = *data++;
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
Console.WriteLine($"Pass 3 ");
|
2019-07-01 21:27:55 +00:00
|
|
|
|
|
2019-07-01 19:44:19 +00:00
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
flag <<= 1;
|
|
|
|
|
|
|
|
|
|
if (flag == 0)
|
2019-09-15 23:13:01 +00:00
|
|
|
|
CheckFinished(ref data, ref end);
|
2019-07-01 19:44:19 +00:00
|
|
|
|
|
|
|
|
|
int op_ofs = (data[0] >> 4) | (data[1] << 4);
|
|
|
|
|
int op_len = data[0] & 0xF;
|
|
|
|
|
|
|
|
|
|
if (op_ofs == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
byte* chunk = output - op_ofs;
|
2019-07-01 19:44:19 +00:00
|
|
|
|
if (op_len > 1)
|
|
|
|
|
data += 2;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
int op_len_ext = data[2] + (op_len | 0x10);
|
|
|
|
|
|
|
|
|
|
if (op_len == 1)
|
|
|
|
|
{
|
|
|
|
|
int add_len = (data[3] << 8) | 0xFF;
|
|
|
|
|
data += 4;
|
|
|
|
|
|
|
|
|
|
op_len = op_len_ext + add_len;
|
|
|
|
|
|
|
|
|
|
if (op_ofs >= 2)
|
2019-09-15 23:13:01 +00:00
|
|
|
|
Loop1(ref flag, ref op_len, ref chunk, ref data, ref output);
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
data += 3;
|
|
|
|
|
op_len = op_len_ext;
|
|
|
|
|
if (op_ofs >= 2)
|
|
|
|
|
{
|
2020-02-06 23:20:42 +00:00
|
|
|
|
Loop1(ref flag, ref op_len, ref chunk, ref data, ref output);
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-02 20:12:23 +00:00
|
|
|
|
|
2020-02-06 23:20:42 +00:00
|
|
|
|
Loop2(ref flag, ref op_len, ref data, ref output, ref chunk);
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
2019-06-19 00:52:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-15 23:13:01 +00:00
|
|
|
|
EndOperation(ref data, ref end);
|
2019-06-19 00:52:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-15 23:13:01 +00:00
|
|
|
|
unsafe void Loop1(ref uint flag, ref int op_len, ref byte* chunk, ref byte* data, ref byte* output)
|
2019-07-02 20:12:23 +00:00
|
|
|
|
{
|
|
|
|
|
if ((((byte)*chunk ^ (byte)*output) & 1) == 0)
|
2019-07-01 19:44:19 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
if (((byte)*chunk & 1) != 0)
|
|
|
|
|
{
|
|
|
|
|
*output++ = *chunk++;
|
|
|
|
|
op_len--;
|
|
|
|
|
}
|
2019-07-01 19:44:19 +00:00
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
uint op_len_sub = (uint)op_len - 2;
|
2019-07-01 19:44:19 +00:00
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
if (op_len >= 2)
|
2019-07-01 19:44:19 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
int masked_len = (((int)op_len_sub >> 1) + 1) & 7;
|
|
|
|
|
|
|
|
|
|
byte* out_ptr = output;
|
|
|
|
|
byte* chunk_ptr = chunk;
|
|
|
|
|
|
|
|
|
|
if (masked_len != 0)
|
2019-07-01 19:44:19 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
while (masked_len-- != 0)
|
|
|
|
|
{
|
|
|
|
|
*out_ptr++ = *chunk_ptr++;
|
|
|
|
|
*out_ptr++ = *chunk_ptr++;
|
|
|
|
|
op_len -= 2;
|
|
|
|
|
}
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
uint masked_ext_len = op_len_sub & 0xFFFFFFFE;
|
2019-07-01 19:44:19 +00:00
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
if (op_len_sub >= 0xE)
|
2019-07-01 19:44:19 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < 0x10; i++)
|
|
|
|
|
*out_ptr++ = *chunk_ptr++;
|
2019-07-01 19:44:19 +00:00
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
op_len -= 0x10;
|
|
|
|
|
}
|
|
|
|
|
while (op_len > 1);
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
2019-07-02 20:12:23 +00:00
|
|
|
|
|
|
|
|
|
output += masked_ext_len + 2;
|
|
|
|
|
op_len = (int)op_len_sub - (int)masked_ext_len;
|
|
|
|
|
chunk += masked_ext_len + 2;
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
if (op_len == 0)
|
|
|
|
|
{
|
|
|
|
|
if ((flag & 0x80000000) == 0)
|
|
|
|
|
{
|
|
|
|
|
flag <<= 1;
|
|
|
|
|
*output++ = *data++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-15 23:13:01 +00:00
|
|
|
|
Loop2(ref flag, ref op_len, ref data, ref output, ref chunk);
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-15 23:13:01 +00:00
|
|
|
|
unsafe void Loop2(ref uint flag, ref int op_len, ref byte* data, ref byte* output, ref byte* chunk)
|
2019-07-01 19:44:19 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
int masked_len = op_len & 7;
|
|
|
|
|
byte* out_ptr = output;
|
|
|
|
|
byte* chunk_ptr = chunk;
|
2019-07-01 19:44:19 +00:00
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
if (masked_len != 0)
|
2019-07-01 19:44:19 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
while (masked_len-- != 0)
|
2019-07-01 19:44:19 +00:00
|
|
|
|
*out_ptr++ = *chunk_ptr++;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
if (op_len - 1 >= 7)
|
|
|
|
|
{
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
|
*out_ptr++ = *chunk_ptr++;
|
|
|
|
|
}
|
|
|
|
|
while (chunk_ptr != chunk + op_len);
|
|
|
|
|
}
|
2019-07-01 19:44:19 +00:00
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
output += op_len;
|
2019-07-01 19:44:19 +00:00
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
if ((flag & 0x80000000) == 0)
|
|
|
|
|
{
|
|
|
|
|
flag <<= 1;
|
|
|
|
|
*output++ = *data++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-01 19:44:19 +00:00
|
|
|
|
|
2019-09-15 23:13:01 +00:00
|
|
|
|
unsafe void CheckFinished(ref byte* data, ref byte* end)
|
2019-07-02 20:12:23 +00:00
|
|
|
|
{
|
|
|
|
|
if (data >= end)
|
2019-09-15 23:13:01 +00:00
|
|
|
|
EndOperation(ref data, ref end);
|
2019-07-02 20:12:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-15 23:13:01 +00:00
|
|
|
|
unsafe void EndOperation(ref byte* data, ref byte* end)
|
2019-07-02 20:12:23 +00:00
|
|
|
|
{
|
|
|
|
|
byte* ext = end + 0x20;
|
|
|
|
|
if (data < ext)
|
|
|
|
|
do
|
|
|
|
|
*end-- = *--ext;
|
|
|
|
|
while (data < ext);
|
|
|
|
|
}
|
2019-07-01 19:44:19 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 00:52:09 +00:00
|
|
|
|
public class LZSS
|
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
static class LzssParameters
|
2019-06-19 00:52:09 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
/// <summary>Size of the ring buffer.</summary>
|
|
|
|
|
public const int N = 4096;
|
|
|
|
|
/// <summary>Maximum match length for position coding. (0x0F + THRESHOLD).</summary>
|
|
|
|
|
public const int F = 18;
|
|
|
|
|
/// <summary>Minimum match length for position coding.</summary>
|
|
|
|
|
public const int THRESHOLD = 3;
|
|
|
|
|
/// <summary>Index for root of binary search trees.</summary>
|
|
|
|
|
public const int NIL = N;
|
|
|
|
|
/// <summary>Character used to fill the ring buffer initially.</summary>
|
|
|
|
|
//private const ubyte BUFF_INIT = ' ';
|
|
|
|
|
public const byte BUFF_INIT = 0; // Changed for F-Zero GX
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static byte[] Decompress(byte[] input, uint decompressedLength)
|
|
|
|
|
{
|
|
|
|
|
List<byte> output = new List<byte>();
|
|
|
|
|
byte[] ringBuf = new byte[LzssParameters.N];
|
|
|
|
|
int inputPos = 0, ringBufPos = LzssParameters.N - LzssParameters.F;
|
|
|
|
|
|
|
|
|
|
ushort flags = 0;
|
|
|
|
|
|
|
|
|
|
// Clear ringBuf with a character that will appear often
|
|
|
|
|
for (int i = 0; i < LzssParameters.N - LzssParameters.F; i++)
|
|
|
|
|
ringBuf[i] = LzssParameters.BUFF_INIT;
|
|
|
|
|
|
|
|
|
|
while (inputPos < input.Length)
|
2019-06-19 00:52:09 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
// Use 16 bits cleverly to count to 8.
|
|
|
|
|
// (After 8 shifts, the high bits will be cleared).
|
|
|
|
|
if ((flags & 0xFF00) == 0)
|
|
|
|
|
flags = (ushort)(input[inputPos++] | 0x8000);
|
2019-06-19 00:52:09 +00:00
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
if ((flags & 1) == 1)
|
2019-06-19 00:52:09 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
// Copy data literally from input
|
|
|
|
|
byte c = input[inputPos++];
|
|
|
|
|
output.Add(c);
|
|
|
|
|
ringBuf[ringBufPos++ % LzssParameters.N] = c;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Copy data from the ring buffer (previous data).
|
|
|
|
|
int index = ((input[inputPos + 1] & 0xF0) << 4) | input[inputPos];
|
|
|
|
|
int count = (input[inputPos + 1] & 0x0F) + LzssParameters.THRESHOLD;
|
|
|
|
|
inputPos += 2;
|
2019-06-19 00:52:09 +00:00
|
|
|
|
|
2019-07-02 20:12:23 +00:00
|
|
|
|
for (int i = 0; i < count; i++)
|
2019-06-19 00:52:09 +00:00
|
|
|
|
{
|
2019-07-02 20:12:23 +00:00
|
|
|
|
byte c = ringBuf[(index + i) % LzssParameters.N];
|
|
|
|
|
output.Add(c);
|
|
|
|
|
ringBuf[ringBufPos++ % LzssParameters.N] = c;
|
2019-06-19 00:52:09 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-02 20:12:23 +00:00
|
|
|
|
|
|
|
|
|
// Advance flags & count bits
|
|
|
|
|
flags >>= 1;
|
2019-06-19 00:52:09 +00:00
|
|
|
|
}
|
2019-07-02 20:12:23 +00:00
|
|
|
|
|
|
|
|
|
return output.ToArray();
|
2019-06-19 00:52:09 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class LZ77
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Decompresses LZ77-compressed data from the given input stream.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="input">The input stream to read from.</param>
|
|
|
|
|
/// <returns>The decompressed data.</returns>
|
|
|
|
|
public static byte[] Decompress(byte[] input)
|
|
|
|
|
{
|
|
|
|
|
BinaryReader reader = new BinaryReader(new MemoryStream(input));
|
|
|
|
|
|
|
|
|
|
// Check LZ77 type.
|
|
|
|
|
// if (reader.ReadByte() != 0x10)
|
|
|
|
|
// throw new System.Exception("Input stream does not contain LZ77-compressed data.");
|
|
|
|
|
|
|
|
|
|
// Read the size.
|
|
|
|
|
int size = reader.ReadUInt16() | (reader.ReadByte() << 16);
|
|
|
|
|
|
|
|
|
|
// Create output stream.
|
|
|
|
|
MemoryStream output = new MemoryStream(size);
|
|
|
|
|
|
|
|
|
|
// Begin decompression.
|
|
|
|
|
while (output.Length < size)
|
|
|
|
|
{
|
|
|
|
|
// Load flags for the next 8 blocks.
|
|
|
|
|
int flagByte = reader.ReadByte();
|
|
|
|
|
|
|
|
|
|
// Process the next 8 blocks.
|
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
|
{
|
|
|
|
|
// Check if the block is compressed.
|
|
|
|
|
if ((flagByte & (0x80 >> i)) == 0)
|
|
|
|
|
{
|
|
|
|
|
// Uncompressed block; copy single byte.
|
|
|
|
|
output.WriteByte(reader.ReadByte());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Compressed block; read block.
|
|
|
|
|
ushort block = reader.ReadUInt16();
|
|
|
|
|
// Get byte count.
|
|
|
|
|
int count = ((block >> 4) & 0xF) + 3;
|
|
|
|
|
// Get displacement.
|
|
|
|
|
int disp = ((block & 0xF) << 8) | ((block >> 8) & 0xFF);
|
|
|
|
|
|
|
|
|
|
// Save current position and copying position.
|
|
|
|
|
long outPos = output.Position;
|
|
|
|
|
long copyPos = output.Position - disp - 1;
|
|
|
|
|
|
|
|
|
|
// Copy all bytes.
|
|
|
|
|
for (int j = 0; j < count; j++)
|
|
|
|
|
{
|
|
|
|
|
// Read byte to be copied.
|
|
|
|
|
output.Position = copyPos++;
|
|
|
|
|
byte b = (byte)output.ReadByte();
|
|
|
|
|
|
|
|
|
|
// Write byte to be copied.
|
|
|
|
|
output.Position = outPos++;
|
|
|
|
|
output.WriteByte(b);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If all data has been decompressed, stop.
|
|
|
|
|
if (output.Length >= size)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output.Position = 0;
|
|
|
|
|
return output.ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class GZIP
|
|
|
|
|
{
|
|
|
|
|
public static byte[] Decompress(byte[] b)
|
|
|
|
|
{
|
|
|
|
|
using (MemoryStream mem = new MemoryStream())
|
|
|
|
|
{
|
|
|
|
|
using (GZipStream source = new GZipStream(new MemoryStream(b), CompressionMode.Decompress, false))
|
|
|
|
|
{
|
|
|
|
|
source.CopyTo(mem);
|
|
|
|
|
}
|
|
|
|
|
return mem.ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static byte[] Compress(byte[] b)
|
|
|
|
|
{
|
|
|
|
|
using (MemoryStream mem = new MemoryStream())
|
|
|
|
|
{
|
|
|
|
|
using (GZipStream gzip = new GZipStream(mem,
|
|
|
|
|
CompressionMode.Compress))
|
|
|
|
|
{
|
|
|
|
|
gzip.Write(b, 0, b.Length);
|
|
|
|
|
}
|
|
|
|
|
return mem.ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class Type_LZ4F
|
|
|
|
|
{
|
|
|
|
|
public static byte[] Decompress(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
using (MemoryStream mem = new MemoryStream())
|
|
|
|
|
{
|
|
|
|
|
using (var source = LZ4Stream.Decode(new MemoryStream(data)))
|
|
|
|
|
{
|
|
|
|
|
source.CopyTo(mem);
|
|
|
|
|
}
|
|
|
|
|
return mem.ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public static byte[] Compress(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
var stream = new MemoryStream();
|
|
|
|
|
using (var writer = new FileWriter(stream))
|
|
|
|
|
{
|
|
|
|
|
writer.Write(data.Length);
|
|
|
|
|
byte[] buffer = LZ4.Frame.LZ4Frame.Compress(new MemoryStream(data),
|
|
|
|
|
LZ4.Frame.LZ4MaxBlockSize.MB1, true, true, false, true, false);
|
|
|
|
|
|
|
|
|
|
writer.Write(buffer, 0, buffer.Length);
|
|
|
|
|
}
|
|
|
|
|
return stream.ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public class Type_LZ4
|
|
|
|
|
{
|
|
|
|
|
public static byte[] Decompress(byte[] data, int inputOffset, int InputLength, int decompressedSize)
|
|
|
|
|
{
|
|
|
|
|
return LZ4.LZ4Codec.Decode(data, inputOffset, InputLength, decompressedSize);
|
|
|
|
|
}
|
|
|
|
|
public static byte[] Decompress(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
using (MemoryStream mem = new MemoryStream())
|
|
|
|
|
{
|
|
|
|
|
using (var source = LZ4Stream.Decode(new MemoryStream(data)))
|
|
|
|
|
{
|
|
|
|
|
source.CopyTo(mem);
|
|
|
|
|
mem.Write(data, 0, data.Length);
|
|
|
|
|
}
|
|
|
|
|
return mem.ToArray();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public static byte[] Compress(byte[] data, int inputOffset = 0)
|
|
|
|
|
{
|
|
|
|
|
return LZ4.LZ4Codec.Encode(data, inputOffset, data.Length);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|