diff --git a/PCK-Studio/Classes/FileTypes/GRFFile.cs b/PCK-Studio/Classes/FileTypes/GRFFile.cs deleted file mode 100644 index 5abf9d2e..00000000 --- a/PCK-Studio/Classes/FileTypes/GRFFile.cs +++ /dev/null @@ -1,283 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace PckStudio.Classes.FileTypes -{ - public class GRFFile - { - public static readonly string[] ValidGameRules = new string[] - { - "MapOptions", - "ApplySchematic", - "GenerateStructure", - "GenerateBox", - "PlaceBlock", - "PlaceContainer", - "PlaceSpawner", - "BiomeOverride", - "StartFeature", - "AddItem", - "AddEnchantment", - "WeighedTreasureItem", - "RandomItemSet", - "DistributeItems", - "WorldPosition", - "LevelRules", - "NamedArea", - "ActiveChunkArea", - "TargetArea", - "ScoreRing", - "ThermalArea", - "PlayerBoundsVolume", - "Killbox", - "BlockLayer", - "UseBlock", - "CollectItem", - "CompleteAll", - "UpdatePlayer", - "OnGameStartSpawnPositions", - "OnInitialiseWorld", - "SpawnPositionSet", - "PopulateContainer", - "DegradationSequence", - "RandomDissolveDegrade", - "DirectionalDegrade", - "GrantPermissions", - "AllowIn", - "LayerGeneration", - "LayerAsset", - "AnyCombinationOf", - "CombinationDefinition", - "Variations", - "BlockDef", - "LayerSize", - "UniformSize", - "RandomizeSize", - "LinearBlendSize", - "LayerShape", - "BasicShape", - "StarShape", - "PatchyShape", - "RingShape", - "SpiralShape", - "LayerFill", - "BasicLayerFill", - "CurvedLayerFill", - "WarpedLayerFill", - "LayerTheme", - "NullTheme", - "FilterTheme", - "ShaftsTheme", - "BasicPatchesTheme", - "BlockStackTheme", - "RainbowTheme", - "TerracottaTheme", - "FunctionPatchesTheme", - "SimplePatchesTheme", - "CarpetTrapTheme", - "MushroomBlockTheme", - "TextureTheme", - "SchematicTheme", - "BlockCollisionException", - "Powerup", - "Checkpoint", - "CustomBeacon", - "ActiveViewArea", - }; - - public readonly GameRule Root = null; - - public int Crc => _crc; - public bool IsWorld => _isWorld; - public CompressionType CompressionLevel => _compressionLevel; - - private int _crc = 0; - private bool _isWorld = false; - private CompressionType _compressionLevel = CompressionType.None; - - public enum CompressionType : byte - { - None = 0, - Compressed = 1, - CompressedRle = 2, - CompressedRleCrc = 3, - } - - /// - /// Initializes a new GRFFile as a non-world grf file - /// - public GRFFile() : this(-1, false) - {} - - public GRFFile(int crc, bool isWolrd) : this(crc, isWolrd, CompressionType.None) - {} - public GRFFile(int crc, bool isWolrd, CompressionType compressionLevel) - { - Root = new GameRule("__ROOT__", null); - _compressionLevel = compressionLevel; - _crc = crc; - _isWorld = isWolrd; - } - - public class GameRule - { - /// Contains all valid Parameter names - public static readonly string[] ValidParameters = new string[] - { - "plus_x", - "minus_x", - "plus_z", - "minus_z", - "omni_plus_x", - "omni_minus_x", - "omni_plus_z", - "omni_minus_z", - "none", - "plus_y", - "minus_y", - "plus_x", - "minus_x", - "plus_z", - "minus_z", - "descriptionName", - "promptName", - "dataTag", - "enchantmentId", - "enchantmentLevel", - "itemId", - "quantity", - "auxValue", - "slot", - "name", - "food", - "health", - "blockId", - "useCoords", - "seed", - "flatworld", - "filename", - "rot", - "data", - "block", - "entity", - "facing", - "edgeBlock", - "fillBlock", - "skipAir", - "x", - "x0", - "x1", - "y", - "y0", - "y1", - "z", - "z0", - "z1", - "chunkX", - "chunkZ", - "yRot", - "xRot", - "spawnX", - "spawnY", - "spawnZ", - "orientation", - "dimension", - "topblockId", - "biomeId", - "feature", - "minCount", - "maxCount", - "weight", - "id", - "probability", - "method", - "hasBeenInCreative", - "cloudHeight", - "fogDistance", - "dayTime", - "target", - "speed", - "dir", - "type", - "pass", - "for", - "random", - "blockAux", - "size", - "scale", - "freq", - "func", - "upper", - "lower", - "dY", - "thickness", - "points", - "holeSize", - "variant", - "startHeight", - "pattern", - "colour", - "primary", - "laps", - "liftForceModifier", - "staticLift", - "targetHeight", - "speedBoost", - "boostDirection", - "condition_type", - "condition_value_0", - "condition_value_1", - "beam_length", - }; - - public string Name { get; set; } = string.Empty; - - public GameRule Parent { get; } = null; - public Dictionary Parameters { get; } = new Dictionary(); - public List ChildRules { get; } = new List(); - - public GameRule(string name, GameRule parent) - { - Name = name; - Parent = parent; - } - - public GameRule AddRule(string gameRuleName) => AddRule(gameRuleName, false); - - /// Adds a new gamerule - /// Game rule to add - /// Wether to check the given game rule - /// The Added GRFTag - public GameRule AddRule(string gameRuleName, bool validate) - { - if (validate && !ValidGameRules.Contains(gameRuleName)) return null; - var tag = new GameRule(gameRuleName, this); - ChildRules.Add(tag); - return tag; - } - - public GameRule AddRule(string gameRuleName, params KeyValuePair[] parameters) - { - var tag = AddRule(gameRuleName); // should never return null unless its called with the validate bool set to true - foreach(var param in parameters) - { - tag.Parameters[param.Key] = param.Value; - } - return tag; - } - } - - public void AddGameRules(IEnumerable gameRules) => Root.ChildRules.AddRange(gameRules); - - public GameRule AddRule(string gameRuleName) - => AddRule(gameRuleName, false); - - public GameRule AddRule(string gameRuleName, bool validate) - => Root.AddRule(gameRuleName, validate); - - public GameRule AddRule(string gameRuleName, params KeyValuePair[] parameters) - => Root.AddRule(gameRuleName, parameters); - } -} diff --git a/PCK-Studio/Classes/IO/GRF/GRFFileReader.cs b/PCK-Studio/Classes/IO/GRF/GRFFileReader.cs deleted file mode 100644 index 953b2108..00000000 --- a/PCK-Studio/Classes/IO/GRF/GRFFileReader.cs +++ /dev/null @@ -1,140 +0,0 @@ -using PckStudio.Classes.FileTypes; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; -using PckStudio.Classes.Utils; -using System.Diagnostics; - -namespace PckStudio.Classes.IO.GRF -{ - internal class GRFFileReader : StreamDataReader - { - private IList StringLookUpTable; - private GRFFile _file; - - public static GRFFile Read(Stream stream) - { - return new GRFFileReader().ReadFromStream(stream); - } - - private GRFFileReader() : base(false) - { } - - protected override GRFFile ReadFromStream(Stream stream) - { - stream = ReadHeader(stream); - ReadBody(stream); - return _file; - } - - private Stream ReadHeader(Stream stream) - { - int x = ReadShort(stream); - if (((x >> 31) | x) == 0) - { - // 14 bools ?... - ReadBytes(stream, 14); - return stream; - } - - GRFFile.CompressionType compression_type = (GRFFile.CompressionType)stream.ReadByte(); - int crc = ReadInt(stream); - int byte1 = stream.ReadByte(); - int byte2 = stream.ReadByte(); - int byte3 = stream.ReadByte(); - int byte4 = stream.ReadByte(); - if (byte4 > 0) - { - compression_type = (GRFFile.CompressionType)byte4; - } - _file = new GRFFile(crc, byte4 > 0, compression_type); - - if (compression_type == GRFFile.CompressionType.None && byte4 == 0) - return stream; - - int buf_size = ReadInt(stream); - var new_stream = stream; - if (byte4 != 0) - { - new_stream = new MemoryStream(ReadBytes(stream, buf_size)); - buf_size = ReadInt(new_stream); - } - else - { - ReadInt(stream); // ignored cuz rest of data is compressed - } - var decompressed_stream = DecompressZLX(new_stream); - new_stream.Dispose(); - if (compression_type > GRFFile.CompressionType.Compressed) - { - byte[] data = ReadBytes(decompressed_stream, buf_size); - byte[] decoded_data = RLE.Decode(data).ToArray(); - decompressed_stream.Dispose(); - decompressed_stream = new MemoryStream(decoded_data); - } - - if (byte4 != 0) - ReadBytes(decompressed_stream, 23); - - return decompressed_stream; - } - - private void ReadBody(Stream stream) - { - ReadStringLookUpTable(stream); - string Name = GetString(stream); - Debug.WriteLine(string.Format("Root Name: {0}", Name), category: nameof(GRFFile)); - ReadGameRuleHierarchy(stream, _file.Root); - } - - private Stream DecompressZLX(Stream compressedStream) - { - Stream outputstream = new MemoryStream(); - using (var inputStream = new InflaterInputStream(compressedStream)) - { - inputStream.IsStreamOwner = false; - inputStream.CopyTo(outputstream); - outputstream.Position = 0; - }; - return outputstream; - } - - private void ReadStringLookUpTable(Stream stream) - { - int tableSize = ReadInt(stream); - StringLookUpTable = new List(tableSize); - for (int i = 0; i < tableSize; i++) - { - string s = ReadString(stream); - StringLookUpTable.Add(s); - } - } - - private void ReadGameRuleHierarchy(Stream stream, GRFFile.GameRule parentRule) - { - _ = parentRule ?? throw new ArgumentNullException(nameof(parentRule)); - int count = ReadInt(stream); - for (int i = 0; i < count; i++) - { - (string Name, int Count) parameter = (GetString(stream), ReadInt(stream)); - var rule = parentRule.AddRule(parameter.Name); - for (int j = 0; j < parameter.Count; j++) - { - rule.Parameters.Add(GetString(stream), ReadString(stream)); - } - ReadGameRuleHierarchy(stream, rule); - } - } - - private string GetString(Stream stream) => StringLookUpTable[ReadInt(stream)]; - - private string ReadString(Stream stream) - { - short stringLength = ReadShort(stream); - return ReadString(stream, stringLength, Encoding.ASCII); - } - } -} diff --git a/PCK-Studio/Classes/IO/GRF/GRFFileWriter.cs b/PCK-Studio/Classes/IO/GRF/GRFFileWriter.cs deleted file mode 100644 index 4d4127a9..00000000 --- a/PCK-Studio/Classes/IO/GRF/GRFFileWriter.cs +++ /dev/null @@ -1,159 +0,0 @@ -using PckStudio.Classes.FileTypes; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using ICSharpCode.SharpZipLib.Zip.Compression.Streams; -using PckStudio.Classes.Utils.grf; -using PckStudio.Classes.Utils; - -namespace PckStudio.Classes.IO.GRF -{ - internal class GRFFileWriter : StreamDataWriter - { - private readonly GRFFile _grfFile; - private List StringLookUpTable; - - private GRFFile.CompressionType _compressionType; - - public static void Write(in Stream stream, GRFFile grfFile, GRFFile.CompressionType compressionType) - { - new GRFFileWriter(grfFile, compressionType).WriteToStream(stream); - } - - private GRFFileWriter(GRFFile grfFile, GRFFile.CompressionType compressionType) : base(false) - { - _compressionType = compressionType; - if (grfFile.IsWorld) - throw new NotImplementedException("World grf saving is currently unsupported"); - _grfFile = grfFile; - StringLookUpTable = new List(); - PrepareLookUpTable(_grfFile.Root, StringLookUpTable); - } - - private void PrepareLookUpTable(GRFFile.GameRule rule, List LUT) - { - if (!LUT.Contains(rule.Name)) LUT.Add(rule.Name); - rule.ChildRules.ForEach(subRule => PrepareLookUpTable(subRule, LUT)); - foreach (var param in rule.Parameters) - if (!LUT.Contains(param.Key)) LUT.Add(param.Key); - } - - protected override void WriteToStream(Stream stream) - { - WriteHeader(stream); - using (var uncompressed_stream = new MemoryStream()) - { - WriteBody(uncompressed_stream); - HandleCompression(stream, uncompressed_stream); - } - } - - private void HandleCompression(Stream destinationStream, MemoryStream sourceStream) - { - byte[] _buffer = sourceStream.ToArray(); - int _original_length = _buffer.Length; - - if (_compressionType >= GRFFile.CompressionType.CompressedRle) - _buffer = CompressRle(_buffer); - if (_compressionType >= GRFFile.CompressionType.Compressed) - { - _buffer = CompressZib(_buffer); - WriteInt(destinationStream, _original_length); - WriteInt(destinationStream, _buffer.Length); - } - if (_compressionType >= GRFFile.CompressionType.CompressedRleCrc) - MakeAndWriteCrc(destinationStream, _buffer); - WriteBytes(destinationStream, _buffer); - return; - } - - private byte[] CompressZib(byte[] buffer) - { - byte[] result; - var outputStream = new MemoryStream(); // Stream gets Disposed in DeflaterOutputStream - using (var deflateStream = new DeflaterOutputStream(outputStream)) - { - WriteBytes(deflateStream, buffer); - deflateStream.Flush(); - deflateStream.Finish(); - outputStream.Position = 0; - result = outputStream.ToArray(); - } - return result; - } - - private byte[] CompressRle(byte[] buffer) => RLE.Encode(buffer).ToArray(); - - private void MakeAndWriteCrc(Stream stream, byte[] data) - { - uint crc = CRC32.CRC(data); - if (crc != _grfFile.Crc) // no writting needed if there is no change - { - stream.Position = 3; - WriteUInt(stream, crc); - stream.Seek(0, SeekOrigin.End); // reset to the end of the stream - } - } - - private void WriteHeader(Stream stream) - { - WriteShort(stream, 1); - if (_compressionType < GRFFile.CompressionType.None || - _compressionType > GRFFile.CompressionType.CompressedRleCrc) - throw new ArgumentException(nameof(_compressionType)); - stream.WriteByte((byte)_compressionType); - WriteInt(stream, _grfFile.Crc); - stream.WriteByte(0); - stream.WriteByte(0); - stream.WriteByte(0); - stream.WriteByte(0); // <- used in world grf - } - - private void WriteBody(Stream stream) - { - WriteTagLookUpTable(stream); - SetString(stream, _grfFile.Root.Name); - WriteInt(stream, _grfFile.Root.ChildRules.Count); - WriteGameRuleHierarchy(stream, _grfFile.Root); - } - - private void WriteTagLookUpTable(Stream stream) - { - WriteInt(stream, StringLookUpTable.Count); - StringLookUpTable.ForEach( s => WriteString(stream, s) ); - } - - private void WriteGameRuleHierarchy(Stream stream, GRFFile.GameRule rule) - { - foreach (var subRule in rule.ChildRules) - { - SetString(stream, subRule.Name); - WriteInt(stream, subRule.Parameters.Count); - foreach (var param in subRule.Parameters) WriteParameter(stream, param); - WriteInt(stream, subRule.ChildRules.Count); - WriteGameRuleHierarchy(stream, subRule); - } - } - - private void WriteParameter(Stream stream, KeyValuePair param) - { - SetString(stream, param.Key); - WriteString(stream, param.Value); - } - - private void SetString(Stream stream, string s) - { - int i = StringLookUpTable.IndexOf(s); - if (i == -1) throw new Exception(nameof(s)); - WriteInt(stream, i); - } - - private void WriteString(Stream stream, string s) - { - WriteShort(stream, (short)s.Length); - WriteString(stream, s, Encoding.ASCII); - } - } -} diff --git a/PCK-Studio/Classes/Utils/RLE.cs b/PCK-Studio/Classes/Utils/RLE.cs deleted file mode 100644 index 2a16135e..00000000 --- a/PCK-Studio/Classes/Utils/RLE.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -namespace PckStudio.Classes.Utils; - -///! Credits -///! - -/// -/// Provides the RLE codec for any integer data type. -/// -/// The data's type. Must be an integer type or an ArgumentException will be thrown -static class RLE where T : struct, IConvertible -{ - /// - /// This is the marker that identifies a compressed run - /// - private static T rleMarker; - - /// - /// A run can be at most as long as the marker - 1 - /// - private static ulong maxLength; - - static RLE() - { - GetMaxValues(); - } - - /// - /// RLE-Encodes a data set. - /// - /// The data to encode - /// Encoded data - public static IEnumerable Encode(IEnumerable data) - { - var enumerator = data.GetEnumerator(); - - if (!enumerator.MoveNext()) - yield break; - - var firstRunValue = enumerator.Current; - ulong runLength = 1; - while (enumerator.MoveNext()) - { - var currentValue = enumerator.Current; - // if the current value is the value of the current run, don't yield anything, - // just extend the run - if (currentValue.Equals(firstRunValue)) - runLength++; - else - { - // the current value is different from the current run - // yield what we have so far - foreach (var item in MakeRun(firstRunValue, runLength)) - yield return item; - - // and reset the run - firstRunValue = currentValue; - runLength = 1; - } - // if there are very many identical values, don't exceed the max length - if (runLength > maxLength) - { - foreach (var item in MakeRun(firstRunValue, maxLength)) - yield return item; - runLength -= maxLength; - } - } - //yield everything that has been buffered - foreach (var item in MakeRun(firstRunValue, runLength)) - yield return item; - } - - /// - /// Decodes RLE-encoded data - /// - /// RLE-encoded data - /// The original data - public static IEnumerable Decode(IEnumerable data) - { - var enumerator = data.GetEnumerator(); - if (!enumerator.MoveNext()) - yield break; - - do - { - var value = enumerator.Current; - if (!value.Equals(rleMarker)) - { - //an ordinary value - yield return value; - } - else - { - //might be flag or escape - //examine the next value - if (!enumerator.MoveNext()) - throw new ArgumentException("The provided data is not properly encoded."); - if (enumerator.Current.Equals(rleMarker)) - { - //escaped value - yield return value; - } - else - { - //rle marker - var length = enumerator.Current.ToInt64(CultureInfo.InvariantCulture); - if (!enumerator.MoveNext()) - throw new ArgumentException("The provided data is not properly encoded."); - var val = enumerator.Current; - for (var j = 0; j < length+1; ++j) - yield return val; - } - } - } - while (enumerator.MoveNext()); - } - - private static IEnumerable MakeRun(T value, ulong length) - { - if ((length <= 3 && !value.Equals(rleMarker)) || length <= 1) - { - //don't compress this run, it is just too small - for (ulong i = 0; i < length; i++) - { - yield return value.Equals(rleMarker) ? rleMarker : value; - } - } - else - { - //compressed run - yield return rleMarker; - yield return (T)(dynamic)(length-1); - yield return value; - } - } - - - private static void GetMaxValues() - { - TypeCode typeCode = Type.GetTypeCode(typeof(T)); - switch (typeCode) - { - case TypeCode.Byte: - { - var limit = byte.MaxValue; - rleMarker = __refvalue(__makeref(limit), T); - maxLength = (ulong)(limit - 1); - break; - } - case TypeCode.Char: - { - var limit = char.MaxValue; - rleMarker = __refvalue(__makeref(limit), T); - maxLength = (ulong)(limit - 1); - break; - } - case TypeCode.Int16: - { - var limit = short.MaxValue; - rleMarker = __refvalue(__makeref(limit), T); - maxLength = (ulong)(limit - 1); - break; - } - case TypeCode.Int32: - { - var limit = int.MaxValue; - rleMarker = __refvalue(__makeref(limit), T); - maxLength = (ulong)(limit - 1); - break; - } - case TypeCode.Int64: - { - var limit = long.MaxValue; - rleMarker = __refvalue(__makeref(limit), T); - maxLength = (ulong)(limit - 1); - break; - } - case TypeCode.SByte: - { - var limit = sbyte.MaxValue; - rleMarker = __refvalue(__makeref(limit), T); - maxLength = (ulong)(limit - 1); - break; - } - case TypeCode.UInt16: - { - var limit = ushort.MaxValue; - rleMarker = __refvalue(__makeref(limit), T); - maxLength = (ulong)(limit - 1); - break; - } - case TypeCode.UInt32: - { - var limit = uint.MaxValue; - rleMarker = __refvalue(__makeref(limit), T); - maxLength = (ulong)(limit - 1); - break; - } - case TypeCode.UInt64: - { - var limit = ulong.MaxValue; - rleMarker = __refvalue(__makeref(limit), T); - maxLength = (ulong)(limit - 1); - break; - } - default: - throw new ArgumentException("The provided type parameter is not an integer type"); - } - } -} \ No newline at end of file diff --git a/PCK-Studio/Classes/Utils/grf/CRC32.cs b/PCK-Studio/Classes/Utils/grf/CRC32.cs deleted file mode 100644 index 7fb171f9..00000000 --- a/PCK-Studio/Classes/Utils/grf/CRC32.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace PckStudio.Classes.Utils.grf -{ - public class CRC32 - { - static readonly private byte[] offsets = - { - 0x05, 0x06, 0x07, 0x03, - 0x04, 0x05, 0x06, 0x07, - 0x01, 0x02, 0x06, 0x01, - 0x01, 0x02, 0x03, 0x04, - 0x05, 0x05, 0x06, 0x07, - 0x03, 0x04, 0x05, 0x06, - 0x07, 0x01, 0x02, 0x06, - 0x01, 0x01, 0x02, 0x03, - 0x04, 0x05, 0x05, 0x06, - 0x07, 0x03, 0x04, 0x05, - 0x06, 0x07, 0x07, 0x05, - 0x04, 0x07, 0x05, 0x04, - 0x07, 0x05, 0x04, 0x07, - 0x06, 0x05, 0x04, 0x03, - }; - - static private uint[] CRCTable; - static private bool hasCRCTable = false; - - static void MakeCRCTable() - { - const uint val = 0xedb88320; - CRCTable = new uint[256]; - for (int i = 0; i < 256; i++) - { - uint temp = (uint)i; - - for (int j = 0; j < 8; j++) - { - if ((temp & 1) == 1) - { - temp >>= 1; - temp ^= val; - } - else { temp >>= 1; } - } - CRCTable[i] = temp; - } - hasCRCTable = true; - } - public static uint UpdateCRC(uint _crc, byte[] data) - { - uint crc = _crc; - if (!hasCRCTable) - MakeCRCTable(); - long pos = 0; - long offset = 0; - do - { - var value = data[pos]; - pos += offsets[offset]; - crc = CRCTable[(crc ^ value) & 0xff] ^ crc >> 8; - offset = offset + 1U + ((offset + 1U >> 3) / 7) * -0x38; - } while (pos < data.Length); - return crc; - } - - public static uint CRC(byte[] data) - { - return ~UpdateCRC(0xffffffff, data); - } - } -} diff --git a/PCK-Studio/Classes/Utils/grf/lzxDecoder.cs b/PCK-Studio/Classes/Utils/grf/lzxDecoder.cs deleted file mode 100644 index 272f99ce..00000000 --- a/PCK-Studio/Classes/Utils/grf/lzxDecoder.cs +++ /dev/null @@ -1,785 +0,0 @@ -#region HEADER -/* This file was derived from libmspack - * (C) 2003-2004 Stuart Caie. - * (C) 2011 Ali Scissons. - * - * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted - * by Microsoft Corporation. - * - * This source file is Dual licensed; meaning the end-user of this source file - * may redistribute/modify it under the LGPL 2.1 or MS-PL licenses. - */ -#region LGPL License -/* GNU LESSER GENERAL PUBLIC LICENSE version 2.1 - * LzxDecoder is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License (LGPL) version 2.1 - */ -#endregion -#region MS-PL License -/* - * MICROSOFT PUBLIC LICENSE - * This source code is subject to the terms of the Microsoft Public License (Ms-PL). - * - * Redistribution and use in source and binary forms, with or without modification, - * is permitted provided that redistributions of the source code retain the above - * copyright notices and this file header. - * - * Additional copyright notices should be appended to the list above. - * - * For details, see . - */ -#endregion -/* - * This derived work is recognized by Stuart Caie and is authorized to adapt - * any changes made to lzxd.c in his libmspack library and will still retain - * this dual licensing scheme. Big thanks to Stuart Caie! - * - * DETAILS - * This file is a pure C# port of the lzxd.c file from libmspack, with minor - * changes towards the decompression of XNB files. The original decompression - * software of LZX encoded data was written by Suart Caie in his - * libmspack/cabextract projects, which can be located at - * http://http://www.cabextract.org.uk/ - */ -#endregion - -using System; -using System.Diagnostics; - -namespace Microsoft.Xna.Framework.Content -{ - using System.IO; - - class LzxDecoder - { - public static uint[] position_base = null; - public static byte[] extra_bits = null; - - private LzxState m_state; - - public LzxDecoder(int window) - { - uint wndsize = (uint)(1 << window); - int posn_slots; - - // setup proper exception - if (window < 15 || window > 21) throw new UnsupportedWindowSizeRange(); - - // let's initialise our state - m_state = new LzxState(); - m_state.actual_size = 0; - m_state.window = new byte[wndsize]; - for (int i = 0; i < wndsize; i++) m_state.window[i] = 0xDC; - m_state.actual_size = wndsize; - m_state.window_size = wndsize; - m_state.window_posn = 0; - - /* initialize static tables */ - if (extra_bits == null) - { - extra_bits = new byte[52]; - for (int i = 0, j = 0; i <= 50; i += 2) - { - extra_bits[i] = extra_bits[i + 1] = (byte)j; - if ((i != 0) && (j < 17)) j++; - } - } - if (position_base == null) - { - position_base = new uint[51]; - for (int i = 0, j = 0; i <= 50; i++) - { - position_base[i] = (uint)j; - j += 1 << extra_bits[i]; - } - } - - /* calculate required position slots */ - if (window == 20) posn_slots = 42; - else if (window == 21) posn_slots = 50; - else posn_slots = window << 1; - - m_state.R0 = m_state.R1 = m_state.R2 = 1; - m_state.main_elements = (ushort)(LzxConstants.NUM_CHARS + (posn_slots << 3)); - m_state.header_read = 0; - m_state.frames_read = 0; - m_state.block_remaining = 0; - m_state.block_type = LzxConstants.BLOCKTYPE.INVALID; - m_state.intel_curpos = 0; - m_state.intel_started = 0; - - // yo dawg i herd u liek arrays so we put arrays in ur arrays so u can array while u array - m_state.PRETREE_table = new ushort[(1 << LzxConstants.PRETREE_TABLEBITS) + (LzxConstants.PRETREE_MAXSYMBOLS << 1)]; - m_state.PRETREE_len = new byte[LzxConstants.PRETREE_MAXSYMBOLS + LzxConstants.LENTABLE_SAFETY]; - m_state.MAINTREE_table = new ushort[(1 << LzxConstants.MAINTREE_TABLEBITS) + (LzxConstants.MAINTREE_MAXSYMBOLS << 1)]; - m_state.MAINTREE_len = new byte[LzxConstants.MAINTREE_MAXSYMBOLS + LzxConstants.LENTABLE_SAFETY]; - m_state.LENGTH_table = new ushort[(1 << LzxConstants.LENGTH_TABLEBITS) + (LzxConstants.LENGTH_MAXSYMBOLS << 1)]; - m_state.LENGTH_len = new byte[LzxConstants.LENGTH_MAXSYMBOLS + LzxConstants.LENTABLE_SAFETY]; - m_state.ALIGNED_table = new ushort[(1 << LzxConstants.ALIGNED_TABLEBITS) + (LzxConstants.ALIGNED_MAXSYMBOLS << 1)]; - m_state.ALIGNED_len = new byte[LzxConstants.ALIGNED_MAXSYMBOLS + LzxConstants.LENTABLE_SAFETY]; - /* initialise tables to 0 (because deltas will be applied to them) */ - for (int i = 0; i < LzxConstants.MAINTREE_MAXSYMBOLS; i++) m_state.MAINTREE_len[i] = 0; - for (int i = 0; i < LzxConstants.LENGTH_MAXSYMBOLS; i++) m_state.LENGTH_len[i] = 0; - } - - public int Decompress(Stream inData, int inLen, Stream outData, int outLen) - { - BitBuffer bitbuf = new BitBuffer(inData); - long startpos = inData.Position; - long endpos = inData.Position + inLen; - - byte[] window = m_state.window; - - uint window_posn = m_state.window_posn; - uint window_size = m_state.window_size; - uint R0 = m_state.R0; - uint R1 = m_state.R1; - uint R2 = m_state.R2; - uint i, j; - - int togo = outLen, this_run, main_element, match_length, match_offset, length_footer, extra, verbatim_bits; - int rundest, runsrc, copy_length, aligned_bits; - - bitbuf.InitBitStream(); - - /* read header if necessary */ - if (m_state.header_read == 0) - { - uint intel = bitbuf.ReadBits(1); - if (intel != 0) - { - // read the filesize - i = bitbuf.ReadBits(16); j = bitbuf.ReadBits(16); - m_state.intel_filesize = (int)((i << 16) | j); - } - m_state.header_read = 1; - } - - /* main decoding loop */ - while (togo > 0) - { - /* last block finished, new block expected */ - if (m_state.block_remaining == 0) - { - // TODO may screw something up here - if (m_state.block_type == LzxConstants.BLOCKTYPE.UNCOMPRESSED) - { - if ((m_state.block_length & 1) == 1) inData.ReadByte(); /* realign bitstream to word */ - bitbuf.InitBitStream(); - } - - m_state.block_type = (LzxConstants.BLOCKTYPE)bitbuf.ReadBits(3); ; - i = bitbuf.ReadBits(16); - j = bitbuf.ReadBits(8); - m_state.block_remaining = m_state.block_length = (uint)((i << 8) | j); - - switch (m_state.block_type) - { - case LzxConstants.BLOCKTYPE.ALIGNED: - for (i = 0, j = 0; i < 8; i++) { j = bitbuf.ReadBits(3); m_state.ALIGNED_len[i] = (byte)j; } - MakeDecodeTable(LzxConstants.ALIGNED_MAXSYMBOLS, LzxConstants.ALIGNED_TABLEBITS, - m_state.ALIGNED_len, m_state.ALIGNED_table); - /* rest of aligned header is same as verbatim */ - goto case LzxConstants.BLOCKTYPE.VERBATIM; - - case LzxConstants.BLOCKTYPE.VERBATIM: - ReadLengths(m_state.MAINTREE_len, 0, 256, bitbuf); - ReadLengths(m_state.MAINTREE_len, 256, m_state.main_elements, bitbuf); - MakeDecodeTable(LzxConstants.MAINTREE_MAXSYMBOLS, LzxConstants.MAINTREE_TABLEBITS, - m_state.MAINTREE_len, m_state.MAINTREE_table); - if (m_state.MAINTREE_len[0xE8] != 0) m_state.intel_started = 1; - - ReadLengths(m_state.LENGTH_len, 0, LzxConstants.NUM_SECONDARY_LENGTHS, bitbuf); - MakeDecodeTable(LzxConstants.LENGTH_MAXSYMBOLS, LzxConstants.LENGTH_TABLEBITS, - m_state.LENGTH_len, m_state.LENGTH_table); - break; - - case LzxConstants.BLOCKTYPE.UNCOMPRESSED: - m_state.intel_started = 1; /* because we can't assume otherwise */ - bitbuf.EnsureBits(16); /* get up to 16 pad bits into the buffer */ - if (bitbuf.GetBitsLeft() > 16) inData.Seek(-2, SeekOrigin.Current); /* and align the bitstream! */ - byte hi, mh, ml, lo; - lo = (byte)inData.ReadByte(); ml = (byte)inData.ReadByte(); mh = (byte)inData.ReadByte(); hi = (byte)inData.ReadByte(); - R0 = (uint)(lo | ml << 8 | mh << 16 | hi << 24); - lo = (byte)inData.ReadByte(); ml = (byte)inData.ReadByte(); mh = (byte)inData.ReadByte(); hi = (byte)inData.ReadByte(); - R1 = (uint)(lo | ml << 8 | mh << 16 | hi << 24); - lo = (byte)inData.ReadByte(); ml = (byte)inData.ReadByte(); mh = (byte)inData.ReadByte(); hi = (byte)inData.ReadByte(); - R2 = (uint)(lo | ml << 8 | mh << 16 | hi << 24); - break; - - default: - return -1; // TODO throw proper exception - } - } - - /* buffer exhaustion check */ - if (inData.Position > (startpos + inLen)) - { - /* it's possible to have a file where the next run is less than - * 16 bits in size. In this case, the READ_HUFFSYM() macro used - * in building the tables will exhaust the buffer, so we should - * allow for this, but not allow those accidentally read bits to - * be used (so we check that there are at least 16 bits - * remaining - in this boundary case they aren't really part of - * the compressed data) - */ - //Debug.WriteLine("WTF"); - - if (inData.Position > (startpos + inLen + 2) || bitbuf.GetBitsLeft() < 16) return -1; //TODO throw proper exception - } - - while ((this_run = (int)m_state.block_remaining) > 0 && togo > 0) - { - if (this_run > togo) this_run = togo; - togo -= this_run; - m_state.block_remaining -= (uint)this_run; - - /* apply 2^x-1 mask */ - window_posn &= window_size - 1; - /* runs can't straddle the window wraparound */ - if ((window_posn + this_run) > window_size) - return -1; //TODO throw proper exception - - switch (m_state.block_type) - { - case LzxConstants.BLOCKTYPE.VERBATIM: - while (this_run > 0) - { - main_element = (int)ReadHuffSym(m_state.MAINTREE_table, m_state.MAINTREE_len, - LzxConstants.MAINTREE_MAXSYMBOLS, LzxConstants.MAINTREE_TABLEBITS, - bitbuf); - if (main_element < LzxConstants.NUM_CHARS) - { - /* literal: 0 to NUM_CHARS-1 */ - window[window_posn++] = (byte)main_element; - this_run--; - } - else - { - /* match: NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ - main_element -= LzxConstants.NUM_CHARS; - - match_length = main_element & LzxConstants.NUM_PRIMARY_LENGTHS; - if (match_length == LzxConstants.NUM_PRIMARY_LENGTHS) - { - length_footer = (int)ReadHuffSym(m_state.LENGTH_table, m_state.LENGTH_len, - LzxConstants.LENGTH_MAXSYMBOLS, LzxConstants.LENGTH_TABLEBITS, - bitbuf); - match_length += length_footer; - } - match_length += LzxConstants.MIN_MATCH; - - match_offset = main_element >> 3; - - if (match_offset > 2) - { - /* not repeated offset */ - if (match_offset != 3) - { - extra = extra_bits[match_offset]; - verbatim_bits = (int)bitbuf.ReadBits((byte)extra); - match_offset = (int)position_base[match_offset] - 2 + verbatim_bits; - } - else - { - match_offset = 1; - } - - /* update repeated offset LRU queue */ - R2 = R1; R1 = R0; R0 = (uint)match_offset; - } - else if (match_offset == 0) - { - match_offset = (int)R0; - } - else if (match_offset == 1) - { - match_offset = (int)R1; - R1 = R0; R0 = (uint)match_offset; - } - else /* match_offset == 2 */ - { - match_offset = (int)R2; - R2 = R0; R0 = (uint)match_offset; - } - - rundest = (int)window_posn; - this_run -= match_length; - - /* copy any wrapped around source data */ - if (window_posn >= match_offset) - { - /* no wrap */ - runsrc = rundest - match_offset; - } - else - { - runsrc = rundest + ((int)window_size - match_offset); - copy_length = match_offset - (int)window_posn; - if (copy_length < match_length) - { - match_length -= copy_length; - window_posn += (uint)copy_length; - while (copy_length-- > 0) window[rundest++] = window[runsrc++]; - runsrc = 0; - } - } - window_posn += (uint)match_length; - - /* copy match data - no worries about destination wraps */ - while (match_length-- > 0) window[rundest++] = window[runsrc++]; - } - } - break; - - case LzxConstants.BLOCKTYPE.ALIGNED: - while (this_run > 0) - { - main_element = (int)ReadHuffSym(m_state.MAINTREE_table, m_state.MAINTREE_len, - LzxConstants.MAINTREE_MAXSYMBOLS, LzxConstants.MAINTREE_TABLEBITS, - bitbuf); - - if (main_element < LzxConstants.NUM_CHARS) - { - /* literal 0 to NUM_CHARS-1 */ - window[window_posn++] = (byte)main_element; - this_run--; - } - else - { - /* match: NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ - main_element -= LzxConstants.NUM_CHARS; - - match_length = main_element & LzxConstants.NUM_PRIMARY_LENGTHS; - if (match_length == LzxConstants.NUM_PRIMARY_LENGTHS) - { - length_footer = (int)ReadHuffSym(m_state.LENGTH_table, m_state.LENGTH_len, - LzxConstants.LENGTH_MAXSYMBOLS, LzxConstants.LENGTH_TABLEBITS, - bitbuf); - match_length += length_footer; - } - match_length += LzxConstants.MIN_MATCH; - - match_offset = main_element >> 3; - - if (match_offset > 2) - { - /* not repeated offset */ - extra = extra_bits[match_offset]; - match_offset = (int)position_base[match_offset] - 2; - if (extra > 3) - { - /* verbatim and aligned bits */ - extra -= 3; - verbatim_bits = (int)bitbuf.ReadBits((byte)extra); - match_offset += (verbatim_bits << 3); - aligned_bits = (int)ReadHuffSym(m_state.ALIGNED_table, m_state.ALIGNED_len, - LzxConstants.ALIGNED_MAXSYMBOLS, LzxConstants.ALIGNED_TABLEBITS, - bitbuf); - match_offset += aligned_bits; - } - else if (extra == 3) - { - /* aligned bits only */ - aligned_bits = (int)ReadHuffSym(m_state.ALIGNED_table, m_state.ALIGNED_len, - LzxConstants.ALIGNED_MAXSYMBOLS, LzxConstants.ALIGNED_TABLEBITS, - bitbuf); - match_offset += aligned_bits; - } - else if (extra > 0) /* extra==1, extra==2 */ - { - /* verbatim bits only */ - verbatim_bits = (int)bitbuf.ReadBits((byte)extra); - match_offset += verbatim_bits; - } - else /* extra == 0 */ - { - /* ??? */ - match_offset = 1; - } - - /* update repeated offset LRU queue */ - R2 = R1; R1 = R0; R0 = (uint)match_offset; - } - else if (match_offset == 0) - { - match_offset = (int)R0; - } - else if (match_offset == 1) - { - match_offset = (int)R1; - R1 = R0; R0 = (uint)match_offset; - } - else /* match_offset == 2 */ - { - match_offset = (int)R2; - R2 = R0; R0 = (uint)match_offset; - } - - rundest = (int)window_posn; - this_run -= match_length; - - /* copy any wrapped around source data */ - if (window_posn >= match_offset) - { - /* no wrap */ - runsrc = rundest - match_offset; - } - else - { - runsrc = rundest + ((int)window_size - match_offset); - copy_length = match_offset - (int)window_posn; - if (copy_length < match_length) - { - match_length -= copy_length; - window_posn += (uint)copy_length; - while (copy_length-- > 0) window[rundest++] = window[runsrc++]; - runsrc = 0; - } - } - window_posn += (uint)match_length; - - /* copy match data - no worries about destination wraps */ - while (match_length-- > 0) window[rundest++] = window[runsrc++]; - } - } - break; - - case LzxConstants.BLOCKTYPE.UNCOMPRESSED: - if ((inData.Position + this_run) > endpos) return -1; //TODO throw proper exception - byte[] temp_buffer = new byte[this_run]; - inData.Read(temp_buffer, 0, this_run); - temp_buffer.CopyTo(window, (int)window_posn); - window_posn += (uint)this_run; - break; - - default: - return -1; //TODO throw proper exception - } - } - } - - if (togo != 0) return -1; //TODO throw proper exception - int start_window_pos = (int)window_posn; - if (start_window_pos == 0) start_window_pos = (int)window_size; - start_window_pos -= outLen; - outData.Write(window, start_window_pos, outLen); - - m_state.window_posn = window_posn; - m_state.R0 = R0; - m_state.R1 = R1; - m_state.R2 = R2; - - // TODO finish intel E8 decoding - /* intel E8 decoding */ - if ((m_state.frames_read++ < 32768) && m_state.intel_filesize != 0) - { - if (outLen <= 6 || m_state.intel_started == 0) - { - m_state.intel_curpos += outLen; - } - else - { - int dataend = outLen - 10; - uint curpos = (uint)m_state.intel_curpos; - - m_state.intel_curpos = (int)curpos + outLen; - - while (outData.Position < dataend) - { - if (outData.ReadByte() != 0xE8) { curpos++; continue; } - } - } - return -1; - } - return 0; - } - - // READ_LENGTHS(table, first, last) - // if(lzx_read_lens(LENTABLE(table), first, last, bitsleft)) - // return ERROR (ILLEGAL_DATA) - // - - // TODO make returns throw exceptions - private int MakeDecodeTable(uint nsyms, uint nbits, byte[] length, ushort[] table) - { - ushort sym; - uint leaf; - byte bit_num = 1; - uint fill; - uint pos = 0; /* the current position in the decode table */ - uint table_mask = (uint)(1 << (int)nbits); - uint bit_mask = table_mask >> 1; /* don't do 0 length codes */ - uint next_symbol = bit_mask; /* base of allocation for long codes */ - - /* fill entries for codes short enough for a direct mapping */ - while (bit_num <= nbits) - { - for (sym = 0; sym < nsyms; sym++) - { - if (length[sym] == bit_num) - { - leaf = pos; - - if ((pos += bit_mask) > table_mask) return 1; /* table overrun */ - - /* fill all possible lookups of this symbol with the symbol itself */ - fill = bit_mask; - while (fill-- > 0) table[leaf++] = sym; - } - } - bit_mask >>= 1; - bit_num++; - } - - /* if there are any codes longer than nbits */ - if (pos != table_mask) - { - /* clear the remainder of the table */ - for (sym = (ushort)pos; sym < table_mask; sym++) table[sym] = 0; - - /* give ourselves room for codes to grow by up to 16 more bits */ - pos <<= 16; - table_mask <<= 16; - bit_mask = 1 << 15; - - while (bit_num <= 16) - { - for (sym = 0; sym < nsyms; sym++) - { - if (length[sym] == bit_num) - { - leaf = pos >> 16; - for (fill = 0; fill < bit_num - nbits; fill++) - { - /* if this path hasn't been taken yet, 'allocate' two entries */ - if (table[leaf] == 0) - { - table[(next_symbol << 1)] = 0; - table[(next_symbol << 1) + 1] = 0; - table[leaf] = (ushort)(next_symbol++); - } - /* follow the path and select either left or right for next bit */ - leaf = (uint)(table[leaf] << 1); - if (((pos >> (int)(15 - fill)) & 1) == 1) leaf++; - } - table[leaf] = sym; - - if ((pos += bit_mask) > table_mask) return 1; - } - } - bit_mask >>= 1; - bit_num++; - } - } - - /* full talbe? */ - if (pos == table_mask) return 0; - - /* either erroneous table, or all elements are 0 - let's find out. */ - for (sym = 0; sym < nsyms; sym++) if (length[sym] != 0) return 1; - return 0; - } - - // TODO throw exceptions instead of returns - private void ReadLengths(byte[] lens, uint first, uint last, BitBuffer bitbuf) - { - uint x, y; - int z; - - // hufftbl pointer here? - - for (x = 0; x < 20; x++) - { - y = bitbuf.ReadBits(4); - m_state.PRETREE_len[x] = (byte)y; - } - MakeDecodeTable(LzxConstants.PRETREE_MAXSYMBOLS, LzxConstants.PRETREE_TABLEBITS, - m_state.PRETREE_len, m_state.PRETREE_table); - - for (x = first; x < last;) - { - z = (int)ReadHuffSym(m_state.PRETREE_table, m_state.PRETREE_len, - LzxConstants.PRETREE_MAXSYMBOLS, LzxConstants.PRETREE_TABLEBITS, bitbuf); - if (z == 17) - { - y = bitbuf.ReadBits(4); y += 4; - while (y-- != 0) lens[x++] = 0; - } - else if (z == 18) - { - y = bitbuf.ReadBits(5); y += 20; - while (y-- != 0) lens[x++] = 0; - } - else if (z == 19) - { - y = bitbuf.ReadBits(1); y += 4; - z = (int)ReadHuffSym(m_state.PRETREE_table, m_state.PRETREE_len, - LzxConstants.PRETREE_MAXSYMBOLS, LzxConstants.PRETREE_TABLEBITS, bitbuf); - z = lens[x] - z; if (z < 0) z += 17; - while (y-- != 0) lens[x++] = (byte)z; - } - else - { - z = lens[x] - z; if (z < 0) z += 17; - lens[x++] = (byte)z; - } - } - } - - private uint ReadHuffSym(ushort[] table, byte[] lengths, uint nsyms, uint nbits, BitBuffer bitbuf) - { - uint i, j; - bitbuf.EnsureBits(16); - if ((i = table[bitbuf.PeekBits((byte)nbits)]) >= nsyms) - { - j = (uint)(1 << (int)((sizeof(uint) * 8) - nbits)); - do - { - j >>= 1; i <<= 1; i |= (bitbuf.GetBuffer() & j) != 0 ? (uint)1 : 0; - if (j == 0) return 0; // TODO throw proper exception - } while ((i = table[i]) >= nsyms); - } - j = lengths[i]; - bitbuf.RemoveBits((byte)j); - - return i; - } - - #region Our BitBuffer Class - private class BitBuffer - { - uint buffer; - byte bitsleft; - Stream byteStream; - - public BitBuffer(Stream stream) - { - byteStream = stream; - InitBitStream(); - } - - public void InitBitStream() - { - buffer = 0; - bitsleft = 0; - } - - public void EnsureBits(byte bits) - { - while (bitsleft < bits) - { - int lo = (byte)byteStream.ReadByte(); - int hi = (byte)byteStream.ReadByte(); - //int amount2shift = sizeof(uint)*8 - 16 - bitsleft; - buffer |= (uint)(((hi << 8) | lo) << (sizeof(uint) * 8 - 16 - bitsleft)); - bitsleft += 16; - } - } - - public uint PeekBits(byte bits) - { - return (buffer >> ((sizeof(uint) * 8) - bits)); - } - - public void RemoveBits(byte bits) - { - buffer <<= bits; - bitsleft -= bits; - } - - public uint ReadBits(byte bits) - { - uint ret = 0; - - if (bits > 0) - { - EnsureBits(bits); - ret = PeekBits(bits); - RemoveBits(bits); - } - - return ret; - } - - public uint GetBuffer() - { - return buffer; - } - - public byte GetBitsLeft() - { - return bitsleft; - } - } - #endregion - - struct LzxState - { - public uint R0, R1, R2; /* for the LRU offset system */ - public ushort main_elements; /* number of main tree elements */ - public int header_read; /* have we started decoding at all yet? */ - public LzxConstants.BLOCKTYPE block_type; /* type of this block */ - public uint block_length; /* uncompressed length of this block */ - public uint block_remaining; /* uncompressed bytes still left to decode */ - public uint frames_read; /* the number of CFDATA blocks processed */ - public int intel_filesize; /* magic header value used for transform */ - public int intel_curpos; /* current offset in transform space */ - public int intel_started; /* have we seen any translateable data yet? */ - - public ushort[] PRETREE_table; - public byte[] PRETREE_len; - public ushort[] MAINTREE_table; - public byte[] MAINTREE_len; - public ushort[] LENGTH_table; - public byte[] LENGTH_len; - public ushort[] ALIGNED_table; - public byte[] ALIGNED_len; - - // NEEDED MEMBERS - // CAB actualsize - // CAB window - // CAB window_size - // CAB window_posn - public uint actual_size; - public byte[] window; - public uint window_size; - public uint window_posn; - } - } - - /* CONSTANTS */ - struct LzxConstants - { - public const ushort MIN_MATCH = 2; - public const ushort MAX_MATCH = 257; - public const ushort NUM_CHARS = 256; - public enum BLOCKTYPE - { - INVALID = 0, - VERBATIM = 1, - ALIGNED = 2, - UNCOMPRESSED = 3 - } - public const ushort PRETREE_NUM_ELEMENTS = 20; - public const ushort ALIGNED_NUM_ELEMENTS = 8; - public const ushort NUM_PRIMARY_LENGTHS = 7; - public const ushort NUM_SECONDARY_LENGTHS = 249; - - public const ushort PRETREE_MAXSYMBOLS = PRETREE_NUM_ELEMENTS; - public const ushort PRETREE_TABLEBITS = 6; - public const ushort MAINTREE_MAXSYMBOLS = NUM_CHARS + 50 * 8; - public const ushort MAINTREE_TABLEBITS = 12; - public const ushort LENGTH_MAXSYMBOLS = NUM_SECONDARY_LENGTHS + 1; - public const ushort LENGTH_TABLEBITS = 12; - public const ushort ALIGNED_MAXSYMBOLS = ALIGNED_NUM_ELEMENTS; - public const ushort ALIGNED_TABLEBITS = 7; - - public const ushort LENTABLE_SAFETY = 64; - } - - /* EXCEPTIONS */ - class UnsupportedWindowSizeRange : Exception - { - } -} \ No newline at end of file diff --git a/PCK-Studio/Classes/Utils/grf/xmemdecompress.cs b/PCK-Studio/Classes/Utils/grf/xmemdecompress.cs deleted file mode 100644 index 764e3fc7..00000000 --- a/PCK-Studio/Classes/Utils/grf/xmemdecompress.cs +++ /dev/null @@ -1,155 +0,0 @@ -// MonoGame - Copyright (C) The MonoGame Team -// This file is subject to the terms and conditions defined in -// file 'LICENSE.txt', which is part of this source code package. - -using System; -using System.IO; -using Microsoft.Xna.Framework.Content; - -namespace MonoGame.Framework.Utilities -{ - internal class LzxDecoderStream : Stream - { - LzxDecoder dec; - MemoryStream decompressedStream; - - public LzxDecoderStream(Stream input, int decompressedSize, int compressedSize) - { - dec = new LzxDecoder(16); - - // TODO: Rewrite using block decompression like Lz4DecoderStream - Decompress(input, decompressedSize, compressedSize); - } - - // Decompress into MemoryStream - private void Decompress(Stream stream, int decompressedSize, int compressedSize) - { - //thanks to ShinAli (https://bitbucket.org/alisci01/xnbdecompressor) - // default window size for XNB encoded files is 64Kb (need 16 bits to represent it) - decompressedStream = new MemoryStream(decompressedSize); - long startPos = stream.Position; - long pos = startPos; - - while (pos - startPos < compressedSize) - { - // the compressed stream is seperated into blocks that will decompress - // into 32Kb or some other size if specified. - // normal, 32Kb output blocks will have a short indicating the size - // of the block before the block starts - // blocks that have a defined output will be preceded by a byte of value - // 0xFF (255), then a short indicating the output size and another - // for the block size - // all shorts for these cases are encoded in big endian order - int hi = stream.ReadByte(); - int lo = stream.ReadByte(); - int block_size = (hi << 8) | lo; - int frame_size = 0x8000; // frame size is 32Kb by default - // does this block define a frame size? - if (hi == 0xFF) - { - hi = lo; - lo = (byte)stream.ReadByte(); - frame_size = (hi << 8) | lo; - hi = (byte)stream.ReadByte(); - lo = (byte)stream.ReadByte(); - block_size = (hi << 8) | lo; - pos += 5; - } - else - pos += 2; - - // either says there is nothing to decode - if (block_size == 0 || frame_size == 0) - break; - - dec.Decompress(stream, block_size, decompressedStream, frame_size); - pos += block_size; - - // reset the position of the input just incase the bit buffer - // read in some unused bytes - stream.Seek(pos, SeekOrigin.Begin); - } - - Console.WriteLine(decompressedStream.Position); - Console.WriteLine(decompressedSize); - //if (decompressedStream.Position != decompressedSize) - //{ - // throw new Exception("Decompression failed."); - //} - - decompressedStream.Seek(0, SeekOrigin.Begin); - using (var fs = File.OpenWrite("xmem_test.grf")) - { - decompressedStream.CopyTo(fs); - } - decompressedStream.Seek(0, SeekOrigin.Begin); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - { - decompressedStream.Dispose(); - } - dec = null; - decompressedStream = null; - } - - #region Stream internals - - public override int Read(byte[] buffer, int offset, int count) - { - return decompressedStream.Read(buffer, offset, count); - } - - public override bool CanRead - { - get { return true; } - } - - - public override bool CanSeek - { - get { return false; } - } - - public override bool CanWrite - { - get { return false; } - } - - public override void Flush() - { - } - - public override long Length - { - get { throw new NotSupportedException(); } - } - - public override long Position - { - get { throw new NotSupportedException(); } - set { throw new NotSupportedException(); } - } - - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotImplementedException(); - } - - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotImplementedException(); - } - - #endregion - } -} \ No newline at end of file diff --git a/PCK-Studio/Forms/Editor/GRFEditor.Designer.cs b/PCK-Studio/Forms/Editor/GameRuleFileEditor.Designer.cs similarity index 90% rename from PCK-Studio/Forms/Editor/GRFEditor.Designer.cs rename to PCK-Studio/Forms/Editor/GameRuleFileEditor.Designer.cs index 375a9638..4c79e93d 100644 --- a/PCK-Studio/Forms/Editor/GRFEditor.Designer.cs +++ b/PCK-Studio/Forms/Editor/GameRuleFileEditor.Designer.cs @@ -1,6 +1,6 @@ namespace PckStudio.Forms.Editor { - partial class GRFEditor + partial class GameRuleFileEditor { /// /// Required designer variable. @@ -29,7 +29,7 @@ private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GRFEditor)); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(GameRuleFileEditor)); this.GrfTreeView = new System.Windows.Forms.TreeView(); this.MessageContextMenu = new MetroFramework.Controls.MetroContextMenu(this.components); this.addGameRuleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -44,9 +44,11 @@ this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.metroPanel1 = new MetroFramework.Controls.MetroPanel(); this.compressionLvlToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripComboBox1 = new System.Windows.Forms.ToolStripComboBox(); + this.compressionTypeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.compressionTypeComboBox = new System.Windows.Forms.ToolStripComboBox(); + this.metroPanel1 = new MetroFramework.Controls.MetroPanel(); this.MessageContextMenu.SuspendLayout(); this.DetailContextMenu.SuspendLayout(); this.menuStrip1.SuspendLayout(); @@ -153,7 +155,8 @@ this.menuStrip1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(64)))), ((int)(((byte)(64)))), ((int)(((byte)(64))))); this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.fileToolStripMenuItem, - this.compressionLvlToolStripMenuItem}); + this.compressionLvlToolStripMenuItem, + this.compressionTypeToolStripMenuItem}); this.menuStrip1.Location = new System.Drawing.Point(25, 60); this.menuStrip1.Name = "menuStrip1"; this.menuStrip1.Size = new System.Drawing.Size(450, 24); @@ -172,10 +175,10 @@ // // openToolStripMenuItem // - this.openToolStripMenuItem.Enabled = false; this.openToolStripMenuItem.Name = "openToolStripMenuItem"; this.openToolStripMenuItem.Size = new System.Drawing.Size(180, 22); this.openToolStripMenuItem.Text = "Open"; + this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click); // // saveToolStripMenuItem // @@ -185,6 +188,43 @@ this.saveToolStripMenuItem.Text = "Save"; this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click); // + // compressionLvlToolStripMenuItem + // + this.compressionLvlToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripComboBox1}); + this.compressionLvlToolStripMenuItem.ForeColor = System.Drawing.SystemColors.Menu; + this.compressionLvlToolStripMenuItem.Name = "compressionLvlToolStripMenuItem"; + this.compressionLvlToolStripMenuItem.Size = new System.Drawing.Size(106, 20); + this.compressionLvlToolStripMenuItem.Text = "Compression Lvl"; + // + // toolStripComboBox1 + // + this.toolStripComboBox1.Items.AddRange(new object[] { + "None", + "Compressed", + "Compressed + RLE", + "Compressed + RLE + CRC"}); + this.toolStripComboBox1.Name = "toolStripComboBox1"; + this.toolStripComboBox1.Size = new System.Drawing.Size(121, 23); + this.toolStripComboBox1.Text = "None"; + // + // compressionTypeToolStripMenuItem + // + this.compressionTypeToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.compressionTypeComboBox}); + this.compressionTypeToolStripMenuItem.Name = "compressionTypeToolStripMenuItem"; + this.compressionTypeToolStripMenuItem.Size = new System.Drawing.Size(116, 20); + this.compressionTypeToolStripMenuItem.Text = "Compression Type"; + // + // compressionTypeComboBox + // + this.compressionTypeComboBox.Items.AddRange(new object[] { + "Zlib", + "Deflate", + "XMem"}); + this.compressionTypeComboBox.Name = "compressionTypeComboBox"; + this.compressionTypeComboBox.Size = new System.Drawing.Size(121, 23); + // // metroPanel1 // this.metroPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) @@ -205,26 +245,6 @@ this.metroPanel1.VerticalScrollbarSize = 10; this.metroPanel1.Resize += new System.EventHandler(this.metroPanel1_Resize); // - // compressionLvlToolStripMenuItem - // - this.compressionLvlToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.toolStripComboBox1}); - this.compressionLvlToolStripMenuItem.ForeColor = System.Drawing.SystemColors.Menu; - this.compressionLvlToolStripMenuItem.Name = "compressionLvlToolStripMenuItem"; - this.compressionLvlToolStripMenuItem.Size = new System.Drawing.Size(106, 20); - this.compressionLvlToolStripMenuItem.Text = "Compression Lvl"; - // - // toolStripComboBox1 - // - this.toolStripComboBox1.Items.AddRange(new object[] { - "None", - "Compressed", - "Compressed + RLE", - "Compressed + RLE + CRC"}); - this.toolStripComboBox1.Name = "toolStripComboBox1"; - this.toolStripComboBox1.Size = new System.Drawing.Size(121, 23); - this.toolStripComboBox1.Text = "Zlib"; - // // GRFEditor // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -274,5 +294,7 @@ private MetroFramework.Controls.MetroPanel metroPanel1; private System.Windows.Forms.ToolStripMenuItem compressionLvlToolStripMenuItem; private System.Windows.Forms.ToolStripComboBox toolStripComboBox1; + private System.Windows.Forms.ToolStripMenuItem compressionTypeToolStripMenuItem; + private System.Windows.Forms.ToolStripComboBox compressionTypeComboBox; } } \ No newline at end of file diff --git a/PCK-Studio/Forms/Editor/GRFEditor.cs b/PCK-Studio/Forms/Editor/GameRuleFileEditor.cs similarity index 65% rename from PCK-Studio/Forms/Editor/GRFEditor.cs rename to PCK-Studio/Forms/Editor/GameRuleFileEditor.cs index 00d8cf8d..fedfd8a3 100644 --- a/PCK-Studio/Forms/Editor/GRFEditor.cs +++ b/PCK-Studio/Forms/Editor/GameRuleFileEditor.cs @@ -5,81 +5,101 @@ using System.IO; using System.Linq; using System.Windows.Forms; using PckStudio.Classes.FileTypes; -using PckStudio.Classes.IO.GRF; using PckStudio.Forms.Additional_Popups.Grf; using PckStudio.Classes.Misc; +using OMI.Formats.GameRule; +using OMI.Workers.GameRule; +using System.Diagnostics; +using PckStudio.Forms.Additional_Popups.Audio; namespace PckStudio.Forms.Editor { - public partial class GRFEditor : MetroFramework.Forms.MetroForm + public partial class GameRuleFileEditor : MetroFramework.Forms.MetroForm { private PCKFile.FileData _pckfile; - private GRFFile _file; + private GameRuleFile _file; - - private GRFEditor() + public GameRuleFileEditor() { InitializeComponent(); + PromptForCompressionType(); } - public GRFEditor(PCKFile.FileData file) : this() + private void PromptForCompressionType() + { + addCategory dialog = new addCategory(compressionTypeComboBox.Items.Cast().ToArray()); + dialog.label2.Text = "Type"; + dialog.button1.Text = "Ok"; + if (dialog.ShowDialog() == DialogResult.OK) + compressionTypeComboBox.SelectedIndex = compressionTypeComboBox.Items.IndexOf(dialog.Category); + } + + public GameRuleFileEditor(PCKFile.FileData file) : this() { _pckfile = file; - using(var stream = new MemoryStream(file.Data)) + using (var stream = new MemoryStream(file.Data)) { - try - { - _file = GRFFileReader.Read(stream); - } - catch (Exception ex) - { - Console.WriteLine(ex.Message); - MessageBox.Show("Faild to open .grf/.grh file"); - } + _file = OpenGameRuleFile(stream, (GameRuleFile.CompressionType)compressionTypeComboBox.SelectedIndex); } } - public GRFEditor(Stream stream) : this() + public GameRuleFileEditor(Stream stream) : this() + { + _file = OpenGameRuleFile(stream, (GameRuleFile.CompressionType)compressionTypeComboBox.SelectedIndex); + } + + private GameRuleFile OpenGameRuleFile(Stream stream, GameRuleFile.CompressionType compressionType) { try { - _file = GRFFileReader.Read(stream); + var reader = new GameRuleFileReader(compressionType); + return reader.FromStream(stream); } catch (Exception ex) { - Console.WriteLine(ex.Message); + Debug.WriteLine(ex.Message); MessageBox.Show("Faild to open .grf/.grh file"); } + return default!; } private void OnLoad(object sender, EventArgs e) { RPC.SetPresence("GRF Editor", "Editing a GRF File"); - toolStripComboBox1.SelectedIndex = (int)_file.CompressionLevel; - loadGRFTreeView(GrfTreeView.Nodes, _file.Root); + ReloadGameRuleTree(); } - private void loadGRFTreeView(TreeNodeCollection root, GRFFile.GameRule parentRule) + private void LoadGameRuleTree(TreeNodeCollection root, GameRuleFile.GameRule parentRule) { foreach (var rule in parentRule.ChildRules) { TreeNode node = new TreeNode(rule.Name); node.Tag = rule; root.Add(node); - loadGRFTreeView(node.Nodes, rule); + LoadGameRuleTree(node.Nodes, rule); + } + } + + private void ReloadGameRuleTree() + { + GrfTreeView.Nodes.Clear(); + if (_file is not null) + { + toolStripComboBox1.SelectedIndex = (int)_file.FileHeader.CompressionLevel; + LoadGameRuleTree(GrfTreeView.Nodes, _file.Root); } } private void GrfTreeView_AfterSelect(object sender, TreeViewEventArgs e) { - if (e.Node is TreeNode t && t.Tag is GRFFile.GameRule) + if (e.Node is TreeNode t && t.Tag is GameRuleFile.GameRule) ReloadParameterTreeView(); } private void ReloadParameterTreeView() { GrfParametersTreeView.Nodes.Clear(); - if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GRFFile.GameRule rule) + if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GameRuleFile.GameRule rule) foreach (var param in rule.Parameters) { GrfParametersTreeView.Nodes.Add(new TreeNode($"{param.Key}: {param.Value}") { Tag = param}); @@ -88,8 +108,8 @@ namespace PckStudio.Forms.Editor private void addDetailContextMenuItem_Click(object sender, EventArgs e) { - if (GrfTreeView.SelectedNode == null || !(GrfTreeView.SelectedNode.Tag is GRFFile.GameRule)) return; - var grfTag = GrfTreeView.SelectedNode.Tag as GRFFile.GameRule; + if (GrfTreeView.SelectedNode == null || !(GrfTreeView.SelectedNode.Tag is GameRuleFile.GameRule)) return; + var grfTag = GrfTreeView.SelectedNode.Tag as GameRuleFile.GameRule; AddParameter prompt = new AddParameter(); if (prompt.ShowDialog() == DialogResult.OK) { @@ -105,7 +125,7 @@ namespace PckStudio.Forms.Editor private void removeToolStripMenuItem_Click(object sender, EventArgs e) { - if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GRFFile.GameRule rule && + if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GameRuleFile.GameRule rule && GrfParametersTreeView.SelectedNode is TreeNode paramNode && paramNode.Tag is KeyValuePair pair && rule.Parameters.ContainsKey(pair.Key) && rule.Parameters.Remove(pair.Key)) { @@ -123,7 +143,7 @@ namespace PckStudio.Forms.Editor private void GrfDetailsTreeView_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e) { - if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GRFFile.GameRule rule && + if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GameRuleFile.GameRule rule && GrfParametersTreeView.SelectedNode is TreeNode paramNode && paramNode.Tag is KeyValuePair param) { AddParameter prompt = new AddParameter(param.Key, param.Value, false); @@ -137,9 +157,9 @@ namespace PckStudio.Forms.Editor private void addGameRuleToolStripMenuItem_Click(object sender, EventArgs e) { - bool isValidNode = GrfTreeView.SelectedNode is TreeNode t && t.Tag is GRFFile.GameRule; - GRFFile.GameRule parentRule = isValidNode - ? GrfTreeView.SelectedNode.Tag as GRFFile.GameRule + bool isValidNode = GrfTreeView.SelectedNode is TreeNode t && t.Tag is GameRuleFile.GameRule; + var parentRule = isValidNode + ? GrfTreeView.SelectedNode.Tag as GameRuleFile.GameRule : _file.Root; TreeNodeCollection root = isValidNode @@ -164,11 +184,11 @@ namespace PckStudio.Forms.Editor private void removeGameRuleToolStripMenuItem_Click(object sender, EventArgs e) { - if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GRFFile.GameRule tag && removeTag(tag)) + if (GrfTreeView.SelectedNode is TreeNode t && t.Tag is GameRuleFile.GameRule tag && removeTag(tag)) t.Remove(); } - private bool removeTag(GRFFile.GameRule rule) + private bool removeTag(GameRuleFile.GameRule rule) { _ = rule.Parent ?? throw new ArgumentNullException(nameof(rule.Parent)); foreach (var subTag in rule.ChildRules.ToList()) @@ -184,7 +204,7 @@ namespace PckStudio.Forms.Editor private void saveToolStripMenuItem_Click(object sender, EventArgs e) { - if (_file.IsWorld) + if (_file.FileHeader.unknownData[3] != 0) { MessageBox.Show("World grf saving is currently unsupported"); return; @@ -193,7 +213,11 @@ namespace PckStudio.Forms.Editor { try { - GRFFileWriter.Write(stream, _file, (GRFFile.CompressionType)toolStripComboBox1.SelectedIndex/*GRFFile.eCompressionType.ZlibRleCrc*/); + var writer = new GameRuleFileWriter( + _file, + (GameRuleFile.CompressionLevel)toolStripComboBox1.SelectedIndex, + (GameRuleFile.CompressionType)compressionTypeComboBox.SelectedIndex); + writer.WriteToStream(stream); _pckfile?.SetData(stream.ToArray()); MessageBox.Show("Saved!"); } @@ -213,5 +237,20 @@ namespace PckStudio.Forms.Editor // good enough metroLabel2.Location = new Point(metroPanel1.Size.Width / 2 + 25, metroLabel2.Location.Y); } + + private void openToolStripMenuItem_Click(object sender, EventArgs e) + { + OpenFileDialog dialog = new OpenFileDialog(); + dialog.Filter = "Game Rule File|*.grf"; + PromptForCompressionType(); + if (dialog.ShowDialog(this) == DialogResult.OK) + { + using (var fs = File.OpenRead(dialog.FileName)) + { + _file = OpenGameRuleFile(fs, (GameRuleFile.CompressionType)compressionTypeComboBox.SelectedIndex); + ReloadGameRuleTree(); + } + } + } } } diff --git a/PCK-Studio/Forms/Editor/GRFEditor.resx b/PCK-Studio/Forms/Editor/GameRuleFileEditor.resx similarity index 100% rename from PCK-Studio/Forms/Editor/GRFEditor.resx rename to PCK-Studio/Forms/Editor/GameRuleFileEditor.resx diff --git a/PCK-Studio/MainForm.cs b/PCK-Studio/MainForm.cs index 7fd6aea1..bc972e83 100644 --- a/PCK-Studio/MainForm.cs +++ b/PCK-Studio/MainForm.cs @@ -8,11 +8,12 @@ using System.Drawing.Drawing2D; using System.Diagnostics; using System.Drawing.Imaging; +using OMI.Formats.GameRule; +using OMI.Workers.GameRule; using PckStudio.Properties; using PckStudio.Classes.FileTypes; using PckStudio.Classes.IO.LOC; using PckStudio.Classes.IO.PCK; -using PckStudio.Classes.IO.GRF; using PckStudio.Classes.Utils; using PckStudio.Classes.Utils.ARC; using PckStudio.Classes._3ds.Utils; @@ -372,7 +373,7 @@ namespace PckStudio private void HandleGameRuleFile(PCKFile.FileData file) { - using GRFEditor grfEditor = new GRFEditor(file); + using GameRuleFileEditor grfEditor = new GameRuleFileEditor(file); wasModified = grfEditor.ShowDialog(this) == DialogResult.OK; UpdateRPC(); } @@ -1256,7 +1257,7 @@ namespace PckStudio { var newPck = InitializeTexturePack(packId, packVersion, packName, res, true); var gameRuleFile = newPck.CreateNew("GameRules.grf", PCKFile.FileData.FileType.GameRulesFile); - var grfFile = new GRFFile(); + var grfFile = new GameRuleFile(); grfFile.AddRule("MapOptions", new KeyValuePair("seed", "0"), new KeyValuePair("baseSaveName", string.Empty), @@ -1272,8 +1273,9 @@ namespace PckStudio new KeyValuePair("spawnZ", "0") ); using (var stream = new MemoryStream()) - { - GRFFileWriter.Write(stream, grfFile, GRFFile.CompressionType.None); + { + var writer = new GameRuleFileWriter(grfFile); + writer.WriteToStream(stream); gameRuleFile.SetData(stream.ToArray()); } return newPck; diff --git a/PCK-Studio/PckStudio.csproj b/PCK-Studio/PckStudio.csproj index 34b3b86e..85ddb6fd 100644 --- a/PCK-Studio/PckStudio.csproj +++ b/PCK-Studio/PckStudio.csproj @@ -176,7 +176,6 @@ - @@ -195,8 +194,6 @@ - - @@ -232,8 +229,6 @@ - - Form @@ -342,11 +337,11 @@ AdvancedOptions.cs - + Form - - GRFEditor.cs + + GameRuleFileEditor.cs Form @@ -561,8 +556,8 @@ AdvancedOptions.cs Designer - - GRFEditor.cs + + GameRuleFileEditor.cs AddParameter.cs @@ -772,9 +767,6 @@ 13.0.2 - - 1.4.1 - 4.5.0 diff --git a/Vendor/OMI-Lib b/Vendor/OMI-Lib index e2fa983a..16280e2f 160000 --- a/Vendor/OMI-Lib +++ b/Vendor/OMI-Lib @@ -1 +1 @@ -Subproject commit e2fa983abc567498059bcbf858f7a638fe006b74 +Subproject commit 16280e2f0a738bbbebc233151fe382047ab3da2d