Worms4Editor/LibXom/Data/XomCompressor.cs

117 lines
3.8 KiB
C#

using LibXom.Exceptions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LibXom.Data
{
public class XomCompressor
{
internal static int readCompressedIntFromStream(MemoryStream 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 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 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.");
}
private static int pow(int val, int pow)
{
return Convert.ToInt32(Math.Pow(val, pow));
}
// TODO: Write the inverse of this; "CompressInt()"
public static int DecompressInt(int compressedInt)
{
int b1 = (compressedInt & 0xFF);
int b2 = (compressedInt >> 8) & 0xFF;
int b3 = (compressedInt >> 16) & 0xFF;
int b4 = (compressedInt >> 24) & 0xFF;
return (b4 * pow(0x80, 3)) + ((b3 % 0x80) * pow(0x80, 0x2)) + ((b2 % 0x80) * pow(0x80, 1)) + (b1 % 0x80);
}
public static int NextCompressedInterger(int compressedInt)
{
// Some parts of XOM follow a particular pattern,
// 0x7b, 0x7d, 0x7e, 0x7f, 0x180
// 0x1fb, 0x1fd, 0x1fe, 0x1ff 0x280
// 0x2fb, 0x2fd, 0x2fe, 0x2ff 0x380
// This function will take any given int,
// and give you the next following this sequence.
// This is done by XOM to "compress" intergers,
// you can read a int and, if its > 0x7f you know theres another byte to be read
// otherwise you've read the entire interger, and can read the next one
// add 1 to current int
int nextId = compressedInt + 1;
int byteCount = getNumberByteCount(nextId);
// if its 0, just return 0
if (nextId <= 0) return nextId;
// if its a multiple of 0x80..
if (nextId % 0x8000 == 0)
{
int div = nextId / 0x8000;
// check if that divisor is also divisible by 2
if (div % 0x2 == 0)
nextId += (0x8000); // add 0x8000
}
if (nextId % 0x80 == 0)
{
int div = nextId / 0x80;
// check if that divisor is also divisible by 2
if (div % 0x2 == 0)
nextId += (0x80); // add 0x80
}
byte[] buffer = BitConverter.GetBytes(nextId);
for(int i = 0; i < 4; i++)
{
if (buffer[i] > 0x7F) continue;
if (buffer[i] == 0) buffer[i]++;
break;
}
nextId = BitConverter.ToInt32(buffer);
return nextId;
}
}
}