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 decompressedData = new List(); 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; } } }