corrade-vassal – Rev 1

Subversion Repositories:
Rev:
/*
 * Copyright (c) 2006-2014, openmetaverse.org
 * All rights reserved.
 *
 * - Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * - Neither the name of the openmetaverse.org nor the names
 *   of its contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Net;
using System.Text;
using System.Reflection;

namespace OpenMetaverse
{
    public static partial class Utils
    {
        #region String Arrays

        private static readonly string[] _AssetTypeNames = new string[]
        {
            "texture",    //  0
                "sound",      //  1
                "callcard",   //  2
                "landmark",   //  3
                "script",     //  4
                "clothing",   //  5
                "object",     //  6
                "notecard",   //  7
                "category",   //  8
                "root",       //  9
                "lsltext",    // 10
                "lslbyte",    // 11
                "txtr_tga",   // 12
                "bodypart",   // 13
                "trash",      // 14
                "snapshot",   // 15
                "lstndfnd",   // 16
                "snd_wav",    // 17
                "img_tga",    // 18
                "jpeg",       // 19
                "animatn",    // 20
                "gesture",    // 21
                "simstate",   // 22
            "favorite",   // 23
            "link",       // 24
            "linkfolder", // 25
            String.Empty, // 26
            String.Empty, // 27
            String.Empty, // 28
            String.Empty, // 29
            String.Empty, // 30
            String.Empty, // 31
            String.Empty, // 32
            String.Empty, // 33
            String.Empty, // 34
            String.Empty, // 35
            String.Empty, // 36
            String.Empty, // 37
            String.Empty, // 38
            String.Empty, // 39
            String.Empty, // 40
            String.Empty, // 41
            String.Empty, // 42
            String.Empty, // 43
            String.Empty, // 44
            String.Empty, // 45
            "curoutfit",  // 46
            "outfit",     // 47
            "myoutfits",  // 48
            "mesh",       // 49
        };

        private static readonly string[] _InventoryTypeNames = new string[]
        {
            "texture",    //  0
                "sound",      //  1
                "callcard",   //  2
                "landmark",   //  3
                String.Empty, //  4
                String.Empty, //  5
                "object",     //  6
                "notecard",   //  7
                "category",   //  8
                "root",       //  9
                "script",     // 10
                String.Empty, // 11
                String.Empty, // 12
                String.Empty, // 13
                String.Empty, // 14
                "snapshot",   // 15
                String.Empty, // 16
                "attach",     // 17
                "wearable",   // 18
                "animation",  // 19
                "gesture",    // 20
            String.Empty, // 21
            "mesh"        // 22
        };

        private static readonly string[] _SaleTypeNames = new string[]
        {
            "not",
            "orig",
            "copy",
            "cntn"
        };

        private static readonly string[] _AttachmentPointNames = new string[]
        {
            string.Empty,
            "ATTACH_CHEST",
                "ATTACH_HEAD",
                "ATTACH_LSHOULDER",
                "ATTACH_RSHOULDER",
                "ATTACH_LHAND",
            "ATTACH_RHAND",
                "ATTACH_LFOOT",
                "ATTACH_RFOOT",
                "ATTACH_BACK",
                "ATTACH_PELVIS",
                "ATTACH_MOUTH",
                "ATTACH_CHIN",
                "ATTACH_LEAR",
                "ATTACH_REAR",
                "ATTACH_LEYE",
                "ATTACH_REYE",
                "ATTACH_NOSE",
                "ATTACH_RUARM",
                "ATTACH_RLARM",
                "ATTACH_LUARM",
                "ATTACH_LLARM",
                "ATTACH_RHIP",
                "ATTACH_RULEG",
                "ATTACH_RLLEG",
                "ATTACH_LHIP",
                "ATTACH_LULEG",
                "ATTACH_LLLEG",
                "ATTACH_BELLY",
                "ATTACH_RPEC",
                "ATTACH_LPEC",
                "ATTACH_HUD_CENTER_2",
                "ATTACH_HUD_TOP_RIGHT",
                "ATTACH_HUD_TOP_CENTER",
                "ATTACH_HUD_TOP_LEFT",
                "ATTACH_HUD_CENTER_1",
                "ATTACH_HUD_BOTTOM_LEFT",
                "ATTACH_HUD_BOTTOM",
                "ATTACH_HUD_BOTTOM_RIGHT"
        };

        public static bool InternStrings = false;

        #endregion String Arrays

        #region BytesTo

        /// <summary>
        /// Convert the first two bytes starting in the byte array in
        /// little endian ordering to a signed short integer
        /// </summary>
        /// <param name="bytes">An array two bytes or longer</param>
        /// <returns>A signed short integer, will be zero if a short can't be
        /// read at the given position</returns>
        public static short BytesToInt16(byte[] bytes)
        {
            return BytesToInt16(bytes, 0);
        }

        /// <summary>
        /// Convert the first two bytes starting at the given position in
        /// little endian ordering to a signed short integer
        /// </summary>
        /// <param name="bytes">An array two bytes or longer</param>
        /// <param name="pos">Position in the array to start reading</param>
        /// <returns>A signed short integer, will be zero if a short can't be
        /// read at the given position</returns>
        public static short BytesToInt16(byte[] bytes, int pos)
        {
            if (bytes.Length <= pos + 1) return 0;
            return (short)(bytes[pos] + (bytes[pos + 1] << 8));
        }

        /// <summary>
        /// Convert the first four bytes starting at the given position in
        /// little endian ordering to a signed integer
        /// </summary>
        /// <param name="bytes">An array four bytes or longer</param>
        /// <param name="pos">Position to start reading the int from</param>
        /// <returns>A signed integer, will be zero if an int can't be read
        /// at the given position</returns>
        public static int BytesToInt(byte[] bytes, int pos)
        {
            if (bytes.Length < pos + 4) return 0;
            return (int)(bytes[pos + 0] + (bytes[pos + 1] << 8) + (bytes[pos + 2] << 16) + (bytes[pos + 3] << 24));
        }

        /// <summary>
        /// Convert the first four bytes of the given array in little endian
        /// ordering to a signed integer
        /// </summary>
        /// <param name="bytes">An array four bytes or longer</param>
        /// <returns>A signed integer, will be zero if the array contains
        /// less than four bytes</returns>
        public static int BytesToInt(byte[] bytes)
        {
            return BytesToInt(bytes, 0);
        }

        /// <summary>
        /// Convert the first eight bytes of the given array in little endian
        /// ordering to a signed long integer
        /// </summary>
        /// <param name="bytes">An array eight bytes or longer</param>
        /// <returns>A signed long integer, will be zero if the array contains
        /// less than eight bytes</returns>
        public static long BytesToInt64(byte[] bytes)
        {
            return BytesToInt64(bytes, 0);
        }

        /// <summary>
        /// Convert the first eight bytes starting at the given position in
        /// little endian ordering to a signed long integer
        /// </summary>
        /// <param name="bytes">An array eight bytes or longer</param>
        /// <param name="pos">Position to start reading the long from</param>
        /// <returns>A signed long integer, will be zero if a long can't be read
        /// at the given position</returns>
        public static long BytesToInt64(byte[] bytes, int pos)
        {
            if (bytes.Length < pos + 8) return 0;
            return (long)
                ((long)bytes[pos + 0] +
                ((long)bytes[pos + 1] << 8) +
                ((long)bytes[pos + 2] << 16) +
                ((long)bytes[pos + 3] << 24) +
                ((long)bytes[pos + 4] << 32) +
                ((long)bytes[pos + 5] << 40) +
                ((long)bytes[pos + 6] << 48) +
                ((long)bytes[pos + 7] << 56));
        }

        /// <summary>
        /// Convert the first two bytes starting at the given position in
        /// little endian ordering to an unsigned short
        /// </summary>
        /// <param name="bytes">Byte array containing the ushort</param>
        /// <param name="pos">Position to start reading the ushort from</param>
        /// <returns>An unsigned short, will be zero if a ushort can't be read
        /// at the given position</returns>
        public static ushort BytesToUInt16(byte[] bytes, int pos)
        {
            if (bytes.Length <= pos + 1) return 0;
            return (ushort)(bytes[pos] + (bytes[pos + 1] << 8));
        }

        /// <summary>
        /// Convert two bytes in little endian ordering to an unsigned short
        /// </summary>
        /// <param name="bytes">Byte array containing the ushort</param>
        /// <returns>An unsigned short, will be zero if a ushort can't be
        /// read</returns>
        public static ushort BytesToUInt16(byte[] bytes)
        {
            return BytesToUInt16(bytes, 0);
        }

        /// <summary>
        /// Convert the first four bytes starting at the given position in
        /// little endian ordering to an unsigned integer
        /// </summary>
        /// <param name="bytes">Byte array containing the uint</param>
        /// <param name="pos">Position to start reading the uint from</param>
        /// <returns>An unsigned integer, will be zero if a uint can't be read
        /// at the given position</returns>
        public static uint BytesToUInt(byte[] bytes, int pos)
        {
            if (bytes.Length < pos + 4) return 0;
            return (uint)(bytes[pos + 0] + (bytes[pos + 1] << 8) + (bytes[pos + 2] << 16) + (bytes[pos + 3] << 24));
        }

        /// <summary>
        /// Convert the first four bytes of the given array in little endian
        /// ordering to an unsigned integer
        /// </summary>
        /// <param name="bytes">An array four bytes or longer</param>
        /// <returns>An unsigned integer, will be zero if the array contains
        /// less than four bytes</returns>
        public static uint BytesToUInt(byte[] bytes)
        {
            return BytesToUInt(bytes, 0);
        }

        /// <summary>
        /// Convert the first eight bytes of the given array in little endian
        /// ordering to an unsigned 64-bit integer
        /// </summary>
        /// <param name="bytes">An array eight bytes or longer</param>
        /// <returns>An unsigned 64-bit integer, will be zero if the array
        /// contains less than eight bytes</returns>
        public static ulong BytesToUInt64(byte[] bytes)
        {
            if (bytes.Length < 8) return 0;
            return (ulong)
                ((ulong)bytes[0] +
                ((ulong)bytes[1] << 8) +
                ((ulong)bytes[2] << 16) +
                ((ulong)bytes[3] << 24) +
                ((ulong)bytes[4] << 32) +
                ((ulong)bytes[5] << 40) +
                ((ulong)bytes[6] << 48) +
                ((ulong)bytes[7] << 56));
        }

        /// <summary>
        /// Convert four bytes in little endian ordering to a floating point
        /// value
        /// </summary>
        /// <param name="bytes">Byte array containing a little ending floating
        /// point value</param>
        /// <param name="pos">Starting position of the floating point value in
        /// the byte array</param>
        /// <returns>Single precision value</returns>
        public static float BytesToFloat(byte[] bytes, int pos)
        {
            if (!BitConverter.IsLittleEndian)
            {
                byte[] newBytes = new byte[4];
                Buffer.BlockCopy(bytes, pos, newBytes, 0, 4);
                Array.Reverse(newBytes, 0, 4);
                return BitConverter.ToSingle(newBytes, 0);
            }
            else
            {
                return BitConverter.ToSingle(bytes, pos);
            }
        }

        public static double BytesToDouble(byte[] bytes, int pos)
        {
            if (!BitConverter.IsLittleEndian)
            {
                byte[] newBytes = new byte[8];
                Buffer.BlockCopy(bytes, pos, newBytes, 0, 8);
                Array.Reverse(newBytes, 0, 8);
                return BitConverter.ToDouble(newBytes, 0);
            }
            else
            {
                return BitConverter.ToDouble(bytes, pos);
            }
        }

        #endregion BytesTo

        #region ToBytes

        public static byte[] Int16ToBytes(short value)
        {
            byte[] bytes = new byte[2];
            bytes[0] = (byte)(value % 256);
            bytes[1] = (byte)((value >> 8) % 256);
            return bytes;
        }

        public static void Int16ToBytes(short value, byte[] dest, int pos)
        {
            dest[pos] = (byte)(value % 256);
            dest[pos + 1] = (byte)((value >> 8) % 256);
        }

        public static byte[] UInt16ToBytes(ushort value)
        {
            byte[] bytes = new byte[2];
            bytes[0] = (byte)(value % 256);
            bytes[1] = (byte)((value >> 8) % 256);
            return bytes;
        }

        public static void UInt16ToBytes(ushort value, byte[] dest, int pos)
        {
            dest[pos] = (byte)(value % 256);
            dest[pos + 1] = (byte)((value >> 8) % 256);
        }

        public static void UInt16ToBytesBig(ushort value, byte[] dest, int pos)
        {
            dest[pos] = (byte)((value >> 8) % 256);
            dest[pos + 1] = (byte)(value % 256);
        }

        /// <summary>
        /// Convert an integer to a byte array in little endian format
        /// </summary>
        /// <param name="value">The integer to convert</param>
        /// <returns>A four byte little endian array</returns>
        public static byte[] IntToBytes(int value)
        {
            byte[] bytes = new byte[4];

            bytes[0] = (byte)(value % 256);
            bytes[1] = (byte)((value >> 8) % 256);
            bytes[2] = (byte)((value >> 16) % 256);
            bytes[3] = (byte)((value >> 24) % 256);

            return bytes;
        }

        /// <summary>
        /// Convert an integer to a byte array in big endian format
        /// </summary>
        /// <param name="value">The integer to convert</param>
        /// <returns>A four byte big endian array</returns>
        public static byte[] IntToBytesBig(int value)
        {
            byte[] bytes = new byte[4];

            bytes[0] = (byte)((value >> 24) % 256);
            bytes[1] = (byte)((value >> 16) % 256);
            bytes[2] = (byte)((value >> 8) % 256);
            bytes[3] = (byte)(value % 256);

            return bytes;
        }

        public static void IntToBytes(int value, byte[] dest, int pos)
        {
            dest[pos] = (byte)(value % 256);
            dest[pos + 1] = (byte)((value >> 8) % 256);
            dest[pos + 2] = (byte)((value >> 16) % 256);
            dest[pos + 3] = (byte)((value >> 24) % 256);
        }

        public static byte[] UIntToBytes(uint value)
        {
            byte[] bytes = new byte[4];
            bytes[0] = (byte)(value % 256);
            bytes[1] = (byte)((value >> 8) % 256);
            bytes[2] = (byte)((value >> 16) % 256);
            bytes[3] = (byte)((value >> 24) % 256);
            return bytes;
        }

        public static void UIntToBytes(uint value, byte[] dest, int pos)
        {
            dest[pos] = (byte)(value % 256);
            dest[pos + 1] = (byte)((value >> 8) % 256);
            dest[pos + 2] = (byte)((value >> 16) % 256);
            dest[pos + 3] = (byte)((value >> 24) % 256);
        }

        public static void UIntToBytesBig(uint value, byte[] dest, int pos)
        {
            dest[pos] = (byte)((value >> 24) % 256);
            dest[pos + 1] = (byte)((value >> 16) % 256);
            dest[pos + 2] = (byte)((value >> 8) % 256);
            dest[pos + 3] = (byte)(value % 256);
        }

        /// <summary>
        /// Convert a 64-bit integer to a byte array in little endian format
        /// </summary>
        /// <param name="value">The value to convert</param>
        /// <returns>An 8 byte little endian array</returns>
        public static byte[] Int64ToBytes(long value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            if (!BitConverter.IsLittleEndian)
                Array.Reverse(bytes);

            return bytes;
        }

        public static void Int64ToBytes(long value, byte[] dest, int pos)
        {
            byte[] bytes = Int64ToBytes(value);
            Buffer.BlockCopy(bytes, 0, dest, pos, 8);
        }

        /// <summary>
        /// Convert a 64-bit unsigned integer to a byte array in little endian
        /// format
        /// </summary>
        /// <param name="value">The value to convert</param>
        /// <returns>An 8 byte little endian array</returns>
        public static byte[] UInt64ToBytes(ulong value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            if (!BitConverter.IsLittleEndian)
                Array.Reverse(bytes);

            return bytes;
        }

        public static byte[] UInt64ToBytesBig(ulong value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            if (BitConverter.IsLittleEndian)
                Array.Reverse(bytes);

            return bytes;
        }

        public static void UInt64ToBytes(ulong value, byte[] dest, int pos)
        {
            byte[] bytes = UInt64ToBytes(value);
            Buffer.BlockCopy(bytes, 0, dest, pos, 8);
        }

        public static void UInt64ToBytesBig(ulong value, byte[] dest, int pos)
        {
            byte[] bytes = UInt64ToBytesBig(value);
            Buffer.BlockCopy(bytes, 0, dest, pos, 8);
        }

        /// <summary>
        /// Convert a floating point value to four bytes in little endian
        /// ordering
        /// </summary>
        /// <param name="value">A floating point value</param>
        /// <returns>A four byte array containing the value in little endian
        /// ordering</returns>
        public static byte[] FloatToBytes(float value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            if (!BitConverter.IsLittleEndian)
                Array.Reverse(bytes);
            return bytes;
        }

        public static void FloatToBytes(float value, byte[] dest, int pos)
        {
            byte[] bytes = FloatToBytes(value);
            Buffer.BlockCopy(bytes, 0, dest, pos, 4);
        }

        public static byte[] DoubleToBytes(double value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            if (!BitConverter.IsLittleEndian)
                Array.Reverse(bytes);
            return bytes;
        }

        public static byte[] DoubleToBytesBig(double value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            if (BitConverter.IsLittleEndian)
                Array.Reverse(bytes);
            return bytes;
        }

        public static void DoubleToBytes(double value, byte[] dest, int pos)
        {
            byte[] bytes = DoubleToBytes(value);
            Buffer.BlockCopy(bytes, 0, dest, pos, 8);
        }

        #endregion ToBytes

        #region Strings

        /// <summary>
        /// Converts an unsigned integer to a hexadecimal string
        /// </summary>
        /// <param name="i">An unsigned integer to convert to a string</param>
        /// <returns>A hexadecimal string 10 characters long</returns>
        /// <example>0x7fffffff</example>
        public static string UIntToHexString(uint i)
        {
            return string.Format("{0:x8}", i);
        }

        /// <summary>
        /// Convert a variable length UTF8 byte array to a string
        /// </summary>
        /// <param name="bytes">The UTF8 encoded byte array to convert</param>
        /// <returns>The decoded string</returns>
        public static string BytesToString(byte[] bytes)
        {
            if (bytes.Length > 0 && bytes[bytes.Length - 1] == 0x00)
                return GetString(bytes, 0, bytes.Length - 1);
            else
                return GetString(bytes, 0, bytes.Length);
        }

        public static string BytesToString(byte[] bytes, int index, int count)
        {
            if (bytes.Length > index + count && bytes[index + count - 1] == 0x00)
                return GetString(bytes, index, count - 1);
            else
                return GetString(bytes, index, count);
        }

        private static string GetString(byte[] bytes, int index, int count)
        {
            string cnv = UTF8Encoding.UTF8.GetString(bytes, index, count);
            return InternStrings ? string.Intern(cnv) : cnv;
        }

        /// <summary>
        /// Converts a byte array to a string containing hexadecimal characters
        /// </summary>
        /// <param name="bytes">The byte array to convert to a string</param>
        /// <param name="fieldName">The name of the field to prepend to each
        /// line of the string</param>
        /// <returns>A string containing hexadecimal characters on multiple
        /// lines. Each line is prepended with the field name</returns>
        public static string BytesToHexString(byte[] bytes, string fieldName)
        {
            string cnv = BytesToHexString(bytes, bytes.Length, fieldName);
            return InternStrings ? string.Intern(cnv) : cnv;
        }

        /// <summary>
        /// Converts a byte array to a string containing hexadecimal characters
        /// </summary>
        /// <param name="bytes">The byte array to convert to a string</param>
        /// <param name="length">Number of bytes in the array to parse</param>
        /// <param name="fieldName">A string to prepend to each line of the hex
        /// dump</param>
        /// <returns>A string containing hexadecimal characters on multiple
        /// lines. Each line is prepended with the field name</returns>
        public static string BytesToHexString(byte[] bytes, int length, string fieldName)
        {
            StringBuilder output = new StringBuilder();

            for (int i = 0; i < length; i += 16)
            {
                if (i != 0)
                    output.Append('\n');

                if (!String.IsNullOrEmpty(fieldName))
                {
                    output.Append(fieldName);
                    output.Append(": ");
                }

                for (int j = 0; j < 16; j++)
                {
                    if ((i + j) < length)
                    {
                        if (j != 0)
                            output.Append(' ');

                        output.Append(String.Format("{0:X2}", bytes[i + j]));
                    }
                }
            }

            return output.ToString();
        }

        /// <summary>
        /// Convert a string to a UTF8 encoded byte array
        /// </summary>
        /// <param name="str">The string to convert</param>
        /// <returns>A null-terminated UTF8 byte array</returns>
        public static byte[] StringToBytes(string str)
        {
            if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
            if (!str.EndsWith("\0")) { str += "\0"; }
            return System.Text.UTF8Encoding.UTF8.GetBytes(str);
        }

        /// <summary>
        /// Converts a string containing hexadecimal characters to a byte array
        /// </summary>
        /// <param name="hexString">String containing hexadecimal characters</param>
        /// <param name="handleDirty">If true, gracefully handles null, empty and
        /// uneven strings as well as stripping unconvertable characters</param>
        /// <returns>The converted byte array</returns>
        public static byte[] HexStringToBytes(string hexString, bool handleDirty)
        {
            if (handleDirty)
            {
                if (String.IsNullOrEmpty(hexString))
                    return Utils.EmptyBytes;

                StringBuilder stripped = new StringBuilder(hexString.Length);
                char c;

                // remove all non A-F, 0-9, characters
                for (int i = 0; i < hexString.Length; i++)
                {
                    c = hexString[i];
                    if (IsHexDigit(c))
                        stripped.Append(c);
                }

                hexString = stripped.ToString();

                // if odd number of characters, discard last character
                if (hexString.Length % 2 != 0)
                {
                    hexString = hexString.Substring(0, hexString.Length - 1);
                }
            }

            int byteLength = hexString.Length / 2;
            byte[] bytes = new byte[byteLength];
            int j = 0;

            for (int i = 0; i < bytes.Length; i++)
            {
                bytes[i] = HexToByte(hexString.Substring(j, 2));
                j += 2;
            }

            return bytes;
        }

        /// <summary>
        /// Returns true is c is a hexadecimal digit (A-F, a-f, 0-9)
        /// </summary>
        /// <param name="c">Character to test</param>
        /// <returns>true if hex digit, false if not</returns>
        private static bool IsHexDigit(Char c)
        {
            const int numA = 65;
            const int num0 = 48;

            int numChar;

            c = Char.ToUpper(c);
            numChar = Convert.ToInt32(c);

            if (numChar >= numA && numChar < (numA + 6))
                return true;
            else if (numChar >= num0 && numChar < (num0 + 10))
                return true;
            else
                return false;
        }

        /// <summary>
        /// Converts 1 or 2 character string into equivalant byte value
        /// </summary>
        /// <param name="hex">1 or 2 character string</param>
        /// <returns>byte</returns>
        private static byte HexToByte(string hex)
        {
            if (hex.Length > 2 || hex.Length <= 0)
                throw new ArgumentException("hex must be 1 or 2 characters in length");
            byte newByte = Byte.Parse(hex, System.Globalization.NumberStyles.HexNumber);
            return newByte;
        }

        #endregion Strings

        #region Packed Values

        /// <summary>
        /// Convert a float value to a byte given a minimum and maximum range
        /// </summary>
        /// <param name="val">Value to convert to a byte</param>
        /// <param name="lower">Minimum value range</param>
        /// <param name="upper">Maximum value range</param>
        /// <returns>A single byte representing the original float value</returns>
        public static byte FloatToByte(float val, float lower, float upper)
        {
            val = Utils.Clamp(val, lower, upper);
            // Normalize the value
            val -= lower;
            val /= (upper - lower);

            return (byte)Math.Floor(val * (float)byte.MaxValue);
        }

        /// <summary>
        /// Convert a byte to a float value given a minimum and maximum range
        /// </summary>
        /// <param name="bytes">Byte array to get the byte from</param>
        /// <param name="pos">Position in the byte array the desired byte is at</param>
        /// <param name="lower">Minimum value range</param>
        /// <param name="upper">Maximum value range</param>
        /// <returns>A float value inclusively between lower and upper</returns>
        public static float ByteToFloat(byte[] bytes, int pos, float lower, float upper)
        {
            if (bytes.Length <= pos) return 0;
            return ByteToFloat(bytes[pos], lower, upper);
        }

        /// <summary>
        /// Convert a byte to a float value given a minimum and maximum range
        /// </summary>
        /// <param name="val">Byte to convert to a float value</param>
        /// <param name="lower">Minimum value range</param>
        /// <param name="upper">Maximum value range</param>
        /// <returns>A float value inclusively between lower and upper</returns>
        public static float ByteToFloat(byte val, float lower, float upper)
        {
            const float ONE_OVER_BYTEMAX = 1.0f / (float)byte.MaxValue;

            float fval = (float)val * ONE_OVER_BYTEMAX;
            float delta = (upper - lower);
            fval *= delta;
            fval += lower;

            // Test for values very close to zero
            float error = delta * ONE_OVER_BYTEMAX;
            if (Math.Abs(fval) < error)
                fval = 0.0f;

            return fval;
        }

        public static float UInt16ToFloat(byte[] bytes, int pos, float lower, float upper)
        {
            ushort val = BytesToUInt16(bytes, pos);
            return UInt16ToFloat(val, lower, upper);
        }

        public static float UInt16ToFloat(ushort val, float lower, float upper)
        {
            const float ONE_OVER_U16_MAX = 1.0f / (float)UInt16.MaxValue;

            float fval = (float)val * ONE_OVER_U16_MAX;
            float delta = upper - lower;
            fval *= delta;
            fval += lower;

            // Make sure zeroes come through as zero
            float maxError = delta * ONE_OVER_U16_MAX;
            if (Math.Abs(fval) < maxError)
                fval = 0.0f;

            return fval;
        }

        public static ushort FloatToUInt16(float value, float lower, float upper)
        {
            float delta = upper - lower;
            value -= lower;
            value /= delta;
            value *= (float)UInt16.MaxValue;

            return (ushort)value;
        }

        #endregion Packed Values

        #region TryParse

        /// <summary>
        /// Attempts to parse a floating point value from a string, using an
        /// EN-US number format
        /// </summary>
        /// <param name="s">String to parse</param>
        /// <param name="result">Resulting floating point number</param>
        /// <returns>True if the parse was successful, otherwise false</returns>
        public static bool TryParseSingle(string s, out float result)
        {
            return Single.TryParse(s, System.Globalization.NumberStyles.Float, EnUsCulture.NumberFormat, out result);
        }

        /// <summary>
        /// Attempts to parse a floating point value from a string, using an
        /// EN-US number format
        /// </summary>
        /// <param name="s">String to parse</param>
        /// <param name="result">Resulting floating point number</param>
        /// <returns>True if the parse was successful, otherwise false</returns>
        public static bool TryParseDouble(string s, out double result)
        {
            // NOTE: Double.TryParse can't parse Double.[Min/Max]Value.ToString(), see:
            // http://blogs.msdn.com/bclteam/archive/2006/05/24/598169.aspx
            return Double.TryParse(s, System.Globalization.NumberStyles.Float, EnUsCulture.NumberFormat, out result);
        }

        /// <summary>
        /// Tries to parse an unsigned 32-bit integer from a hexadecimal string
        /// </summary>
        /// <param name="s">String to parse</param>
        /// <param name="result">Resulting integer</param>
        /// <returns>True if the parse was successful, otherwise false</returns>
        public static bool TryParseHex(string s, out uint result)
        {
            return UInt32.TryParse(s, System.Globalization.NumberStyles.HexNumber, EnUsCulture.NumberFormat, out result);
        }

        #endregion TryParse

        #region Enum String Conversion

        /// <summary>
        /// Returns text specified in EnumInfo attribute of the enumerator
        /// To add the text use [EnumInfo(Text = "Some nice text here")] before declaration
        /// of enum values
        /// </summary>
        /// <param name="value">Enum value</param>
        /// <returns>Text representation of the enum</returns>
        public static string EnumToText(Enum value)
        {
            // Get the type
            Type type = value.GetType();

            // Get fieldinfo for this type
            FieldInfo fieldInfo = type.GetField(value.ToString());

            // Find extended attributes, if any
            EnumInfoAttribute[] attribs = (EnumInfoAttribute[])fieldInfo.GetCustomAttributes(typeof(EnumInfoAttribute), false);

            return attribs.Length > 0 ? attribs[0].Text : value.ToString();
        }

        /// <summary>
        /// Takes an AssetType and returns the string representation
        /// </summary>
        /// <param name="type">The source <seealso cref="AssetType"/></param>
        /// <returns>The string version of the AssetType</returns>
        public static string AssetTypeToString(AssetType type)
        {
            return _AssetTypeNames[(int)type];
        }

        /// <summary>
        /// Translate a string name of an AssetType into the proper Type
        /// </summary>
        /// <param name="type">A string containing the AssetType name</param>
        /// <returns>The AssetType which matches the string name, or AssetType.Unknown if no match was found</returns>
        public static AssetType StringToAssetType(string type)
        {
            for (int i = 0; i < _AssetTypeNames.Length; i++)
            {
                if (_AssetTypeNames[i] == type)
                    return (AssetType)i;
            }

            return AssetType.Unknown;
        }

        /// <summary>
        /// Convert an InventoryType to a string
        /// </summary>
        /// <param name="type">The <seealso cref="T:InventoryType"/> to convert</param>
        /// <returns>A string representation of the source</returns>
        public static string InventoryTypeToString(InventoryType type)
        {
            return _InventoryTypeNames[(int)type];
        }

        /// <summary>
        /// Convert a string into a valid InventoryType
        /// </summary>
        /// <param name="type">A string representation of the InventoryType to convert</param>
        /// <returns>A InventoryType object which matched the type</returns>
        public static InventoryType StringToInventoryType(string type)
        {
            for (int i = 0; i < _InventoryTypeNames.Length; i++)
            {
                if (_InventoryTypeNames[i] == type)
                    return (InventoryType)i;
            }

            return InventoryType.Unknown;
        }

        /// <summary>
        /// Convert a SaleType to a string
        /// </summary>
        /// <param name="type">The <seealso cref="T:SaleType"/> to convert</param>
        /// <returns>A string representation of the source</returns>
        public static string SaleTypeToString(SaleType type)
        {
            return _SaleTypeNames[(int)type];
        }

        /// <summary>
        /// Convert a string into a valid SaleType
        /// </summary>
        /// <param name="value">A string representation of the SaleType to convert</param>
        /// <returns>A SaleType object which matched the type</returns>
        public static SaleType StringToSaleType(string value)
        {
            for (int i = 0; i < _SaleTypeNames.Length; i++)
            {
                if (value == _SaleTypeNames[i])
                    return (SaleType)i;
            }

            return SaleType.Not;
        }

        /// <summary>
        /// Converts a string used in LLSD to AttachmentPoint type
        /// </summary>
        /// <param name="value">String representation of AttachmentPoint to convert</param>
        /// <returns>AttachmentPoint enum</returns>
        public static AttachmentPoint StringToAttachmentPoint(string value)
        {
            for (int i = 0; i < _AttachmentPointNames.Length; i++)
            {
                if (value == _AttachmentPointNames[i])
                    return (AttachmentPoint)i;
            }

            return AttachmentPoint.Default;
        }

        #endregion Enum String Conversion

        #region Miscellaneous

        /// <summary>
        /// Copy a byte array
        /// </summary>
        /// <param name="bytes">Byte array to copy</param>
        /// <returns>A copy of the given byte array</returns>
        public static byte[] CopyBytes(byte[] bytes)
        {
            if (bytes == null)
                return null;

            byte[] newBytes = new byte[bytes.Length];
            Buffer.BlockCopy(bytes, 0, newBytes, 0, bytes.Length);
            return newBytes;
        }

        /// <summary>
        /// Packs to 32-bit unsigned integers in to a 64-bit unsigned integer
        /// </summary>
        /// <param name="a">The left-hand (or X) value</param>
        /// <param name="b">The right-hand (or Y) value</param>
        /// <returns>A 64-bit integer containing the two 32-bit input values</returns>
        public static ulong UIntsToLong(uint a, uint b)
        {
            return ((ulong)a << 32) | (ulong)b;
        }

        /// <summary>
        /// Unpacks two 32-bit unsigned integers from a 64-bit unsigned integer
        /// </summary>
        /// <param name="a">The 64-bit input integer</param>
        /// <param name="b">The left-hand (or X) output value</param>
        /// <param name="c">The right-hand (or Y) output value</param>
        public static void LongToUInts(ulong a, out uint b, out uint c)
        {
            b = (uint)(a >> 32);
            c = (uint)(a & 0x00000000FFFFFFFF);
        }

        /// <summary>
        /// Convert an IP address object to an unsigned 32-bit integer
        /// </summary>
        /// <param name="address">IP address to convert</param>
        /// <returns>32-bit unsigned integer holding the IP address bits</returns>
        public static uint IPToUInt(System.Net.IPAddress address)
        {
            byte[] bytes = address.GetAddressBytes();
            return (uint)((bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0]);
        }

        /// <summary>
        /// Gets a unix timestamp for the current time
        /// </summary>
        /// <returns>An unsigned integer representing a unix timestamp for now</returns>
        public static uint GetUnixTime()
        {
            return (uint)(DateTime.UtcNow - Epoch).TotalSeconds;
        }

        /// <summary>
        /// Convert a UNIX timestamp to a native DateTime object
        /// </summary>
        /// <param name="timestamp">An unsigned integer representing a UNIX
        /// timestamp</param>
        /// <returns>A DateTime object containing the same time specified in
        /// the given timestamp</returns>
        public static DateTime UnixTimeToDateTime(uint timestamp)
        {
            DateTime dateTime = Epoch;

            // Add the number of seconds in our UNIX timestamp
            dateTime = dateTime.AddSeconds(timestamp);

            return dateTime;
        }

        /// <summary>
        /// Convert a UNIX timestamp to a native DateTime object
        /// </summary>
        /// <param name="timestamp">A signed integer representing a UNIX
        /// timestamp</param>
        /// <returns>A DateTime object containing the same time specified in
        /// the given timestamp</returns>
        public static DateTime UnixTimeToDateTime(int timestamp)
        {
            return UnixTimeToDateTime((uint)timestamp);
        }

        /// <summary>
        /// Convert a native DateTime object to a UNIX timestamp
        /// </summary>
        /// <param name="time">A DateTime object you want to convert to a 
        /// timestamp</param>
        /// <returns>An unsigned integer representing a UNIX timestamp</returns>
        public static uint DateTimeToUnixTime(DateTime time)
        {
            TimeSpan ts = (time - new DateTime(1970, 1, 1, 0, 0, 0));
            return (uint)ts.TotalSeconds;
        }

        /// <summary>
        /// Swap two values
        /// </summary>
        /// <typeparam name="T">Type of the values to swap</typeparam>
        /// <param name="lhs">First value</param>
        /// <param name="rhs">Second value</param>
        public static void Swap<T>(ref T lhs, ref T rhs)
        {
            T temp = lhs;
            lhs = rhs;
            rhs = temp;
        }

        /// <summary>
        /// Try to parse an enumeration value from a string
        /// </summary>
        /// <typeparam name="T">Enumeration type</typeparam>
        /// <param name="strType">String value to parse</param>
        /// <param name="result">Enumeration value on success</param>
        /// <returns>True if the parsing succeeded, otherwise false</returns>
        public static bool EnumTryParse<T>(string strType, out T result)
        {
            Type t = typeof(T);

            if (Enum.IsDefined(t, strType))
            {
                result = (T)Enum.Parse(t, strType, true);
                return true;
            }
            else
            {
                foreach (string value in Enum.GetNames(typeof(T)))
                {
                    if (value.Equals(strType, StringComparison.OrdinalIgnoreCase))
                    {
                        result = (T)Enum.Parse(typeof(T), value);
                        return true;
                    }
                }
                result = default(T);
                return false;
            }
        }

        /// <summary>
        /// Swaps the high and low words in a byte. Converts aaaabbbb to bbbbaaaa
        /// </summary>
        /// <param name="value">Byte to swap the words in</param>
        /// <returns>Byte value with the words swapped</returns>
        public static byte SwapWords(byte value)
        {
            return (byte)(((value & 0xF0) >> 4) | ((value & 0x0F) << 4));
        }

        /// <summary>
        /// Attempts to convert a string representation of a hostname or IP
        /// address to a <seealso cref="System.Net.IPAddress"/>
        /// </summary>
        /// <param name="hostname">Hostname to convert to an IPAddress</param>
        /// <returns>Converted IP address object, or null if the conversion
        /// failed</returns>
        public static IPAddress HostnameToIPv4(string hostname)
        {
            // Is it already a valid IP?
            IPAddress ip;
            if (IPAddress.TryParse(hostname, out ip))
                return ip;

            IPAddress[] hosts = Dns.GetHostEntry(hostname).AddressList;

            for (int i = 0; i < hosts.Length; i++)
            {
                IPAddress host = hosts[i];

                if (host.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    return host;
            }

            return null;
        }

        #endregion Miscellaneous
    }
}