146 lines
4.6 KiB
C#
146 lines
4.6 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));
|
|
}
|
|
public static int[] Decompress(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 decompressInt16(int compressedInt)
|
|
{
|
|
int mul = (compressedInt >> 8);
|
|
int add = (compressedInt & 0xFF);
|
|
|
|
return (mul * 0x80) + (add % 0x80);
|
|
}
|
|
private static int decompressInt24(int compressedInt)
|
|
{
|
|
int mul = (compressedInt >> 8) & 0xFF;
|
|
int mul2 = (compressedInt >> 16);
|
|
|
|
int add = (compressedInt & 0xFF);
|
|
|
|
return ((mul2 * 0x80) * mul) + (add % 0x80);
|
|
}
|
|
|
|
private static int decompressInt32(int compressedInt)
|
|
{
|
|
int mul = (compressedInt >> 8) & 0xFF;
|
|
int mul2 = (compressedInt >> 16) & 0xFF;
|
|
int mul3 = (compressedInt >> 24);
|
|
|
|
int add = (compressedInt & 0xFF);
|
|
|
|
return (((mul3 * 0x80) * mul2 * 0x80) * mul * 0x80) + (add % 0x80);
|
|
}
|
|
|
|
public static int DecompressInt(int compressedInt)
|
|
{
|
|
switch (getNumberByteCount(compressedInt))
|
|
{
|
|
case 1:
|
|
return compressedInt;
|
|
case 2:
|
|
return decompressInt16(compressedInt);
|
|
case 3:
|
|
return decompressInt24(compressedInt);
|
|
case 4:
|
|
return decompressInt32(compressedInt);
|
|
default:
|
|
throw new XomException("Number is too large or too small.");
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|