Worms4Editor/LibXom/Data/XomCompressor.cs

122 lines
3.9 KiB
C#

using LibXom.Exceptions;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Intrinsics;
using System.Text;
using System.Threading.Tasks;
namespace LibXom.Data
{
public class XomCompressor
{
internal static int getNumberByteCount(int num)
{
uint unum = Convert.ToUInt32(num);
if (unum <= 0xFF) return 1;
if (unum <= 0xFFFF) return 2;
if (unum <= 0xFFFFFF) return 3;
if (unum <= 0xFFFFFFFF) return 4;
throw new XomException("Number is too large or too small.");
}
public static int ReadCompressedIntFromStream(Stream ms)
{
byte[] buffer = new byte[4];
for (int i = 0; i < 4; i++)
{
int b = ms.ReadByte();
buffer[i] = Convert.ToByte(b);
if (b > 0x7F) continue;
else break;
}
return DecompressInt(BitConverter.ToInt32(buffer));
}
internal static void compressIntAndWriteToStream(Stream s, int uncompressedInt)
{
int c = XomCompressor.CompressInt(uncompressedInt);
int sz = XomCompressor.getNumberByteCount(c);
byte[] buffer = BitConverter.GetBytes(c);
s.Write(buffer, 0x00, sz);
}
internal static byte[] compressBuffer(int[] input)
{
using(MemoryStream ms = new MemoryStream())
{
foreach(int enc in input)
{
compressIntAndWriteToStream(ms, enc);
}
ms.Seek(0, SeekOrigin.Begin);
return ms.ToArray();
}
}
internal static int[] decompressBuffer(byte[] input)
{
List<int> decompressedData = new List<int>();
using (MemoryStream inputStream = new MemoryStream(input))
{
while (inputStream.Position < inputStream.Length)
{
int n = ReadCompressedIntFromStream(inputStream);
decompressedData.Add(n);
}
}
return decompressedData.ToArray();
}
private static int pow(int val, int pow)
{
return Convert.ToInt32(Math.Pow(val, pow));
}
private static int floor(double val)
{
return Convert.ToInt32(Math.Floor(val));
}
private static int minusButNot0(int val, int minus)
{
int nval = val - minus;
if (nval < 0) nval = 0;
return nval;
}
public static int CompressInt(int decompressedInt)
{
int val = decompressedInt;
// Calculate each byte
int b4 = floor(val / pow(0x80, 3));
val -= minusButNot0(b4, 1) * pow(0x80, 3);
int b3 = floor(val / pow(0x80, 2));
val -= minusButNot0(b3, 1) * pow(0x80, 2);
int b2 = floor(val / pow(0x80, 1));
val -= minusButNot0(b2, 1) * pow(0x80, 1);
int b1 = val;
// rebuild int from bytes
int compressedInt = 0;
compressedInt = (compressedInt << 8) | b4;
compressedInt = (compressedInt << 8) | b3;
compressedInt = (compressedInt << 8) | b2;
compressedInt = (compressedInt << 8) | b1;
return compressedInt;
}
public static int DecompressInt(int compressedInt)
{
// Extract each byte from the int
int b1 = (compressedInt >> 0) & 0xFF;
int b2 = (compressedInt >> 8) & 0xFF;
int b3 = (compressedInt >> 16) & 0xFF;
int b4 = (compressedInt >> 24) & 0xFF;
// calculate full number from bytes
return (b4 * pow(0x80, 3)) + ((b3 % 0x80) * pow(0x80, 0x2)) + ((b2 % 0x80) * pow(0x80, 0x1)) + (b1 % 0x80);
}
}
}