Implement Compression
This commit is contained in:
parent
b3b07228a9
commit
53ffd4eeea
|
@ -1,7 +1,9 @@
|
||||||
using LibXom.Exceptions;
|
using LibXom.Exceptions;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -9,6 +11,15 @@ namespace LibXom.Data
|
||||||
{
|
{
|
||||||
public class XomCompressor
|
public class XomCompressor
|
||||||
{
|
{
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
|
||||||
internal static int readCompressedIntFromStream(MemoryStream ms)
|
internal static int readCompressedIntFromStream(MemoryStream ms)
|
||||||
{
|
{
|
||||||
|
@ -24,6 +35,21 @@ namespace LibXom.Data
|
||||||
|
|
||||||
return DecompressInt(BitConverter.ToInt32(buffer));
|
return DecompressInt(BitConverter.ToInt32(buffer));
|
||||||
}
|
}
|
||||||
|
internal static byte[] compressBuffer(int[] input)
|
||||||
|
{
|
||||||
|
using(MemoryStream ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
foreach(int enc in input)
|
||||||
|
{
|
||||||
|
int c = CompressInt(enc);
|
||||||
|
int sz = getNumberByteCount(c);
|
||||||
|
byte[] buffer = BitConverter.GetBytes(c);
|
||||||
|
ms.Write(buffer, 0x00, sz);
|
||||||
|
}
|
||||||
|
ms.Seek(0, SeekOrigin.Begin);
|
||||||
|
return ms.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
internal static int[] decompressBuffer(byte[] input)
|
internal static int[] decompressBuffer(byte[] input)
|
||||||
{
|
{
|
||||||
List<int> decompressedData = new List<int>();
|
List<int> decompressedData = new List<int>();
|
||||||
|
@ -39,78 +65,53 @@ namespace LibXom.Data
|
||||||
}
|
}
|
||||||
return decompressedData.ToArray();
|
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)
|
private static int pow(int val, int pow)
|
||||||
{
|
{
|
||||||
return Convert.ToInt32(Math.Pow(val, pow));
|
return Convert.ToInt32(Math.Pow(val, pow));
|
||||||
}
|
}
|
||||||
// TODO: Write the inverse of this; "CompressInt()"
|
private static int floor(double val)
|
||||||
public static int DecompressInt(int compressedInt)
|
|
||||||
{
|
{
|
||||||
int b1 = (compressedInt & 0xFF);
|
return Convert.ToInt32(Math.Floor(val));
|
||||||
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)
|
private static int minusButNot0(int val, int minus)
|
||||||
{
|
{
|
||||||
// Some parts of XOM follow a particular pattern,
|
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;
|
||||||
|
|
||||||
// 0x7b, 0x7d, 0x7e, 0x7f, 0x180
|
// calculate full number from bytes
|
||||||
// 0x1fb, 0x1fd, 0x1fe, 0x1ff 0x280
|
return (b4 * pow(0x80, 3)) + ((b3 % 0x80) * pow(0x80, 0x2)) + ((b2 % 0x80) * pow(0x80, 0x1)) + (b1 % 0x80);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,13 @@ namespace LibXom.Data
|
||||||
private string typeBelongs;
|
private string typeBelongs;
|
||||||
internal byte[] data;
|
internal byte[] data;
|
||||||
|
|
||||||
|
internal XomContainer(XomFile fromFile, string fromType, byte[] data)
|
||||||
|
{
|
||||||
|
fileBelongs = fromFile;
|
||||||
|
typeBelongs = fromType;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
public XomType Type
|
public XomType Type
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -34,6 +41,48 @@ namespace LibXom.Data
|
||||||
{
|
{
|
||||||
this.Type.ReplaceContainerData(this, data);
|
this.Type.ReplaceContainerData(this, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compress method, convert bytes to int array, then to compressed byte array.
|
||||||
|
public byte[] Compress(byte[] bytes)
|
||||||
|
{
|
||||||
|
return this.Compress(byteArrayToIntArray(bytes));
|
||||||
|
}
|
||||||
|
// Compress method, convert nums to compressed byte array.
|
||||||
|
public byte[] Compress(int[] nums)
|
||||||
|
{
|
||||||
|
byte[] buffer = XomCompressor.compressBuffer(nums);
|
||||||
|
byte[] compressedData = new byte[buffer.Length + 3];
|
||||||
|
Array.ConstrainedCopy(buffer, 0, compressedData, 3, compressedData.Length);
|
||||||
|
return compressedData;
|
||||||
|
}
|
||||||
|
public void CompressAndUpdate(byte[] bytes)
|
||||||
|
{
|
||||||
|
this.SetData(this.Compress(bytes));
|
||||||
|
}
|
||||||
|
public void CompressAndUpdate(int[] nums)
|
||||||
|
{
|
||||||
|
this.SetData(this.Compress(nums));
|
||||||
|
}
|
||||||
|
// Decompress methods, decompress all data, (eg for WeaponFactoryCollective, or StoredStatsCollective)
|
||||||
|
public int[] Decompress()
|
||||||
|
{
|
||||||
|
byte[] compressedData = new byte[data.Length - 3];
|
||||||
|
Array.ConstrainedCopy(data, 3, compressedData, 0, compressedData.Length);
|
||||||
|
return XomCompressor.decompressBuffer(compressedData);
|
||||||
|
}
|
||||||
|
// Decompress to a byte array.
|
||||||
|
public byte[] DecompressToBytes()
|
||||||
|
{
|
||||||
|
return intArrayToByteArray(this.Decompress());
|
||||||
|
}
|
||||||
|
private int[] byteArrayToIntArray(byte[] byteArray)
|
||||||
|
{
|
||||||
|
int[] intArray = new int[byteArray.Length / 4];
|
||||||
|
for (int i = 0; i < intArray.Length; i++)
|
||||||
|
intArray[i] = BitConverter.ToInt32(byteArray, i * 4);
|
||||||
|
return intArray;
|
||||||
|
}
|
||||||
|
|
||||||
private byte[] intArrayToByteArray(int[] intArray)
|
private byte[] intArrayToByteArray(int[] intArray)
|
||||||
{
|
{
|
||||||
using (MemoryStream ms = new MemoryStream())
|
using (MemoryStream ms = new MemoryStream())
|
||||||
|
@ -48,23 +97,5 @@ namespace LibXom.Data
|
||||||
return ms.ToArray();
|
return ms.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] Decompress()
|
|
||||||
{
|
|
||||||
byte[] compressedData = new byte[data.Length - 3];
|
|
||||||
Array.ConstrainedCopy(data, 3, compressedData, 0, compressedData.Length);
|
|
||||||
return XomCompressor.decompressBuffer(compressedData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] DecompressToBytes()
|
|
||||||
{
|
|
||||||
return intArrayToByteArray(this.Decompress());
|
|
||||||
}
|
|
||||||
internal XomContainer(XomFile fromFile, string fromType, byte[] data)
|
|
||||||
{
|
|
||||||
fileBelongs = fromFile;
|
|
||||||
typeBelongs = fromType;
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,13 +10,7 @@ namespace Worms4Editor
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
/* int id = 0;
|
int id = 0;
|
||||||
for (int i = 0; i < 0x7FFFFF; i++) {
|
|
||||||
int d = XomCompressor.DecompressInt(id);
|
|
||||||
if (d != i) Console.WriteLine("FAIL; " + i.ToString("X") + " id " + id.ToString("X") + " d " + d.ToString("X"));
|
|
||||||
else Console.WriteLine("PASS; " + i.ToString("X") + " id " + id.ToString("X") + " d " + d.ToString("X"));
|
|
||||||
id = XomCompressor.NextCompressedInterger(id);
|
|
||||||
} */
|
|
||||||
XomFile xfile = XomReader.ReadXomFile(@"Original.xom");
|
XomFile xfile = XomReader.ReadXomFile(@"Original.xom");
|
||||||
//XomFile ps2file = XomReader.ReadXomFile(@"ps2.xom");
|
//XomFile ps2file = XomReader.ReadXomFile(@"ps2.xom");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue