From 53ffd4eeea1518b5c7cbc2cce8580c7a4189c3bf Mon Sep 17 00:00:00 2001
From: Li
Date: Sun, 8 Jan 2023 18:54:06 -0800
Subject: [PATCH] Implement Compression
---
LibXom/Data/XomCompressor.cs | 127 ++++++++++++++++++-----------------
LibXom/Data/XomContainer.cs | 67 +++++++++++++-----
Worms4Editor/Program.cs | 8 +--
3 files changed, 114 insertions(+), 88 deletions(-)
diff --git a/LibXom/Data/XomCompressor.cs b/LibXom/Data/XomCompressor.cs
index 1b142ee..f1fb11b 100644
--- a/LibXom/Data/XomCompressor.cs
+++ b/LibXom/Data/XomCompressor.cs
@@ -1,7 +1,9 @@
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;
@@ -9,6 +11,15 @@ namespace LibXom.Data
{
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)
{
@@ -24,6 +35,21 @@ namespace LibXom.Data
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)
{
List decompressedData = new List();
@@ -39,78 +65,53 @@ namespace LibXom.Data
}
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)
+ private static int floor(double val)
{
- 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);
+ return Convert.ToInt32(Math.Floor(val));
}
- 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
- // 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;
+ // calculate full number from bytes
+ return (b4 * pow(0x80, 3)) + ((b3 % 0x80) * pow(0x80, 0x2)) + ((b2 % 0x80) * pow(0x80, 0x1)) + (b1 % 0x80);
}
}
}
diff --git a/LibXom/Data/XomContainer.cs b/LibXom/Data/XomContainer.cs
index e900d7f..5ac5ac5 100644
--- a/LibXom/Data/XomContainer.cs
+++ b/LibXom/Data/XomContainer.cs
@@ -12,6 +12,13 @@ namespace LibXom.Data
private string typeBelongs;
internal byte[] data;
+ internal XomContainer(XomFile fromFile, string fromType, byte[] data)
+ {
+ fileBelongs = fromFile;
+ typeBelongs = fromType;
+ this.data = data;
+ }
+
public XomType Type
{
get
@@ -34,6 +41,48 @@ namespace LibXom.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)
{
using (MemoryStream ms = new MemoryStream())
@@ -48,23 +97,5 @@ namespace LibXom.Data
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;
- }
}
}
diff --git a/Worms4Editor/Program.cs b/Worms4Editor/Program.cs
index d08a8a7..67f4abc 100644
--- a/Worms4Editor/Program.cs
+++ b/Worms4Editor/Program.cs
@@ -10,13 +10,7 @@ namespace Worms4Editor
{
static void Main(string[] args)
{
- /* 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);
- } */
+ int id = 0;
XomFile xfile = XomReader.ReadXomFile(@"Original.xom");
//XomFile ps2file = XomReader.ReadXomFile(@"ps2.xom");