diff --git a/Warcraft.NET/Extensions/ExtendedIO.cs b/Warcraft.NET/Extensions/ExtendedIO.cs
index a76ba06..296f792 100644
--- a/Warcraft.NET/Extensions/ExtendedIO.cs
+++ b/Warcraft.NET/Extensions/ExtendedIO.cs
@@ -1,12 +1,13 @@
-using Warcraft.NET.Files.Interfaces;
-using System;
-using System.IO;
-using System.Text;
-using Warcraft.NET.Files.Structures;
+using System;
using System.Collections.Generic;
+using System.IO;
using System.Numerics;
-using Warcraft.NET.Exceptions;
using System.Runtime.CompilerServices;
+using System.Text;
+using Warcraft.NET.Exceptions;
+using Warcraft.NET.Files.Interfaces;
+using Warcraft.NET.Files.Structures;
+using Warcraft.NET.Types;
namespace Warcraft.NET.Extensions
{
@@ -155,6 +156,16 @@ public static RGBA ReadBGRA(this BinaryReader reader)
};
}
+ ///
+ /// Reads a 2-byte from the data stream.
+ ///
+ /// The reader.
+ /// The half.
+ public static HalfFloat ReadHalfFloat(this BinaryReader reader)
+ {
+ return new HalfFloat(reader.ReadUInt16());
+ }
+
/// Reads ab 4.byte from the data stream.
///
/// The reader.
@@ -379,6 +390,17 @@ public static void WriteNullTerminatedString(this BinaryWriter binaryWriter, str
binaryWriter.Write((char)0);
}
+
+ ///
+ /// Writes a 2-byte to the data stream.
+ ///
+ /// The current object.
+ ///
+ public static void WriteHalfFloat(this BinaryWriter binaryWriter, HalfFloat half)
+ {
+ binaryWriter.Write(half.RawValue);
+ }
+
///
/// Writes the provided string to the data stream as a C-style null-terminated string.
///
diff --git a/Warcraft.NET/Types/HalfFloat.cs b/Warcraft.NET/Types/HalfFloat.cs
new file mode 100644
index 0000000..aca6c09
--- /dev/null
+++ b/Warcraft.NET/Types/HalfFloat.cs
@@ -0,0 +1,235 @@
+using System.Globalization;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Warcraft.NET.Utils;
+
+namespace Warcraft.NET.Types
+{
+ ///
+ /// A half precision (16 bit) floating point value.
+ /// Code copy from https://github.com/sharpdx/SharpDX/blob/master/Source/SharpDX.Mathematics/Half.cs by SharpDX
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public struct HalfFloat
+ {
+ private ushort value;
+
+ ///
+ /// Number of decimal digits of precision.
+ ///
+ public const int PrecisionDigits = 3;
+
+ ///
+ /// Number of bits in the mantissa.
+ ///
+ public const int MantissaBits = 11;
+
+ ///
+ /// Maximum decimal exponent.
+ ///
+ public const int MaximumDecimalExponent = 4;
+
+ ///
+ /// Maximum binary exponent.
+ ///
+ public const int MaximumBinaryExponent = 15;
+
+ ///
+ /// Minimum decimal exponent.
+ ///
+ public const int MinimumDecimalExponent = -4;
+
+ ///
+ /// Minimum binary exponent.
+ ///
+ public const int MinimumBinaryExponent = -14;
+
+ ///
+ /// Exponent radix.
+ ///
+ public const int ExponentRadix = 2;
+
+ ///
+ /// Additional rounding.
+ ///
+ public const int AdditionRounding = 1;
+
+ ///
+ /// Smallest such that 1.0 + epsilon != 1.0
+ ///
+ public static readonly float Epsilon;
+
+ ///
+ /// Maximum value of the number.
+ ///
+ public static readonly float MaxValue;
+
+ ///
+ /// Minimum value of the number.
+ ///
+ public static readonly float MinValue;
+
+ ///
+ /// Initializes a new instance of the structure.
+ ///
+ /// The floating point value that should be stored in 16 bit format.
+ public HalfFloat(float value)
+ {
+ this.value = HalfUtils.Pack(value);
+ }
+
+ ///
+ /// Initializes a new instance of the structure.
+ ///
+ /// The floating point value that should be stored in 16 bit format.
+ public HalfFloat(ushort rawvalue)
+ {
+ this.value = rawvalue;
+ }
+
+ ///
+ /// Gets or sets the raw 16 bit value used to back this half-float.
+ ///
+ public ushort RawValue
+ {
+ get { return value; }
+ set { this.value = value; }
+ }
+
+ ///
+ /// Converts an array of half precision values into full precision values.
+ ///
+ /// The values to be converted.
+ /// An array of converted values.
+ public static float[] ConvertToFloat(HalfFloat[] values)
+ {
+ float[] results = new float[values.Length];
+ for (int i = 0; i < results.Length; i++)
+ results[i] = HalfUtils.Unpack(values[i].RawValue);
+ return results;
+ }
+
+ ///
+ /// Converts an array of full precision values into half precision values.
+ ///
+ /// The values to be converted.
+ /// An array of converted values.
+ public static HalfFloat[] ConvertToHalf(float[] values)
+ {
+ HalfFloat[] results = new HalfFloat[values.Length];
+ for (int i = 0; i < results.Length; i++)
+ results[i] = new HalfFloat(values[i]);
+ return results;
+ }
+
+ ///
+ /// Performs an explicit conversion from to .
+ ///
+ /// The value to be converted.
+ /// The converted value.
+ public static implicit operator HalfFloat(float value)
+ {
+ return new HalfFloat(value);
+ }
+
+ ///
+ /// Performs an implicit conversion from to .
+ ///
+ /// The value to be converted.
+ /// The converted value.
+ public static implicit operator float(HalfFloat value)
+ {
+ return HalfUtils.Unpack(value.value);
+ }
+
+ ///
+ /// Tests for equality between two objects.
+ ///
+ /// The first value to compare.
+ /// The second value to compare.
+ ///
+ /// true if has the same value as ; otherwise, false.
+ public static bool operator ==(HalfFloat left, HalfFloat right)
+ {
+ return left.value == right.value;
+ }
+
+ ///
+ /// Tests for inequality between two objects.
+ ///
+ /// The first value to compare.
+ /// The second value to compare.
+ ///
+ /// true if has a different value than ; otherwise, false.
+ public static bool operator !=(HalfFloat left, HalfFloat right)
+ {
+ return left.value != right.value;
+ }
+
+ ///
+ /// Converts the value of the object to its equivalent string representation.
+ ///
+ /// The string representation of the value of this instance.
+ public override string ToString()
+ {
+ float num = this;
+ return num.ToString(CultureInfo.CurrentCulture);
+ }
+
+ ///
+ /// Returns the hash code for this instance.
+ ///
+ /// A 32-bit signed integer hash code.
+ public override int GetHashCode()
+ {
+ ushort num = value;
+ return (((num * 3) / 2) ^ num);
+ }
+
+ ///
+ /// Determines whether the specified object instances are considered equal.
+ ///
+ ///
+ ///
+ ///
+ /// true if is the same instance as or
+ /// if both are null references or if value1.Equals(value2) returns true; otherwise, false.
+ [MethodImpl((MethodImplOptions)0x100)] // MethodImplOptions.AggressiveInlining
+ public static bool Equals(ref HalfFloat value1, ref HalfFloat value2)
+ {
+ return value1.value == value2.value;
+ }
+
+ ///
+ /// Returns a value that indicates whether the current instance is equal to the specified object.
+ ///
+ /// Object to make the comparison with.
+ ///
+ /// true if the current instance is equal to the specified object; false otherwise.
+ public bool Equals(HalfFloat other)
+ {
+ return other.value == value;
+ }
+
+ ///
+ /// Returns a value that indicates whether the current instance is equal to a specified object.
+ ///
+ /// Object to make the comparison with.
+ ///
+ /// true if the current instance is equal to the specified object; false otherwise.
+ public override bool Equals(object obj)
+ {
+ if (!(obj is HalfFloat))
+ return false;
+
+ return Equals((HalfFloat)obj);
+ }
+
+ static HalfFloat()
+ {
+ Epsilon = 0.0004887581f;
+ MaxValue = 65504f;
+ MinValue = 6.103516E-05f;
+ }
+ }
+}
diff --git a/Warcraft.NET/Utils/HalfUtils.cs b/Warcraft.NET/Utils/HalfUtils.cs
new file mode 100644
index 0000000..16b9de0
--- /dev/null
+++ b/Warcraft.NET/Utils/HalfUtils.cs
@@ -0,0 +1,150 @@
+using System.Runtime.InteropServices;
+
+namespace Warcraft.NET.Utils
+{
+ ///
+ /// Helper class to perform Half/Float conversion.
+ /// Code extract from paper : www.fox-toolkit.org/ftp/fasthalffloatconversion.pdf by Jeroen van der Zijp
+ ///
+ internal class HalfUtils
+ {
+ [StructLayout(LayoutKind.Explicit)]
+ private struct FloatToUint
+ {
+ [FieldOffset(0)]
+ public uint uintValue;
+ [FieldOffset(0)]
+ public float floatValue;
+ }
+
+ ///
+ /// Unpacks the specified h.
+ ///
+ /// The h.
+ ///
+ public static float Unpack(ushort h)
+ {
+ var conv = new FloatToUint();
+ conv.uintValue = HalfToFloatMantissaTable[HalfToFloatOffsetTable[h >> 10] + (((uint)h) & 0x3ff)] + HalfToFloatExponentTable[h >> 10];
+ return conv.floatValue;
+ }
+
+ ///
+ /// Packs the specified f.
+ ///
+ /// The f.
+ ///
+ public static ushort Pack(float f)
+ {
+ FloatToUint conv = new FloatToUint();
+ conv.floatValue = f;
+ return (ushort)(FloatToHalfBaseTable[(conv.uintValue >> 23) & 0x1ff] + ((conv.uintValue & 0x007fffff) >> FloatToHalfShiftTable[(conv.uintValue >> 23) & 0x1ff]));
+ }
+
+ static readonly uint[] HalfToFloatMantissaTable = new uint[2048];
+ static readonly uint[] HalfToFloatExponentTable = new uint[64];
+ static readonly uint[] HalfToFloatOffsetTable = new uint[64];
+ static readonly ushort[] FloatToHalfBaseTable = new ushort[512];
+ static readonly byte[] FloatToHalfShiftTable = new byte[512];
+
+ static HalfUtils()
+ {
+ int i;
+
+ // -------------------------------------------------------------------
+ // Half to Float tables
+ // -------------------------------------------------------------------
+
+ // Mantissa table
+
+ // 0 => 0
+ HalfToFloatMantissaTable[0] = 0;
+
+ // Transform subnormal to normalized
+ for (i = 1; i < 1024; i++)
+ {
+ uint m = ((uint)i) << 13;
+ uint e = 0;
+
+ while ((m & 0x00800000) == 0)
+ {
+ e -= 0x00800000;
+ m <<= 1;
+ }
+ m &= ~0x00800000U;
+ e += 0x38800000;
+ HalfToFloatMantissaTable[i] = m | e;
+ }
+
+ // Normal case
+ for (i = 1024; i < 2048; i++)
+ HalfToFloatMantissaTable[i] = 0x38000000 + (((uint)(i - 1024)) << 13);
+
+ // Exponent table
+
+ // 0 => 0
+ HalfToFloatExponentTable[0] = 0;
+
+ for (i = 1; i < 63; i++)
+ {
+ if (i < 31) // Positive Numbers
+ HalfToFloatExponentTable[i] = ((uint)i) << 23;
+ else // Negative Numbers
+ HalfToFloatExponentTable[i] = 0x80000000 + (((uint)(i - 32)) << 23);
+ }
+ HalfToFloatExponentTable[31] = 0x47800000;
+ HalfToFloatExponentTable[32] = 0x80000000;
+ HalfToFloatExponentTable[63] = 0xC7800000;
+
+ // Offset table
+ HalfToFloatOffsetTable[0] = 0;
+ for (i = 1; i < 64; i++)
+ HalfToFloatOffsetTable[i] = 1024;
+ HalfToFloatOffsetTable[32] = 0;
+
+ // -------------------------------------------------------------------
+ // Float to Half tables
+ // -------------------------------------------------------------------
+
+ for (i = 0; i < 256; i++)
+ {
+ int e = i - 127;
+ if (e < -24)
+ { // Very small numbers map to zero
+ FloatToHalfBaseTable[i | 0x000] = 0x0000;
+ FloatToHalfBaseTable[i | 0x100] = 0x8000;
+ FloatToHalfShiftTable[i | 0x000] = 24;
+ FloatToHalfShiftTable[i | 0x100] = 24;
+ }
+ else if (e < -14)
+ { // Small numbers map to denorms
+ FloatToHalfBaseTable[i | 0x000] = (ushort)((0x0400 >> (-e - 14)));
+ FloatToHalfBaseTable[i | 0x100] = (ushort)((0x0400 >> (-e - 14)) | 0x8000);
+ FloatToHalfShiftTable[i | 0x000] = (byte)(-e - 1);
+ FloatToHalfShiftTable[i | 0x100] = (byte)(-e - 1);
+ }
+ else if (e <= 15)
+ { // Normal numbers just lose precision
+ FloatToHalfBaseTable[i | 0x000] = (ushort)(((e + 15) << 10));
+ FloatToHalfBaseTable[i | 0x100] = (ushort)(((e + 15) << 10) | 0x8000);
+ FloatToHalfShiftTable[i | 0x000] = 13;
+ FloatToHalfShiftTable[i | 0x100] = 13;
+ }
+ else if (e < 128)
+ { // Large numbers map to Infinity
+ FloatToHalfBaseTable[i | 0x000] = 0x7C00;
+ FloatToHalfBaseTable[i | 0x100] = 0xFC00;
+ FloatToHalfShiftTable[i | 0x000] = 24;
+ FloatToHalfShiftTable[i | 0x100] = 24;
+ }
+ else
+ { // Infinity and NaN's stay Infinity and NaN's
+ FloatToHalfBaseTable[i | 0x000] = 0x7C00;
+ FloatToHalfBaseTable[i | 0x100] = 0xFC00;
+ FloatToHalfShiftTable[i | 0x000] = 13;
+ FloatToHalfShiftTable[i | 0x100] = 13;
+ }
+ }
+ }
+ }
+}