280 lines
9.0 KiB
C#
280 lines
9.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Threading.Tasks;
|
|
using Toolbox.Library.IO;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Toolbox.Library
|
|
{
|
|
public class Yaz0 : ICompressionFormat
|
|
{
|
|
public int Alignment = 0;
|
|
|
|
public string[] Description { get; set; } = new string[] { "Yaz0" };
|
|
public string[] Extension { get; set; } = new string[] { "*.yaz0", "*.szs", };
|
|
|
|
public override string ToString() { return "Yaz0"; }
|
|
|
|
public bool Identify(Stream stream, string fileName)
|
|
{
|
|
using (var reader = new FileReader(stream, true))
|
|
{
|
|
return reader.CheckSignature(4, "Yaz0");
|
|
}
|
|
}
|
|
|
|
public bool CanCompress { get; } = true;
|
|
|
|
public Stream Decompress(Stream stream)
|
|
{
|
|
var comp = stream.ToBytes();
|
|
UInt32 decompressedSize = (uint)(comp[4] << 24 | comp[5] << 16 | comp[6] << 8 | comp[7]);
|
|
var data = Decompress(comp);
|
|
// var data = EveryFileExplorer.YAZ0.Decompress(comp);
|
|
// System.IO.File.WriteAllBytes("testYaz0.dec", data);
|
|
return new MemoryStream(data);
|
|
}
|
|
|
|
[DllImport("Lib/Yaz0.dll")]
|
|
static unsafe extern byte* decompress(byte* src, uint src_len, uint* dest_len);
|
|
|
|
[DllImport("Lib/Yaz0.dll")]
|
|
static unsafe extern byte* compress(byte* src, uint src_len, uint* dest_len, byte opt_compr);
|
|
|
|
[DllImport("Lib/Yaz0.dll")]
|
|
static unsafe extern void freePtr(void* ptr);
|
|
|
|
private unsafe byte[] Decompress(byte[] data)
|
|
{
|
|
return EveryFileExplorer.YAZ0.Decompress(data);
|
|
|
|
uint src_len = (uint)data.Length;
|
|
|
|
uint dest_len;
|
|
fixed (byte* inputPtr = data)
|
|
{
|
|
byte* outputPtr = decompress(inputPtr, src_len, &dest_len);
|
|
byte[] decomp = new byte[dest_len];
|
|
Marshal.Copy((IntPtr)outputPtr, decomp, 0, (int)dest_len);
|
|
freePtr(outputPtr);
|
|
return decomp;
|
|
}
|
|
}
|
|
|
|
public Stream Compress(Stream stream)
|
|
{
|
|
return new MemoryStream(EveryFileExplorer.YAZ0.Compress(
|
|
stream.ToArray(), Runtime.Yaz0CompressionLevel, (uint)Alignment));
|
|
|
|
var mem = new MemoryStream();
|
|
using (var writer = new FileWriter(mem, true))
|
|
{
|
|
writer.SetByteOrder(true);
|
|
writer.WriteSignature("Yaz0");
|
|
writer.Write((uint)stream.Length);
|
|
writer.Write((uint)Alignment);
|
|
writer.Write(0);
|
|
writer.Write(Compress(stream.ToArray(), (byte)Runtime.Yaz0CompressionLevel));
|
|
}
|
|
return mem;
|
|
}
|
|
|
|
public static unsafe byte[] Compress(byte[] src, byte opt_compr)
|
|
{
|
|
uint src_len = (uint)src.Length;
|
|
|
|
uint dest_len;
|
|
fixed (byte* inputPtr = src)
|
|
{
|
|
byte* outputPtr = compress(inputPtr, src_len, &dest_len, opt_compr);
|
|
byte[] comp = new byte[dest_len];
|
|
Marshal.Copy((IntPtr)outputPtr, comp, 0, (int)dest_len);
|
|
freePtr(outputPtr);
|
|
return comp;
|
|
}
|
|
|
|
Console.WriteLine($"opt_compr {opt_compr}");
|
|
|
|
uint range;
|
|
if (opt_compr == 0)
|
|
range = 0;
|
|
else if (opt_compr < 9)
|
|
range = (uint)(0x10e0 * opt_compr / 9 - 0x0e0);
|
|
else
|
|
range = 0x1000;
|
|
|
|
uint pos = 0;
|
|
uint src_end = (uint)src.Length;
|
|
|
|
byte[] dest = new byte[src_end + (src_end + 8) / 8];
|
|
uint dest_pos = 0;
|
|
uint code_byte_pos = 0;
|
|
|
|
ulong found = 0;
|
|
ulong found_len = 0;
|
|
uint delta;
|
|
int max_len = 0x111;
|
|
|
|
while (pos < src_end)
|
|
{
|
|
code_byte_pos = dest_pos;
|
|
dest[dest_pos] = 0; dest_pos++;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
if (pos >= src_end)
|
|
break;
|
|
|
|
found_len = 1;
|
|
|
|
if (range != 0)
|
|
{
|
|
// Going after speed here.
|
|
// Dunno if using a tuple is slower, so I don't want to risk it.
|
|
ulong search = compressionSearch(src, pos, max_len, range, src_end);
|
|
found = search >> 32;
|
|
found_len = search & 0xFFFFFFFF;
|
|
}
|
|
|
|
if (found_len > 2)
|
|
{
|
|
delta = (uint)(pos - found - 1);
|
|
|
|
if (found_len < 0x12)
|
|
{
|
|
dest[dest_pos] = (byte)(delta >> 8 | (found_len - 2) << 4); dest_pos++;
|
|
dest[dest_pos] = (byte)(delta & 0xFF); dest_pos++;
|
|
}
|
|
else
|
|
{
|
|
dest[dest_pos] = (byte)(delta >> 8); dest_pos++;
|
|
dest[dest_pos] = (byte)(delta & 0xFF); dest_pos++;
|
|
dest[dest_pos] = (byte)((found_len - 0x12) & 0xFF); dest_pos++;
|
|
}
|
|
|
|
pos += (uint)found_len;
|
|
}
|
|
|
|
else
|
|
{
|
|
dest[code_byte_pos] |= (byte)(1 << (7 - i));
|
|
dest[dest_pos] = src[pos]; dest_pos++; pos++;
|
|
}
|
|
}
|
|
}
|
|
|
|
byte[] result = new byte[dest_pos];
|
|
Array.Copy(dest, result, dest_pos);
|
|
return result;
|
|
}
|
|
|
|
public static ulong ReadULong(byte[] v, uint i)
|
|
{
|
|
int i1 = v[i] | (v[i + 1] << 8) | (v[i + 2] << 16) | (v[i + 3] << 24);
|
|
int i2 = v[i + 4] | (v[i + 5] << 8) | (v[i + 6] << 16) | (v[i + 7] << 24);
|
|
return (ulong)((uint)i1 | ((long)i2 << 32));
|
|
}
|
|
|
|
public static long IndexOfByte(byte[] src, byte v, uint i, uint c)
|
|
{
|
|
// https://stackoverflow.com/a/46678141
|
|
|
|
ulong t;
|
|
uint p, pEnd;
|
|
|
|
for (p = i; ((long)p & 7) != 0; c--, p++)
|
|
if (c == 0)
|
|
return -1;
|
|
else if (src[p] == v)
|
|
return p;
|
|
|
|
ulong r = v; r |= r << 8; r |= r << 16; r |= r << 32;
|
|
|
|
for (pEnd = (uint)(p + (c & ~7)); p < pEnd; p += 8)
|
|
{
|
|
t = ReadULong(src, p) ^ r;
|
|
t = (t - 0x0101010101010101) & ~t & 0x8080808080808080;
|
|
if (t != 0)
|
|
{
|
|
t &= (ulong)-(long)t;
|
|
return p + dbj8[t * 0x07EDD5E59A4E28C2 >> 58];
|
|
}
|
|
}
|
|
|
|
for (pEnd += c & 7; p < pEnd; p++)
|
|
if (src[p] == v)
|
|
return p;
|
|
|
|
return -1;
|
|
}
|
|
|
|
readonly static sbyte[] dbj8 =
|
|
{
|
|
7, -1, -1, -1, -1, 5, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, 3, -1, -1, -1, -1, -1, -1, 1, -1, 2, 0, -1, -1,
|
|
};
|
|
|
|
public static unsafe long find(byte[] byteArray, byte byteToFind, uint start, uint length)
|
|
{
|
|
return Array.IndexOf(byteArray, byteToFind, (int)start, (int)length);
|
|
return IndexOfByte(byteArray, byteToFind, start, length);
|
|
}
|
|
|
|
public static ulong compressionSearch(byte[] src, uint pos, int max_len, uint range, uint src_end)
|
|
{
|
|
ulong found_len = 1;
|
|
ulong found = 0;
|
|
|
|
long search;
|
|
uint cmp_end, cmp1, cmp2;
|
|
byte c1;
|
|
uint len;
|
|
|
|
if (pos + 2 < src_end)
|
|
{
|
|
search = ((long)pos - (long)range);
|
|
if (search < 0)
|
|
search = 0;
|
|
|
|
cmp_end = (uint)(pos + max_len);
|
|
if (cmp_end > src_end)
|
|
cmp_end = src_end;
|
|
|
|
c1 = src[pos];
|
|
while (search < pos)
|
|
{
|
|
search = find(src, c1, (uint)search, (uint)(pos - search));
|
|
if (search < 0)
|
|
break;
|
|
|
|
cmp1 = (uint)(search + 1);
|
|
cmp2 = pos + 1;
|
|
|
|
while (cmp2 < cmp_end && src[cmp1] == src[cmp2])
|
|
{
|
|
cmp1++; cmp2++;
|
|
}
|
|
|
|
len = cmp2 - pos;
|
|
if (found_len < len)
|
|
{
|
|
found_len = len;
|
|
found = (uint)search;
|
|
if ((long)found_len == max_len)
|
|
break;
|
|
}
|
|
|
|
search++;
|
|
}
|
|
}
|
|
|
|
return (ulong)((found << 32) | found_len);
|
|
}
|
|
}
|
|
}
|