diff --git a/ArrayFill.cs b/ArrayFill.cs index a0c3687..53edd14 100644 --- a/ArrayFill.cs +++ b/ArrayFill.cs @@ -27,55 +27,54 @@ using System; using System.Text; -namespace Aaru.Helpers +namespace Aaru.Helpers; + +public static partial class ArrayHelpers { - public static partial class ArrayHelpers + /// Fills an array with the specified value + /// Array + /// Value + /// Array type + public static void ArrayFill(T[] destinationArray, T value) => ArrayFill(destinationArray, new[] { - /// Fills an array with the specified value - /// Array - /// Value - /// Array type - public static void ArrayFill(T[] destinationArray, T value) => ArrayFill(destinationArray, new[] - { - value - }); + value + }); - /// Fills an array with the contents of the specified array - /// Array - /// Value - /// Array type - public static void ArrayFill(T[] destinationArray, T[] value) - { - if(destinationArray == null) - throw new ArgumentNullException(nameof(destinationArray)); + /// Fills an array with the contents of the specified array + /// Array + /// Value + /// Array type + public static void ArrayFill(T[] destinationArray, T[] value) + { + if(destinationArray == null) + throw new ArgumentNullException(nameof(destinationArray)); - if(value.Length > destinationArray.Length) - throw new ArgumentException("Length of value array must not be more than length of destination"); + if(value.Length > destinationArray.Length) + throw new ArgumentException("Length of value array must not be more than length of destination"); - // set the initial array value - Array.Copy(value, destinationArray, value.Length); + // set the initial array value + Array.Copy(value, destinationArray, value.Length); - int arrayToFillHalfLength = destinationArray.Length / 2; - int copyLength; + int arrayToFillHalfLength = destinationArray.Length / 2; + int copyLength; - for(copyLength = value.Length; copyLength < arrayToFillHalfLength; copyLength <<= 1) - Array.Copy(destinationArray, 0, destinationArray, copyLength, copyLength); + for(copyLength = value.Length; copyLength < arrayToFillHalfLength; copyLength <<= 1) + Array.Copy(destinationArray, 0, destinationArray, copyLength, copyLength); - Array.Copy(destinationArray, 0, destinationArray, copyLength, destinationArray.Length - copyLength); - } + Array.Copy(destinationArray, 0, destinationArray, copyLength, destinationArray.Length - copyLength); + } - /// Converts a byte array to its hexadecimal representation - /// Byte array - /// true to use uppercase - /// - public static string ByteArrayToHex(byte[] array, bool upper = false) - { - var sb = new StringBuilder(); + /// Converts a byte array to its hexadecimal representation + /// Byte array + /// true to use uppercase + /// + public static string ByteArrayToHex(byte[] array, bool upper = false) + { + var sb = new StringBuilder(); - for(long i = 0; i < array.LongLength; i++) - sb.AppendFormat("{0:x2}", array[i]); + for(long i = 0; i < array.LongLength; i++) + sb.AppendFormat("{0:x2}", array[i]); - return upper ? sb.ToString().ToUpper() : sb.ToString(); - } + return upper ? sb.ToString().ToUpper() : sb.ToString(); } } \ No newline at end of file diff --git a/ArrayIsEmpty.cs b/ArrayIsEmpty.cs index 77a6f23..93aa330 100644 --- a/ArrayIsEmpty.cs +++ b/ArrayIsEmpty.cs @@ -32,19 +32,18 @@ using System.Linq; -namespace Aaru.Helpers -{ - /// Helper operations to work with arrays - public static partial class ArrayHelpers - { - /// Checks if an array is null, filled with the NULL byte (0x00) or ASCII whitespace (0x20) - /// Array - /// True if null or whitespace - public static bool ArrayIsNullOrWhiteSpace(byte[] array) => array?.All(b => b == 0x00 || b == 0x20) != false; +namespace Aaru.Helpers; - /// Checks if an array is null or filled with the NULL byte (0x00) - /// Array - /// True if null - public static bool ArrayIsNullOrEmpty(byte[] array) => array?.All(b => b == 0x00) != false; - } +/// Helper operations to work with arrays +public static partial class ArrayHelpers +{ + /// Checks if an array is null, filled with the NULL byte (0x00) or ASCII whitespace (0x20) + /// Array + /// True if null or whitespace + public static bool ArrayIsNullOrWhiteSpace(byte[] array) => array?.All(b => b == 0x00 || b == 0x20) != false; + + /// Checks if an array is null or filled with the NULL byte (0x00) + /// Array + /// True if null + public static bool ArrayIsNullOrEmpty(byte[] array) => array?.All(b => b == 0x00) != false; } \ No newline at end of file diff --git a/BigEndianBitConverter.cs b/BigEndianBitConverter.cs index 34e6839..03cf0ad 100644 --- a/BigEndianBitConverter.cs +++ b/BigEndianBitConverter.cs @@ -33,292 +33,291 @@ using System; using System.Linq; -namespace Aaru.Helpers +namespace Aaru.Helpers; + +/// +/// Converts base data types to an array of bytes, and an array of bytes to base data types. All info taken from +/// the meta data of System.BitConverter. This implementation allows for Endianness consideration. +/// +public static class BigEndianBitConverter { + /// Converts the specified double-precision floating point number to a 64-bit signed integer. + /// The number to convert. + /// A 64-bit signed integer whose value is equivalent to value. + /// It is not currently implemented + public static long DoubleToInt64Bits(double value) => throw new NotImplementedException(); + + /// Returns the specified Boolean value as an array of bytes. + /// A Boolean value. + /// An array of bytes with length 1. + public static byte[] GetBytes(bool value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified Unicode character value as an array of bytes. + /// A character to convert. + /// An array of bytes with length 2. + public static byte[] GetBytes(char value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified double-precision floating point value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 8. + public static byte[] GetBytes(double value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified single-precision floating point value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 4. + public static byte[] GetBytes(float value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 32-bit signed integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 4. + public static byte[] GetBytes(int value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 64-bit signed integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 8. + public static byte[] GetBytes(long value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 16-bit signed integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 2. + public static byte[] GetBytes(short value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 32-bit unsigned integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 4. + public static byte[] GetBytes(uint value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 64-bit unsigned integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 8. + public static byte[] GetBytes(ulong value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Returns the specified 16-bit unsigned integer value as an array of bytes. + /// The number to convert. + /// An array of bytes with length 2. + public static byte[] GetBytes(ushort value) => BitConverter.GetBytes(value).Reverse().ToArray(); + + /// Converts the specified 64-bit signed integer to a double-precision floating point number. + /// The number to convert. + /// A double-precision floating point number whose value is equivalent to value. + public static double Int64BitsToDouble(long value) => throw new NotImplementedException(); + + /// Returns a Boolean value converted from one byte at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// true if the byte at in value is nonzero; otherwise, false. + /// value is null. + /// + /// is less than zero or greater than the + /// length of value minus 1. + /// + public static bool ToBoolean(byte[] value, int startIndex) => throw new NotImplementedException(); + + /// Returns a Unicode character converted from two bytes at a specified position in a byte array. + /// An array. + /// The starting position within value. + /// A character formed by two bytes beginning at . + /// equals the length of value minus 1. + /// value is null. + /// + /// is less than zero or greater than the + /// length of value minus 1. + /// + public static char ToChar(byte[] value, int startIndex) => throw new NotImplementedException(); + /// - /// Converts base data types to an array of bytes, and an array of bytes to base data types. All info taken from - /// the meta data of System.BitConverter. This implementation allows for Endianness consideration. + /// Returns a double-precision floating point number converted from eight bytes at a specified position in a byte + /// array. /// - public static class BigEndianBitConverter - { - /// Converts the specified double-precision floating point number to a 64-bit signed integer. - /// The number to convert. - /// A 64-bit signed integer whose value is equivalent to value. - /// It is not currently implemented - public static long DoubleToInt64Bits(double value) => throw new NotImplementedException(); + /// An array of bytes. + /// The starting position within value. + /// A double precision floating point number formed by eight bytes beginning at . + /// + /// is greater than or equal to the length of value + /// minus 7, and is less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// is less than zero or greater than the + /// length of value minus 1. + /// + public static double ToDouble(byte[] value, int startIndex) => throw new NotImplementedException(); - /// Returns the specified Boolean value as an array of bytes. - /// A Boolean value. - /// An array of bytes with length 1. - public static byte[] GetBytes(bool value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// A 16-bit signed integer formed by two bytes beginning at . + /// equals the length of value minus 1. + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static short ToInt16(byte[] value, int startIndex) => + BitConverter.ToInt16(value.Reverse().ToArray(), value.Length - sizeof(short) - startIndex); - /// Returns the specified Unicode character value as an array of bytes. - /// A character to convert. - /// An array of bytes with length 2. - public static byte[] GetBytes(char value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// A 32-bit signed integer formed by four bytes beginning at . + /// + /// is greater than or equal to the length of value + /// minus 3, and is less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static int ToInt32(byte[] value, int startIndex) => + BitConverter.ToInt32(value.Reverse().ToArray(), value.Length - sizeof(int) - startIndex); - /// Returns the specified double-precision floating point value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 8. - public static byte[] GetBytes(double value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// A 64-bit signed integer formed by eight bytes beginning at . + /// + /// is greater than or equal to the length of value + /// minus 7, and is less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// is less than zero or greater than the + /// length of value minus 1. + /// + public static long ToInt64(byte[] value, int startIndex) => + BitConverter.ToInt64(value.Reverse().ToArray(), value.Length - sizeof(long) - startIndex); - /// Returns the specified single-precision floating point value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 4. - public static byte[] GetBytes(float value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// + /// Returns a single-precision floating point number converted from four bytes at a specified position in a byte + /// array. + /// + /// An array of bytes. + /// The starting position within value. + /// A single-precision floating point number formed by four bytes beginning at . + /// + /// is greater than or equal to the length of value + /// minus 3, and is less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// is less than zero or greater than the + /// length of value minus 1. + /// + public static float ToSingle(byte[] value, int startIndex) => + BitConverter.ToSingle(value.Reverse().ToArray(), value.Length - sizeof(float) - startIndex); - /// Returns the specified 32-bit signed integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 4. - public static byte[] GetBytes(int value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// + /// Converts the numeric value of each element of a specified array of bytes to its equivalent hexadecimal string + /// representation. + /// + /// An array of bytes. + /// + /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding + /// element in value; for example, "7F-2C-4A". + /// + /// value is null. + public static string ToString(byte[] value) => BitConverter.ToString(value.Reverse().ToArray()); - /// Returns the specified 64-bit signed integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 8. - public static byte[] GetBytes(long value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// + /// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal + /// string representation. + /// + /// An array of bytes. + /// The starting position within value. + /// + /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding + /// element in a subarray of value; for example, "7F-2C-4A". + /// + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static string ToString(byte[] value, int startIndex) => + BitConverter.ToString(value.Reverse().ToArray(), startIndex); - /// Returns the specified 16-bit signed integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 2. - public static byte[] GetBytes(short value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// + /// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal + /// string representation. + /// + /// An array of bytes. + /// The starting position within value. + /// The number of array elements in value to convert. + /// + /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding + /// element in a subarray of value; for example, "7F-2C-4A". + /// + /// value is null. + /// + /// startIndex or length is less than zero. -or- startIndex is greater + /// than zero and is greater than or equal to the length of value. + /// + /// + /// The combination of startIndex and length does not specify a position within + /// value; that is, the startIndex parameter is greater than the length of value minus the length parameter. + /// + public static string ToString(byte[] value, int startIndex, int length) => + BitConverter.ToString(value.Reverse().ToArray(), startIndex, length); - /// Returns the specified 32-bit unsigned integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 4. - public static byte[] GetBytes(uint value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array. + /// The array of bytes. + /// The starting position within value. + /// A 16-bit unsigned integer formed by two bytes beginning at startIndex. + /// startIndex equals the length of value minus 1. + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static ushort ToUInt16(byte[] value, int startIndex) => + BitConverter.ToUInt16(value.Reverse().ToArray(), value.Length - sizeof(ushort) - startIndex); - /// Returns the specified 64-bit unsigned integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 8. - public static byte[] GetBytes(ulong value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// A 32-bit unsigned integer formed by four bytes beginning at startIndex. + /// + /// startIndex is greater than or equal to the length of value minus 3, and is + /// less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static uint ToUInt32(byte[] value, int startIndex) => + BitConverter.ToUInt32(value.Reverse().ToArray(), value.Length - sizeof(uint) - startIndex); - /// Returns the specified 16-bit unsigned integer value as an array of bytes. - /// The number to convert. - /// An array of bytes with length 2. - public static byte[] GetBytes(ushort value) => BitConverter.GetBytes(value).Reverse().ToArray(); + /// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// A 64-bit unsigned integer formed by the eight bytes beginning at startIndex. + /// + /// startIndex is greater than or equal to the length of value minus 7, and is + /// less than or equal to the length of value minus 1. + /// + /// value is null. + /// + /// startIndex is less than zero or greater than the length of value + /// minus 1. + /// + public static ulong ToUInt64(byte[] value, int startIndex) => + BitConverter.ToUInt64(value.Reverse().ToArray(), value.Length - sizeof(ulong) - startIndex); - /// Converts the specified 64-bit signed integer to a double-precision floating point number. - /// The number to convert. - /// A double-precision floating point number whose value is equivalent to value. - public static double Int64BitsToDouble(long value) => throw new NotImplementedException(); - - /// Returns a Boolean value converted from one byte at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// true if the byte at in value is nonzero; otherwise, false. - /// value is null. - /// - /// is less than zero or greater than the - /// length of value minus 1. - /// - public static bool ToBoolean(byte[] value, int startIndex) => throw new NotImplementedException(); - - /// Returns a Unicode character converted from two bytes at a specified position in a byte array. - /// An array. - /// The starting position within value. - /// A character formed by two bytes beginning at . - /// equals the length of value minus 1. - /// value is null. - /// - /// is less than zero or greater than the - /// length of value minus 1. - /// - public static char ToChar(byte[] value, int startIndex) => throw new NotImplementedException(); - - /// - /// Returns a double-precision floating point number converted from eight bytes at a specified position in a byte - /// array. - /// - /// An array of bytes. - /// The starting position within value. - /// A double precision floating point number formed by eight bytes beginning at . - /// - /// is greater than or equal to the length of value - /// minus 7, and is less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// is less than zero or greater than the - /// length of value minus 1. - /// - public static double ToDouble(byte[] value, int startIndex) => throw new NotImplementedException(); - - /// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// A 16-bit signed integer formed by two bytes beginning at . - /// equals the length of value minus 1. - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static short ToInt16(byte[] value, int startIndex) => - BitConverter.ToInt16(value.Reverse().ToArray(), value.Length - sizeof(short) - startIndex); - - /// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// A 32-bit signed integer formed by four bytes beginning at . - /// - /// is greater than or equal to the length of value - /// minus 3, and is less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static int ToInt32(byte[] value, int startIndex) => - BitConverter.ToInt32(value.Reverse().ToArray(), value.Length - sizeof(int) - startIndex); - - /// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// A 64-bit signed integer formed by eight bytes beginning at . - /// - /// is greater than or equal to the length of value - /// minus 7, and is less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// is less than zero or greater than the - /// length of value minus 1. - /// - public static long ToInt64(byte[] value, int startIndex) => - BitConverter.ToInt64(value.Reverse().ToArray(), value.Length - sizeof(long) - startIndex); - - /// - /// Returns a single-precision floating point number converted from four bytes at a specified position in a byte - /// array. - /// - /// An array of bytes. - /// The starting position within value. - /// A single-precision floating point number formed by four bytes beginning at . - /// - /// is greater than or equal to the length of value - /// minus 3, and is less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// is less than zero or greater than the - /// length of value minus 1. - /// - public static float ToSingle(byte[] value, int startIndex) => - BitConverter.ToSingle(value.Reverse().ToArray(), value.Length - sizeof(float) - startIndex); - - /// - /// Converts the numeric value of each element of a specified array of bytes to its equivalent hexadecimal string - /// representation. - /// - /// An array of bytes. - /// - /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding - /// element in value; for example, "7F-2C-4A". - /// - /// value is null. - public static string ToString(byte[] value) => BitConverter.ToString(value.Reverse().ToArray()); - - /// - /// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal - /// string representation. - /// - /// An array of bytes. - /// The starting position within value. - /// - /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding - /// element in a subarray of value; for example, "7F-2C-4A". - /// - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static string ToString(byte[] value, int startIndex) => - BitConverter.ToString(value.Reverse().ToArray(), startIndex); - - /// - /// Converts the numeric value of each element of a specified subarray of bytes to its equivalent hexadecimal - /// string representation. - /// - /// An array of bytes. - /// The starting position within value. - /// The number of array elements in value to convert. - /// - /// A System.String of hexadecimal pairs separated by hyphens, where each pair represents the corresponding - /// element in a subarray of value; for example, "7F-2C-4A". - /// - /// value is null. - /// - /// startIndex or length is less than zero. -or- startIndex is greater - /// than zero and is greater than or equal to the length of value. - /// - /// - /// The combination of startIndex and length does not specify a position within - /// value; that is, the startIndex parameter is greater than the length of value minus the length parameter. - /// - public static string ToString(byte[] value, int startIndex, int length) => - BitConverter.ToString(value.Reverse().ToArray(), startIndex, length); - - /// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array. - /// The array of bytes. - /// The starting position within value. - /// A 16-bit unsigned integer formed by two bytes beginning at startIndex. - /// startIndex equals the length of value minus 1. - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static ushort ToUInt16(byte[] value, int startIndex) => - BitConverter.ToUInt16(value.Reverse().ToArray(), value.Length - sizeof(ushort) - startIndex); - - /// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// A 32-bit unsigned integer formed by four bytes beginning at startIndex. - /// - /// startIndex is greater than or equal to the length of value minus 3, and is - /// less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static uint ToUInt32(byte[] value, int startIndex) => - BitConverter.ToUInt32(value.Reverse().ToArray(), value.Length - sizeof(uint) - startIndex); - - /// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array. - /// An array of bytes. - /// The starting position within value. - /// A 64-bit unsigned integer formed by the eight bytes beginning at startIndex. - /// - /// startIndex is greater than or equal to the length of value minus 7, and is - /// less than or equal to the length of value minus 1. - /// - /// value is null. - /// - /// startIndex is less than zero or greater than the length of value - /// minus 1. - /// - public static ulong ToUInt64(byte[] value, int startIndex) => - BitConverter.ToUInt64(value.Reverse().ToArray(), value.Length - sizeof(ulong) - startIndex); - - /// Converts a big endian byte array representation of a GUID into the .NET Guid structure - /// Byte array containing a GUID in big endian - /// Start of the byte array to process - /// Processed Guid - public static Guid ToGuid(byte[] value, int startIndex) => new Guid(ToUInt32(value, 0 + startIndex), - ToUInt16(value, 4 + startIndex), - ToUInt16(value, 6 + startIndex), - value[8 + startIndex + 0], - value[8 + startIndex + 1], - value[8 + startIndex + 2], - value[8 + startIndex + 3], - value[8 + startIndex + 5], - value[8 + startIndex + 5], - value[8 + startIndex + 6], - value[8 + startIndex + 7]); - } + /// Converts a big endian byte array representation of a GUID into the .NET Guid structure + /// Byte array containing a GUID in big endian + /// Start of the byte array to process + /// Processed Guid + public static Guid ToGuid(byte[] value, int startIndex) => new Guid(ToUInt32(value, 0 + startIndex), + ToUInt16(value, 4 + startIndex), + ToUInt16(value, 6 + startIndex), + value[8 + startIndex + 0], + value[8 + startIndex + 1], + value[8 + startIndex + 2], + value[8 + startIndex + 3], + value[8 + startIndex + 5], + value[8 + startIndex + 5], + value[8 + startIndex + 6], + value[8 + startIndex + 7]); } \ No newline at end of file diff --git a/BitEndian.cs b/BitEndian.cs index 857a35a..f85dccc 100644 --- a/BitEndian.cs +++ b/BitEndian.cs @@ -36,16 +36,15 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Helpers +namespace Aaru.Helpers; + +/// Describes the endianness of bits on a data structure +public enum BitEndian { - /// Describes the endianness of bits on a data structure - public enum BitEndian - { - /// Little-endian, or least significant bit - Little, - /// Big-endian, or most significant bit - Big, - /// PDP-11 endian, little endian except for 32-bit integers where the 16 halves are swapped between them - Pdp - } + /// Little-endian, or least significant bit + Little, + /// Big-endian, or most significant bit + Big, + /// PDP-11 endian, little endian except for 32-bit integers where the 16 halves are swapped between them + Pdp } \ No newline at end of file diff --git a/CHS.cs b/CHS.cs index b31d37d..aa58ff3 100644 --- a/CHS.cs +++ b/CHS.cs @@ -30,20 +30,19 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Helpers +namespace Aaru.Helpers; + +/// Helper operations to work with CHS values +public static class CHS { - /// Helper operations to work with CHS values - public static class CHS - { - /// Converts a CHS position to a LBA one - /// Cylinder - /// Head - /// Sector - /// Number of heads - /// Number of sectors per track - /// - public static uint ToLBA(uint cyl, uint head, uint sector, uint maxHead, uint maxSector) => - maxHead == 0 || maxSector == 0 ? (((cyl * 16) + head) * 63) + sector - 1 - : (((cyl * maxHead) + head) * maxSector) + sector - 1; - } + /// Converts a CHS position to a LBA one + /// Cylinder + /// Head + /// Sector + /// Number of heads + /// Number of sectors per track + /// + public static uint ToLBA(uint cyl, uint head, uint sector, uint maxHead, uint maxSector) => + maxHead == 0 || maxSector == 0 ? (((cyl * 16) + head) * 63) + sector - 1 + : (((cyl * maxHead) + head) * maxSector) + sector - 1; } \ No newline at end of file diff --git a/CompareBytes.cs b/CompareBytes.cs index 96dff9c..b868d4e 100644 --- a/CompareBytes.cs +++ b/CompareBytes.cs @@ -30,43 +30,42 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Helpers +namespace Aaru.Helpers; + +public static partial class ArrayHelpers { - public static partial class ArrayHelpers + /// Compares two byte arrays + /// true if they are different in any way + /// true if they have the same size + /// Left array + /// Right array + public static void CompareBytes(out bool different, out bool sameSize, byte[] compareArray1, + byte[] compareArray2) { - /// Compares two byte arrays - /// true if they are different in any way - /// true if they have the same size - /// Left array - /// Right array - public static void CompareBytes(out bool different, out bool sameSize, byte[] compareArray1, - byte[] compareArray2) + different = false; + sameSize = true; + + long leastBytes; + + if(compareArray1.LongLength < compareArray2.LongLength) { - different = false; - sameSize = true; - - long leastBytes; - - if(compareArray1.LongLength < compareArray2.LongLength) - { - sameSize = false; - leastBytes = compareArray1.LongLength; - } - else if(compareArray1.LongLength > compareArray2.LongLength) - { - sameSize = false; - leastBytes = compareArray2.LongLength; - } - else - leastBytes = compareArray1.LongLength; - - for(long i = 0; i < leastBytes; i++) - if(compareArray1[i] != compareArray2[i]) - { - different = true; - - return; - } + sameSize = false; + leastBytes = compareArray1.LongLength; } + else if(compareArray1.LongLength > compareArray2.LongLength) + { + sameSize = false; + leastBytes = compareArray2.LongLength; + } + else + leastBytes = compareArray1.LongLength; + + for(long i = 0; i < leastBytes; i++) + if(compareArray1[i] != compareArray2[i]) + { + different = true; + + return; + } } } \ No newline at end of file diff --git a/CountBits.cs b/CountBits.cs index 88cd8d4..ba50fe7 100644 --- a/CountBits.cs +++ b/CountBits.cs @@ -30,20 +30,19 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Helpers -{ - /// Helper operations to count bits - public static class CountBits - { - /// Counts the number of bits set to true in a number - /// Number - /// Bits set to true - public static int Count(uint number) - { - number -= (number >> 1) & 0x55555555; - number = (number & 0x33333333) + ((number >> 2) & 0x33333333); +namespace Aaru.Helpers; - return (int)((((number + (number >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24); - } +/// Helper operations to count bits +public static class CountBits +{ + /// Counts the number of bits set to true in a number + /// Number + /// Bits set to true + public static int Count(uint number) + { + number -= (number >> 1) & 0x55555555; + number = (number & 0x33333333) + ((number >> 2) & 0x33333333); + + return (int)((((number + (number >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24); } } \ No newline at end of file diff --git a/DateHandlers.cs b/DateHandlers.cs index c86b3d2..7fc047c 100644 --- a/DateHandlers.cs +++ b/DateHandlers.cs @@ -34,356 +34,355 @@ using System; using System.Text; using Aaru.Console; -namespace Aaru.Helpers +namespace Aaru.Helpers; + +/// Helper operations for timestamp management (date and time) +public static class DateHandlers { - /// Helper operations for timestamp management (date and time) - public static class DateHandlers + static readonly DateTime _lisaEpoch = new DateTime(1901, 1, 1, 0, 0, 0); + static readonly DateTime _macEpoch = new DateTime(1904, 1, 1, 0, 0, 0); + static readonly DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0); + /// Day 0 of Julian Date system + static readonly DateTime _julianEpoch = new DateTime(1858, 11, 17, 0, 0, 0); + static readonly DateTime _amigaEpoch = new DateTime(1978, 1, 1, 0, 0, 0); + + /// Converts a Macintosh timestamp to a .NET DateTime + /// Macintosh timestamp (seconds since 1st Jan. 1904) + /// .NET DateTime + public static DateTime MacToDateTime(ulong macTimeStamp) => _macEpoch.AddTicks((long)(macTimeStamp * 10000000)); + + /// Converts a Lisa timestamp to a .NET DateTime + /// Lisa timestamp (seconds since 1st Jan. 1901) + /// .NET DateTime + public static DateTime LisaToDateTime(uint lisaTimeStamp) => _lisaEpoch.AddSeconds(lisaTimeStamp); + + /// Converts a UNIX timestamp to a .NET DateTime + /// UNIX timestamp (seconds since 1st Jan. 1970) + /// .NET DateTime + public static DateTime UnixToDateTime(int unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp); + + /// Converts a UNIX timestamp to a .NET DateTime + /// UNIX timestamp (seconds since 1st Jan. 1970) + /// .NET DateTime + public static DateTime UnixToDateTime(long unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp); + + /// Converts a UNIX timestamp to a .NET DateTime + /// UNIX timestamp (seconds since 1st Jan. 1970) + /// .NET DateTime + public static DateTime UnixUnsignedToDateTime(uint unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp); + + /// Converts a UNIX timestamp to a .NET DateTime + /// Seconds since 1st Jan. 1970) + /// Nanoseconds + /// .NET DateTime + public static DateTime UnixUnsignedToDateTime(uint seconds, uint nanoseconds) => + _unixEpoch.AddSeconds(seconds).AddTicks((long)nanoseconds / 100); + + /// Converts a UNIX timestamp to a .NET DateTime + /// UNIX timestamp (seconds since 1st Jan. 1970) + /// .NET DateTime + public static DateTime UnixUnsignedToDateTime(ulong unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp); + + /// Converts a High Sierra Format timestamp to a .NET DateTime + /// High Sierra Format timestamp + /// .NET DateTime + public static DateTime HighSierraToDateTime(byte[] vdDateTime) { - static readonly DateTime _lisaEpoch = new DateTime(1901, 1, 1, 0, 0, 0); - static readonly DateTime _macEpoch = new DateTime(1904, 1, 1, 0, 0, 0); - static readonly DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0); - /// Day 0 of Julian Date system - static readonly DateTime _julianEpoch = new DateTime(1858, 11, 17, 0, 0, 0); - static readonly DateTime _amigaEpoch = new DateTime(1978, 1, 1, 0, 0, 0); + byte[] isoTime = new byte[17]; + Array.Copy(vdDateTime, 0, isoTime, 0, 16); - /// Converts a Macintosh timestamp to a .NET DateTime - /// Macintosh timestamp (seconds since 1st Jan. 1904) - /// .NET DateTime - public static DateTime MacToDateTime(ulong macTimeStamp) => _macEpoch.AddTicks((long)(macTimeStamp * 10000000)); + return Iso9660ToDateTime(isoTime); + } - /// Converts a Lisa timestamp to a .NET DateTime - /// Lisa timestamp (seconds since 1st Jan. 1901) - /// .NET DateTime - public static DateTime LisaToDateTime(uint lisaTimeStamp) => _lisaEpoch.AddSeconds(lisaTimeStamp); + // TODO: Timezone + /// Converts an ISO9660 timestamp to a .NET DateTime + /// ISO9660 timestamp + /// .NET DateTime + public static DateTime Iso9660ToDateTime(byte[] vdDateTime) + { + byte[] twoCharValue = new byte[2]; + byte[] fourCharValue = new byte[4]; - /// Converts a UNIX timestamp to a .NET DateTime - /// UNIX timestamp (seconds since 1st Jan. 1970) - /// .NET DateTime - public static DateTime UnixToDateTime(int unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp); + fourCharValue[0] = vdDateTime[0]; + fourCharValue[1] = vdDateTime[1]; + fourCharValue[2] = vdDateTime[2]; + fourCharValue[3] = vdDateTime[3]; - /// Converts a UNIX timestamp to a .NET DateTime - /// UNIX timestamp (seconds since 1st Jan. 1970) - /// .NET DateTime - public static DateTime UnixToDateTime(long unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp); + AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "year = \"{0}\"", + StringHandlers.CToString(fourCharValue, Encoding.ASCII)); - /// Converts a UNIX timestamp to a .NET DateTime - /// UNIX timestamp (seconds since 1st Jan. 1970) - /// .NET DateTime - public static DateTime UnixUnsignedToDateTime(uint unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp); + if(!int.TryParse(StringHandlers.CToString(fourCharValue, Encoding.ASCII), out int year)) + year = 0; - /// Converts a UNIX timestamp to a .NET DateTime - /// Seconds since 1st Jan. 1970) - /// Nanoseconds - /// .NET DateTime - public static DateTime UnixUnsignedToDateTime(uint seconds, uint nanoseconds) => - _unixEpoch.AddSeconds(seconds).AddTicks((long)nanoseconds / 100); + twoCharValue[0] = vdDateTime[4]; + twoCharValue[1] = vdDateTime[5]; - /// Converts a UNIX timestamp to a .NET DateTime - /// UNIX timestamp (seconds since 1st Jan. 1970) - /// .NET DateTime - public static DateTime UnixUnsignedToDateTime(ulong unixTimeStamp) => _unixEpoch.AddSeconds(unixTimeStamp); + AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "month = \"{0}\"", + StringHandlers.CToString(twoCharValue, Encoding.ASCII)); - /// Converts a High Sierra Format timestamp to a .NET DateTime - /// High Sierra Format timestamp - /// .NET DateTime - public static DateTime HighSierraToDateTime(byte[] vdDateTime) + if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int month)) + month = 0; + + twoCharValue[0] = vdDateTime[6]; + twoCharValue[1] = vdDateTime[7]; + + AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "day = \"{0}\"", + StringHandlers.CToString(twoCharValue, Encoding.ASCII)); + + if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int day)) + day = 0; + + twoCharValue[0] = vdDateTime[8]; + twoCharValue[1] = vdDateTime[9]; + + AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "hour = \"{0}\"", + StringHandlers.CToString(twoCharValue, Encoding.ASCII)); + + if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int hour)) + hour = 0; + + twoCharValue[0] = vdDateTime[10]; + twoCharValue[1] = vdDateTime[11]; + + AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "minute = \"{0}\"", + StringHandlers.CToString(twoCharValue, Encoding.ASCII)); + + if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int minute)) + minute = 0; + + twoCharValue[0] = vdDateTime[12]; + twoCharValue[1] = vdDateTime[13]; + + AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "second = \"{0}\"", + StringHandlers.CToString(twoCharValue, Encoding.ASCII)); + + if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int second)) + second = 0; + + twoCharValue[0] = vdDateTime[14]; + twoCharValue[1] = vdDateTime[15]; + + AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "hundredths = \"{0}\"", + StringHandlers.CToString(twoCharValue, Encoding.ASCII)); + + if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int hundredths)) + hundredths = 0; + + AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", + "decodedDT = new DateTime({0}, {1}, {2}, {3}, {4}, {5}, {6}, DateTimeKind.Unspecified);", + year, month, day, hour, minute, second, hundredths * 10); + + sbyte difference = (sbyte)vdDateTime[16]; + + var decodedDt = new DateTime(year, month, day, hour, minute, second, hundredths * 10, DateTimeKind.Utc); + + return decodedDt.AddMinutes(difference * -15); + } + + /// Converts a VMS timestamp to a .NET DateTime + /// VMS timestamp (tenths of microseconds since day 0 of the Julian Date) + /// .NET DateTime + /// C# works in UTC, VMS on Julian Date, some displacement may occur on disks created outside UTC + public static DateTime VmsToDateTime(ulong vmsDate) + { + double delta = vmsDate * 0.0001; // Tenths of microseconds to milliseconds, will lose some detail + + return _julianEpoch.AddMilliseconds(delta); + } + + /// Converts an Amiga timestamp to a .NET DateTime + /// Days since the 1st Jan. 1978 + /// Minutes since o'clock + /// Ticks + /// .NET DateTime + public static DateTime AmigaToDateTime(uint days, uint minutes, uint ticks) + { + DateTime temp = _amigaEpoch.AddDays(days); + temp = temp.AddMinutes(minutes); + + return temp.AddMilliseconds(ticks * 20); + } + + /// Converts an UCSD Pascal timestamp to a .NET DateTime + /// UCSD Pascal timestamp + /// .NET DateTime + public static DateTime UcsdPascalToDateTime(short dateRecord) + { + int year = ((dateRecord & 0xFE00) >> 9) + 1900; + int day = (dateRecord & 0x01F0) >> 4; + int month = dateRecord & 0x000F; + + AaruConsole.DebugWriteLine("UCSDPascalToDateTime handler", + "dateRecord = 0x{0:X4}, year = {1}, month = {2}, day = {3}", dateRecord, year, + month, day); + + return new DateTime(year, month, day); + } + + /// Converts a DOS timestamp to a .NET DateTime + /// Date + /// Time + /// .NET DateTime + public static DateTime DosToDateTime(ushort date, ushort time) + { + int year = ((date & 0xFE00) >> 9) + 1980; + int month = (date & 0x1E0) >> 5; + int day = date & 0x1F; + int hour = (time & 0xF800) >> 11; + int minute = (time & 0x7E0) >> 5; + int second = (time & 0x1F) * 2; + + AaruConsole.DebugWriteLine("DOSToDateTime handler", "date = 0x{0:X4}, year = {1}, month = {2}, day = {3}", + date, year, month, day); + + AaruConsole.DebugWriteLine("DOSToDateTime handler", + "time = 0x{0:X4}, hour = {1}, minute = {2}, second = {3}", time, hour, minute, + second); + + DateTime dosDate; + + try { - byte[] isoTime = new byte[17]; - Array.Copy(vdDateTime, 0, isoTime, 0, 16); - - return Iso9660ToDateTime(isoTime); + dosDate = new DateTime(year, month, day, hour, minute, second); + } + catch(ArgumentOutOfRangeException) + { + dosDate = new DateTime(1980, 1, 1, 0, 0, 0); } - // TODO: Timezone - /// Converts an ISO9660 timestamp to a .NET DateTime - /// ISO9660 timestamp - /// .NET DateTime - public static DateTime Iso9660ToDateTime(byte[] vdDateTime) + return dosDate; + } + + /// Converts a CP/M timestamp to .NET DateTime + /// CP/M timestamp + /// .NET DateTime + public static DateTime CpmToDateTime(byte[] timestamp) + { + ushort days = BitConverter.ToUInt16(timestamp, 0); + int hours = timestamp[2]; + int minutes = timestamp[3]; + + DateTime temp = _amigaEpoch.AddDays(days); + temp = temp.AddHours(hours); + temp = temp.AddMinutes(minutes); + + return temp; + } + + /// Converts an ECMA timestamp to a .NET DateTime + /// Timezone + /// Year + /// Month + /// Day + /// Hour + /// Minute + /// Second + /// Centiseconds + /// Hundreds of microseconds + /// Microseconds + /// + public static DateTime EcmaToDateTime(ushort typeAndTimeZone, short year, byte month, byte day, byte hour, + byte minute, byte second, byte centiseconds, byte hundredsOfMicroseconds, + byte microseconds) + { + byte specification = (byte)((typeAndTimeZone & 0xF000) >> 12); + + long ticks = ((long)centiseconds * 100000) + ((long)hundredsOfMicroseconds * 1000) + + ((long)microseconds * 10); + + if(specification == 0) + return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc).AddTicks(ticks); + + ushort preOffset = (ushort)(typeAndTimeZone & 0xFFF); + short offset; + + if((preOffset & 0x800) == 0x800) + offset = (short)(preOffset | 0xF000); + else + offset = (short)(preOffset & 0x7FF); + + if(offset == -2047) + return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified).AddTicks(ticks); + + if(offset < -1440 || + offset > 1440) + offset = 0; + + return new DateTimeOffset(year, month, day, hour, minute, second, new TimeSpan(0, offset, 0)). + AddTicks(ticks).DateTime; + } + + /// Converts a Solaris high resolution timestamp to .NET DateTime + /// Solaris high resolution timestamp + /// .NET DateTime + public static DateTime UnixHrTimeToDateTime(ulong hrTimeStamp) => + _unixEpoch.AddTicks((long)(hrTimeStamp / 100)); + + /// Converts an OS-9 timestamp to .NET DateTime + /// OS-9 timestamp + /// .NET DateTime + public static DateTime Os9ToDateTime(byte[] date) + { + if(date == null || + (date.Length != 3 && date.Length != 5)) + return DateTime.MinValue; + + DateTime os9Date; + + try { - byte[] twoCharValue = new byte[2]; - byte[] fourCharValue = new byte[4]; - - fourCharValue[0] = vdDateTime[0]; - fourCharValue[1] = vdDateTime[1]; - fourCharValue[2] = vdDateTime[2]; - fourCharValue[3] = vdDateTime[3]; - - AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "year = \"{0}\"", - StringHandlers.CToString(fourCharValue, Encoding.ASCII)); - - if(!int.TryParse(StringHandlers.CToString(fourCharValue, Encoding.ASCII), out int year)) - year = 0; - - twoCharValue[0] = vdDateTime[4]; - twoCharValue[1] = vdDateTime[5]; - - AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "month = \"{0}\"", - StringHandlers.CToString(twoCharValue, Encoding.ASCII)); - - if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int month)) - month = 0; - - twoCharValue[0] = vdDateTime[6]; - twoCharValue[1] = vdDateTime[7]; - - AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "day = \"{0}\"", - StringHandlers.CToString(twoCharValue, Encoding.ASCII)); - - if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int day)) - day = 0; - - twoCharValue[0] = vdDateTime[8]; - twoCharValue[1] = vdDateTime[9]; - - AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "hour = \"{0}\"", - StringHandlers.CToString(twoCharValue, Encoding.ASCII)); - - if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int hour)) - hour = 0; - - twoCharValue[0] = vdDateTime[10]; - twoCharValue[1] = vdDateTime[11]; - - AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "minute = \"{0}\"", - StringHandlers.CToString(twoCharValue, Encoding.ASCII)); - - if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int minute)) - minute = 0; - - twoCharValue[0] = vdDateTime[12]; - twoCharValue[1] = vdDateTime[13]; - - AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "second = \"{0}\"", - StringHandlers.CToString(twoCharValue, Encoding.ASCII)); - - if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int second)) - second = 0; - - twoCharValue[0] = vdDateTime[14]; - twoCharValue[1] = vdDateTime[15]; - - AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", "hundredths = \"{0}\"", - StringHandlers.CToString(twoCharValue, Encoding.ASCII)); - - if(!int.TryParse(StringHandlers.CToString(twoCharValue, Encoding.ASCII), out int hundredths)) - hundredths = 0; - - AaruConsole.DebugWriteLine("ISO9600ToDateTime handler", - "decodedDT = new DateTime({0}, {1}, {2}, {3}, {4}, {5}, {6}, DateTimeKind.Unspecified);", - year, month, day, hour, minute, second, hundredths * 10); - - sbyte difference = (sbyte)vdDateTime[16]; - - var decodedDt = new DateTime(year, month, day, hour, minute, second, hundredths * 10, DateTimeKind.Utc); - - return decodedDt.AddMinutes(difference * -15); + os9Date = date.Length == 5 ? new DateTime(1900 + date[0], date[1], date[2], date[3], date[4], 0) + : new DateTime(1900 + date[0], date[1], date[2], 0, 0, 0); + } + catch(ArgumentOutOfRangeException) + { + os9Date = new DateTime(1900, 0, 0, 0, 0, 0); } - /// Converts a VMS timestamp to a .NET DateTime - /// VMS timestamp (tenths of microseconds since day 0 of the Julian Date) - /// .NET DateTime - /// C# works in UTC, VMS on Julian Date, some displacement may occur on disks created outside UTC - public static DateTime VmsToDateTime(ulong vmsDate) + return os9Date; + } + + /// Converts a LIF timestamp to .NET DateTime + /// LIF timestamp + /// .NET DateTime + public static DateTime LifToDateTime(byte[] date) + { + if(date == null || + date.Length != 6) + return new DateTime(1970, 1, 1, 0, 0, 0); + + return LifToDateTime(date[0], date[1], date[2], date[3], date[4], date[5]); + } + + /// Converts a LIF timestamp to .NET DateTime + /// Yer + /// Month + /// Day + /// Hour + /// Minute + /// Second + /// .NET DateTime + public static DateTime LifToDateTime(byte year, byte month, byte day, byte hour, byte minute, byte second) + { + try { - double delta = vmsDate * 0.0001; // Tenths of microseconds to milliseconds, will lose some detail + int iyear = ((year >> 4) * 10) + (year & 0xF); + int imonth = ((month >> 4) * 10) + (month & 0xF); + int iday = ((day >> 4) * 10) + (day & 0xF); + int iminute = ((minute >> 4) * 10) + (minute & 0xF); + int ihour = ((hour >> 4) * 10) + (hour & 0xF); + int isecond = ((second >> 4) * 10) + (second & 0xF); - return _julianEpoch.AddMilliseconds(delta); - } - - /// Converts an Amiga timestamp to a .NET DateTime - /// Days since the 1st Jan. 1978 - /// Minutes since o'clock - /// Ticks - /// .NET DateTime - public static DateTime AmigaToDateTime(uint days, uint minutes, uint ticks) - { - DateTime temp = _amigaEpoch.AddDays(days); - temp = temp.AddMinutes(minutes); - - return temp.AddMilliseconds(ticks * 20); - } - - /// Converts an UCSD Pascal timestamp to a .NET DateTime - /// UCSD Pascal timestamp - /// .NET DateTime - public static DateTime UcsdPascalToDateTime(short dateRecord) - { - int year = ((dateRecord & 0xFE00) >> 9) + 1900; - int day = (dateRecord & 0x01F0) >> 4; - int month = dateRecord & 0x000F; - - AaruConsole.DebugWriteLine("UCSDPascalToDateTime handler", - "dateRecord = 0x{0:X4}, year = {1}, month = {2}, day = {3}", dateRecord, year, - month, day); - - return new DateTime(year, month, day); - } - - /// Converts a DOS timestamp to a .NET DateTime - /// Date - /// Time - /// .NET DateTime - public static DateTime DosToDateTime(ushort date, ushort time) - { - int year = ((date & 0xFE00) >> 9) + 1980; - int month = (date & 0x1E0) >> 5; - int day = date & 0x1F; - int hour = (time & 0xF800) >> 11; - int minute = (time & 0x7E0) >> 5; - int second = (time & 0x1F) * 2; - - AaruConsole.DebugWriteLine("DOSToDateTime handler", "date = 0x{0:X4}, year = {1}, month = {2}, day = {3}", - date, year, month, day); - - AaruConsole.DebugWriteLine("DOSToDateTime handler", - "time = 0x{0:X4}, hour = {1}, minute = {2}, second = {3}", time, hour, minute, - second); - - DateTime dosDate; - - try - { - dosDate = new DateTime(year, month, day, hour, minute, second); - } - catch(ArgumentOutOfRangeException) - { - dosDate = new DateTime(1980, 1, 1, 0, 0, 0); - } - - return dosDate; - } - - /// Converts a CP/M timestamp to .NET DateTime - /// CP/M timestamp - /// .NET DateTime - public static DateTime CpmToDateTime(byte[] timestamp) - { - ushort days = BitConverter.ToUInt16(timestamp, 0); - int hours = timestamp[2]; - int minutes = timestamp[3]; - - DateTime temp = _amigaEpoch.AddDays(days); - temp = temp.AddHours(hours); - temp = temp.AddMinutes(minutes); - - return temp; - } - - /// Converts an ECMA timestamp to a .NET DateTime - /// Timezone - /// Year - /// Month - /// Day - /// Hour - /// Minute - /// Second - /// Centiseconds - /// Hundreds of microseconds - /// Microseconds - /// - public static DateTime EcmaToDateTime(ushort typeAndTimeZone, short year, byte month, byte day, byte hour, - byte minute, byte second, byte centiseconds, byte hundredsOfMicroseconds, - byte microseconds) - { - byte specification = (byte)((typeAndTimeZone & 0xF000) >> 12); - - long ticks = ((long)centiseconds * 100000) + ((long)hundredsOfMicroseconds * 1000) + - ((long)microseconds * 10); - - if(specification == 0) - return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Utc).AddTicks(ticks); - - ushort preOffset = (ushort)(typeAndTimeZone & 0xFFF); - short offset; - - if((preOffset & 0x800) == 0x800) - offset = (short)(preOffset | 0xF000); + if(iyear >= 70) + iyear += 1900; else - offset = (short)(preOffset & 0x7FF); + iyear += 2000; - if(offset == -2047) - return new DateTime(year, month, day, hour, minute, second, DateTimeKind.Unspecified).AddTicks(ticks); - - if(offset < -1440 || - offset > 1440) - offset = 0; - - return new DateTimeOffset(year, month, day, hour, minute, second, new TimeSpan(0, offset, 0)). - AddTicks(ticks).DateTime; + return new DateTime(iyear, imonth, iday, ihour, iminute, isecond); } - - /// Converts a Solaris high resolution timestamp to .NET DateTime - /// Solaris high resolution timestamp - /// .NET DateTime - public static DateTime UnixHrTimeToDateTime(ulong hrTimeStamp) => - _unixEpoch.AddTicks((long)(hrTimeStamp / 100)); - - /// Converts an OS-9 timestamp to .NET DateTime - /// OS-9 timestamp - /// .NET DateTime - public static DateTime Os9ToDateTime(byte[] date) + catch(ArgumentOutOfRangeException) { - if(date == null || - (date.Length != 3 && date.Length != 5)) - return DateTime.MinValue; - - DateTime os9Date; - - try - { - os9Date = date.Length == 5 ? new DateTime(1900 + date[0], date[1], date[2], date[3], date[4], 0) - : new DateTime(1900 + date[0], date[1], date[2], 0, 0, 0); - } - catch(ArgumentOutOfRangeException) - { - os9Date = new DateTime(1900, 0, 0, 0, 0, 0); - } - - return os9Date; - } - - /// Converts a LIF timestamp to .NET DateTime - /// LIF timestamp - /// .NET DateTime - public static DateTime LifToDateTime(byte[] date) - { - if(date == null || - date.Length != 6) - return new DateTime(1970, 1, 1, 0, 0, 0); - - return LifToDateTime(date[0], date[1], date[2], date[3], date[4], date[5]); - } - - /// Converts a LIF timestamp to .NET DateTime - /// Yer - /// Month - /// Day - /// Hour - /// Minute - /// Second - /// .NET DateTime - public static DateTime LifToDateTime(byte year, byte month, byte day, byte hour, byte minute, byte second) - { - try - { - int iyear = ((year >> 4) * 10) + (year & 0xF); - int imonth = ((month >> 4) * 10) + (month & 0xF); - int iday = ((day >> 4) * 10) + (day & 0xF); - int iminute = ((minute >> 4) * 10) + (minute & 0xF); - int ihour = ((hour >> 4) * 10) + (hour & 0xF); - int isecond = ((second >> 4) * 10) + (second & 0xF); - - if(iyear >= 70) - iyear += 1900; - else - iyear += 2000; - - return new DateTime(iyear, imonth, iday, ihour, iminute, isecond); - } - catch(ArgumentOutOfRangeException) - { - return new DateTime(1970, 1, 1, 0, 0, 0); - } + return new DateTime(1970, 1, 1, 0, 0, 0); } } } \ No newline at end of file diff --git a/Marshal.cs b/Marshal.cs index d2c03e9..8a1c3bf 100644 --- a/Marshal.cs +++ b/Marshal.cs @@ -36,463 +36,462 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Aaru.Helpers +namespace Aaru.Helpers; + +/// Provides methods to marshal binary data into C# structs +public static class Marshal { - /// Provides methods to marshal binary data into C# structs - public static class Marshal + /// Returns the size of an unmanaged type in bytes. + /// The type whose size is to be returned. + /// The size, in bytes, of the type that is specified by the generic type parameter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SizeOf() => System.Runtime.InteropServices.Marshal.SizeOf(); + + /// Marshal little-endian binary data to a structure + /// Byte array containing the binary data + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ByteArrayToStructureLittleEndian(byte[] bytes) where T : struct { - /// Returns the size of an unmanaged type in bytes. - /// The type whose size is to be returned. - /// The size, in bytes, of the type that is specified by the generic type parameter. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int SizeOf() => System.Runtime.InteropServices.Marshal.SizeOf(); + var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned); - /// Marshal little-endian binary data to a structure - /// Byte array containing the binary data - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ByteArrayToStructureLittleEndian(byte[] bytes) where T : struct + var str = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)); + + ptr.Free(); + + return str; + } + + /// Marshal little-endian binary data to a structure + /// Byte array containing the binary data + /// Start on the array where the structure begins + /// Length of the structure in bytes + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ByteArrayToStructureLittleEndian(byte[] bytes, int start, int length) where T : struct + { + Span span = bytes; + + return ByteArrayToStructureLittleEndian(span.Slice(start, length).ToArray()); + } + + /// Marshal big-endian binary data to a structure + /// Byte array containing the binary data + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ByteArrayToStructureBigEndian(byte[] bytes) where T : struct + { + var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned); + + object str = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)); + + ptr.Free(); + + return (T)SwapStructureMembersEndian(str); + } + + /// Marshal big-endian binary data to a structure + /// Byte array containing the binary data + /// Start on the array where the structure begins + /// Length of the structure in bytes + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ByteArrayToStructureBigEndian(byte[] bytes, int start, int length) where T : struct + { + Span span = bytes; + + return ByteArrayToStructureBigEndian(span.Slice(start, length).ToArray()); + } + + /// Marshal PDP-11 binary data to a structure + /// Byte array containing the binary data + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ByteArrayToStructurePdpEndian(byte[] bytes) where T : struct + { { var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned); - var str = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)); + object str = + (T)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)); ptr.Free(); - return str; - } - - /// Marshal little-endian binary data to a structure - /// Byte array containing the binary data - /// Start on the array where the structure begins - /// Length of the structure in bytes - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ByteArrayToStructureLittleEndian(byte[] bytes, int start, int length) where T : struct - { - Span span = bytes; - - return ByteArrayToStructureLittleEndian(span.Slice(start, length).ToArray()); - } - - /// Marshal big-endian binary data to a structure - /// Byte array containing the binary data - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ByteArrayToStructureBigEndian(byte[] bytes) where T : struct - { - var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned); - - object str = (T)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)); - - ptr.Free(); - - return (T)SwapStructureMembersEndian(str); - } - - /// Marshal big-endian binary data to a structure - /// Byte array containing the binary data - /// Start on the array where the structure begins - /// Length of the structure in bytes - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ByteArrayToStructureBigEndian(byte[] bytes, int start, int length) where T : struct - { - Span span = bytes; - - return ByteArrayToStructureBigEndian(span.Slice(start, length).ToArray()); - } - - /// Marshal PDP-11 binary data to a structure - /// Byte array containing the binary data - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ByteArrayToStructurePdpEndian(byte[] bytes) where T : struct - { - { - var ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned); - - object str = - (T)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)); - - ptr.Free(); - - return (T)SwapStructureMembersEndianPdp(str); - } - } - - /// Marshal PDP-11 binary data to a structure - /// Byte array containing the binary data - /// Start on the array where the structure begins - /// Length of the structure in bytes - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T ByteArrayToStructurePdpEndian(byte[] bytes, int start, int length) where T : struct - { - Span span = bytes; - - return ByteArrayToStructurePdpEndian(span.Slice(start, length).ToArray()); - } - - /// - /// Marshal little-endian binary data to a structure. If the structure type contains any non value type, this - /// method will crash. - /// - /// Byte array containing the binary data - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T SpanToStructureLittleEndian(ReadOnlySpan bytes) where T : struct => - MemoryMarshal.Read(bytes); - - /// - /// Marshal little-endian binary data to a structure. If the structure type contains any non value type, this - /// method will crash. - /// - /// Byte span containing the binary data - /// Start on the span where the structure begins - /// Length of the structure in bytes - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T SpanToStructureLittleEndian(ReadOnlySpan bytes, int start, int length) - where T : struct => MemoryMarshal.Read(bytes.Slice(start, length)); - - /// - /// Marshal big-endian binary data to a structure. If the structure type contains any non value type, this method - /// will crash. - /// - /// Byte array containing the binary data - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T SpanToStructureBigEndian(ReadOnlySpan bytes) where T : struct - { - T str = SpanToStructureLittleEndian(bytes); - - return (T)SwapStructureMembersEndian(str); - } - - /// - /// Marshal big-endian binary data to a structure. If the structure type contains any non value type, this method - /// will crash. - /// - /// Byte span containing the binary data - /// Start on the span where the structure begins - /// Length of the structure in bytes - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T SpanToStructureBigEndian(ReadOnlySpan bytes, int start, int length) where T : struct - { - T str = SpanToStructureLittleEndian(bytes.Slice(start, length)); - - return (T)SwapStructureMembersEndian(str); - } - - /// - /// Marshal PDP-11 binary data to a structure. If the structure type contains any non value type, this method will - /// crash. - /// - /// Byte array containing the binary data - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T SpanToStructurePdpEndian(ReadOnlySpan bytes) where T : struct - { - object str = SpanToStructureLittleEndian(bytes); - return (T)SwapStructureMembersEndianPdp(str); } - - /// - /// Marshal PDP-11 binary data to a structure. If the structure type contains any non value type, this method will - /// crash. - /// - /// Byte array containing the binary data - /// Start on the span where the structure begins - /// Length of the structure in bytes - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T SpanToStructurePdpEndian(ReadOnlySpan bytes, int start, int length) where T : struct - { - object str = SpanToStructureLittleEndian(bytes.Slice(start, length)); - - return (T)SwapStructureMembersEndianPdp(str); - } - - /// - /// Marshal a structure depending on the decoration of . If the - /// decoration is not present it will marshal as a reference type containing little endian structure. - /// - /// Byte array containing the binary data - /// Type of the structure to marshal - /// The binary data marshalled in a structure with the specified type - /// - /// The contains an unsupported - /// endian - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MarshalStructure(byte[] bytes) where T : struct - { - if(!(typeof(T).GetCustomAttribute(typeof(MarshallingPropertiesAttribute)) is MarshallingPropertiesAttribute - properties)) - return ByteArrayToStructureLittleEndian(bytes); - - switch(properties.Endian) - { - case BitEndian.Little: - return properties.HasReferences ? ByteArrayToStructureLittleEndian(bytes) - : SpanToStructureLittleEndian(bytes); - - case BitEndian.Big: - return properties.HasReferences ? ByteArrayToStructureBigEndian(bytes) - : SpanToStructureBigEndian(bytes); - - case BitEndian.Pdp: - return properties.HasReferences ? ByteArrayToStructurePdpEndian(bytes) - : SpanToStructurePdpEndian(bytes); - default: throw new ArgumentOutOfRangeException(); - } - } - - /// Swaps all members of a structure - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining), SuppressMessage("ReSharper", "InconsistentNaming")] - public static object SwapStructureMembersEndian(object str) - { - Type t = str.GetType(); - FieldInfo[] fieldInfo = t.GetFields(); - - foreach(FieldInfo fi in fieldInfo) - if(fi.FieldType == typeof(short) || - (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(short))) - { - short x = (short)fi.GetValue(str); - fi.SetValue(str, (short)((x << 8) | ((x >> 8) & 0xFF))); - } - else if(fi.FieldType == typeof(int) || - (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(int))) - { - int x = (int)fi.GetValue(str); - x = (int)(((x << 8) & 0xFF00FF00) | (((uint)x >> 8) & 0xFF00FF)); - fi.SetValue(str, (int)(((uint)x << 16) | (((uint)x >> 16) & 0xFFFF))); - } - else if(fi.FieldType == typeof(long) || - (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(long))) - { - long x = (long)fi.GetValue(str); - x = ((x & 0x00000000FFFFFFFF) << 32) | (long)(((ulong)x & 0xFFFFFFFF00000000) >> 32); - x = ((x & 0x0000FFFF0000FFFF) << 16) | (long)(((ulong)x & 0xFFFF0000FFFF0000) >> 16); - x = ((x & 0x00FF00FF00FF00FF) << 8) | (long)(((ulong)x & 0xFF00FF00FF00FF00) >> 8); - - fi.SetValue(str, x); - } - else if(fi.FieldType == typeof(ushort) || - (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(ushort))) - { - ushort x = (ushort)fi.GetValue(str); - fi.SetValue(str, (ushort)((x << 8) | (x >> 8))); - } - else if(fi.FieldType == typeof(uint) || - (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(uint))) - { - uint x = (uint)fi.GetValue(str); - x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0xFF00FF); - fi.SetValue(str, (x << 16) | (x >> 16)); - } - else if(fi.FieldType == typeof(ulong) || - (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(ulong))) - { - ulong x = (ulong)fi.GetValue(str); - x = ((x & 0x00000000FFFFFFFF) << 32) | ((x & 0xFFFFFFFF00000000) >> 32); - x = ((x & 0x0000FFFF0000FFFF) << 16) | ((x & 0xFFFF0000FFFF0000) >> 16); - x = ((x & 0x00FF00FF00FF00FF) << 8) | ((x & 0xFF00FF00FF00FF00) >> 8); - fi.SetValue(str, x); - } - else if(fi.FieldType == typeof(float)) - { - float flt = (float)fi.GetValue(str); - byte[] flt_b = BitConverter.GetBytes(flt); - - fi.SetValue(str, BitConverter.ToSingle(new[] - { - flt_b[3], flt_b[2], flt_b[1], flt_b[0] - }, 0)); - } - else if(fi.FieldType == typeof(double)) - { - double dbl = (double)fi.GetValue(str); - byte[] dbl_b = BitConverter.GetBytes(dbl); - - fi.SetValue(str, BitConverter.ToDouble(new[] - { - dbl_b[7], dbl_b[6], dbl_b[5], dbl_b[4], dbl_b[3], dbl_b[2], dbl_b[1], dbl_b[0] - }, 0)); - } - else if(fi.FieldType == typeof(byte) || - fi.FieldType == typeof(sbyte)) - { - // Do nothing, can't byteswap them! - } - else if(fi.FieldType == typeof(Guid)) - { - // TODO: Swap GUID - } - - // TODO: Swap arrays - else if(fi.FieldType.IsValueType && - !fi.FieldType.IsEnum && - !fi.FieldType.IsArray) - { - object obj = fi.GetValue(str); - object strc = SwapStructureMembersEndian(obj); - fi.SetValue(str, strc); - } - - return str; - } - - /// Swaps all fields in an structure considering them to follow PDP endian conventions - /// Source structure - /// Resulting structure - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static object SwapStructureMembersEndianPdp(object str) - { - Type t = str.GetType(); - FieldInfo[] fieldInfo = t.GetFields(); - - foreach(FieldInfo fi in fieldInfo) - if(fi.FieldType == typeof(short) || - fi.FieldType == typeof(long) || - fi.FieldType == typeof(ushort) || - fi.FieldType == typeof(ulong) || - fi.FieldType == typeof(float) || - fi.FieldType == typeof(double) || - fi.FieldType == typeof(byte) || - fi.FieldType == typeof(sbyte) || - fi.FieldType == typeof(Guid)) - { - // Do nothing - } - else if(fi.FieldType == typeof(int) || - (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(int))) - { - int x = (int)fi.GetValue(str); - fi.SetValue(str, ((x & 0xffffu) << 16) | ((x & 0xffff0000u) >> 16)); - } - else if(fi.FieldType == typeof(uint) || - (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(uint))) - { - uint x = (uint)fi.GetValue(str); - fi.SetValue(str, ((x & 0xffffu) << 16) | ((x & 0xffff0000u) >> 16)); - } - - // TODO: Swap arrays - else if(fi.FieldType.IsValueType && - !fi.FieldType.IsEnum && - !fi.FieldType.IsArray) - { - object obj = fi.GetValue(str); - object strc = SwapStructureMembersEndianPdp(obj); - fi.SetValue(str, strc); - } - - return str; - } - - /// Marshal a structure to little-endian binary data - /// The structure you want to marshal to binary - /// Type of the structure to marshal - /// The byte array representing the given structure - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] StructureToByteArrayLittleEndian(T str) where T : struct - { - byte[] buf = new byte[SizeOf()]; - var ptr = GCHandle.Alloc(buf, GCHandleType.Pinned); - System.Runtime.InteropServices.Marshal.StructureToPtr(str, ptr.AddrOfPinnedObject(), false); - ptr.Free(); - - return buf; - } - - /// Marshal a structure to little-endian binary data - /// The structure you want to marshal to binary - /// Type of the structure to marshal - /// The byte array representing the given structure - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static byte[] StructureToByteArrayBigEndian(T str) where T : struct => - StructureToByteArrayLittleEndian((T)SwapStructureMembersEndian(str)); - - /// Converts a hexadecimal string into a byte array - /// Hexadecimal string - /// Resulting byte array - /// Number of output bytes processed - public static int ConvertFromHexAscii(string hex, out byte[] outBuf) - { - outBuf = null; - - if(hex is null || - hex == "") - return -1; - - int off = 0; - - if(hex[0] == '0' && - (hex[1] == 'x' || hex[1] == 'X')) - { - off = 2; - } - - outBuf = new byte[(hex.Length - off) / 2]; - int count = 0; - - for(int i = off; i < hex.Length; i += 2) - { - char c = hex[i]; - - if(c < '0' || - (c > '9' && c < 'A') || - (c > 'F' && c < 'a') || - c > 'f') - break; - - c -= c >= 'a' && c <= 'f' - ? '\u0057' - : c >= 'A' && c <= 'F' - ? '\u0037' - : '\u0030'; - - outBuf[(i - off) / 2] = (byte)(c << 4); - - c = hex[i + 1]; - - if(c < '0' || - (c > '9' && c < 'A') || - (c > 'F' && c < 'a') || - c > 'f') - break; - - c -= c >= 'a' && c <= 'f' - ? '\u0057' - : c >= 'A' && c <= 'F' - ? '\u0037' - : '\u0030'; - - outBuf[(i - off) / 2] += (byte)c; - - count++; - } - - return count; - } + } + + /// Marshal PDP-11 binary data to a structure + /// Byte array containing the binary data + /// Start on the array where the structure begins + /// Length of the structure in bytes + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T ByteArrayToStructurePdpEndian(byte[] bytes, int start, int length) where T : struct + { + Span span = bytes; + + return ByteArrayToStructurePdpEndian(span.Slice(start, length).ToArray()); + } + + /// + /// Marshal little-endian binary data to a structure. If the structure type contains any non value type, this + /// method will crash. + /// + /// Byte array containing the binary data + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T SpanToStructureLittleEndian(ReadOnlySpan bytes) where T : struct => + MemoryMarshal.Read(bytes); + + /// + /// Marshal little-endian binary data to a structure. If the structure type contains any non value type, this + /// method will crash. + /// + /// Byte span containing the binary data + /// Start on the span where the structure begins + /// Length of the structure in bytes + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T SpanToStructureLittleEndian(ReadOnlySpan bytes, int start, int length) + where T : struct => MemoryMarshal.Read(bytes.Slice(start, length)); + + /// + /// Marshal big-endian binary data to a structure. If the structure type contains any non value type, this method + /// will crash. + /// + /// Byte array containing the binary data + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T SpanToStructureBigEndian(ReadOnlySpan bytes) where T : struct + { + T str = SpanToStructureLittleEndian(bytes); + + return (T)SwapStructureMembersEndian(str); + } + + /// + /// Marshal big-endian binary data to a structure. If the structure type contains any non value type, this method + /// will crash. + /// + /// Byte span containing the binary data + /// Start on the span where the structure begins + /// Length of the structure in bytes + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T SpanToStructureBigEndian(ReadOnlySpan bytes, int start, int length) where T : struct + { + T str = SpanToStructureLittleEndian(bytes.Slice(start, length)); + + return (T)SwapStructureMembersEndian(str); + } + + /// + /// Marshal PDP-11 binary data to a structure. If the structure type contains any non value type, this method will + /// crash. + /// + /// Byte array containing the binary data + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T SpanToStructurePdpEndian(ReadOnlySpan bytes) where T : struct + { + object str = SpanToStructureLittleEndian(bytes); + + return (T)SwapStructureMembersEndianPdp(str); + } + + /// + /// Marshal PDP-11 binary data to a structure. If the structure type contains any non value type, this method will + /// crash. + /// + /// Byte array containing the binary data + /// Start on the span where the structure begins + /// Length of the structure in bytes + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T SpanToStructurePdpEndian(ReadOnlySpan bytes, int start, int length) where T : struct + { + object str = SpanToStructureLittleEndian(bytes.Slice(start, length)); + + return (T)SwapStructureMembersEndianPdp(str); + } + + /// + /// Marshal a structure depending on the decoration of . If the + /// decoration is not present it will marshal as a reference type containing little endian structure. + /// + /// Byte array containing the binary data + /// Type of the structure to marshal + /// The binary data marshalled in a structure with the specified type + /// + /// The contains an unsupported + /// endian + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MarshalStructure(byte[] bytes) where T : struct + { + if(!(typeof(T).GetCustomAttribute(typeof(MarshallingPropertiesAttribute)) is MarshallingPropertiesAttribute + properties)) + return ByteArrayToStructureLittleEndian(bytes); + + switch(properties.Endian) + { + case BitEndian.Little: + return properties.HasReferences ? ByteArrayToStructureLittleEndian(bytes) + : SpanToStructureLittleEndian(bytes); + + case BitEndian.Big: + return properties.HasReferences ? ByteArrayToStructureBigEndian(bytes) + : SpanToStructureBigEndian(bytes); + + case BitEndian.Pdp: + return properties.HasReferences ? ByteArrayToStructurePdpEndian(bytes) + : SpanToStructurePdpEndian(bytes); + default: throw new ArgumentOutOfRangeException(); + } + } + + /// Swaps all members of a structure + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining), SuppressMessage("ReSharper", "InconsistentNaming")] + public static object SwapStructureMembersEndian(object str) + { + Type t = str.GetType(); + FieldInfo[] fieldInfo = t.GetFields(); + + foreach(FieldInfo fi in fieldInfo) + if(fi.FieldType == typeof(short) || + (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(short))) + { + short x = (short)fi.GetValue(str); + fi.SetValue(str, (short)((x << 8) | ((x >> 8) & 0xFF))); + } + else if(fi.FieldType == typeof(int) || + (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(int))) + { + int x = (int)fi.GetValue(str); + x = (int)(((x << 8) & 0xFF00FF00) | (((uint)x >> 8) & 0xFF00FF)); + fi.SetValue(str, (int)(((uint)x << 16) | (((uint)x >> 16) & 0xFFFF))); + } + else if(fi.FieldType == typeof(long) || + (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(long))) + { + long x = (long)fi.GetValue(str); + x = ((x & 0x00000000FFFFFFFF) << 32) | (long)(((ulong)x & 0xFFFFFFFF00000000) >> 32); + x = ((x & 0x0000FFFF0000FFFF) << 16) | (long)(((ulong)x & 0xFFFF0000FFFF0000) >> 16); + x = ((x & 0x00FF00FF00FF00FF) << 8) | (long)(((ulong)x & 0xFF00FF00FF00FF00) >> 8); + + fi.SetValue(str, x); + } + else if(fi.FieldType == typeof(ushort) || + (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(ushort))) + { + ushort x = (ushort)fi.GetValue(str); + fi.SetValue(str, (ushort)((x << 8) | (x >> 8))); + } + else if(fi.FieldType == typeof(uint) || + (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(uint))) + { + uint x = (uint)fi.GetValue(str); + x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0xFF00FF); + fi.SetValue(str, (x << 16) | (x >> 16)); + } + else if(fi.FieldType == typeof(ulong) || + (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(ulong))) + { + ulong x = (ulong)fi.GetValue(str); + x = ((x & 0x00000000FFFFFFFF) << 32) | ((x & 0xFFFFFFFF00000000) >> 32); + x = ((x & 0x0000FFFF0000FFFF) << 16) | ((x & 0xFFFF0000FFFF0000) >> 16); + x = ((x & 0x00FF00FF00FF00FF) << 8) | ((x & 0xFF00FF00FF00FF00) >> 8); + fi.SetValue(str, x); + } + else if(fi.FieldType == typeof(float)) + { + float flt = (float)fi.GetValue(str); + byte[] flt_b = BitConverter.GetBytes(flt); + + fi.SetValue(str, BitConverter.ToSingle(new[] + { + flt_b[3], flt_b[2], flt_b[1], flt_b[0] + }, 0)); + } + else if(fi.FieldType == typeof(double)) + { + double dbl = (double)fi.GetValue(str); + byte[] dbl_b = BitConverter.GetBytes(dbl); + + fi.SetValue(str, BitConverter.ToDouble(new[] + { + dbl_b[7], dbl_b[6], dbl_b[5], dbl_b[4], dbl_b[3], dbl_b[2], dbl_b[1], dbl_b[0] + }, 0)); + } + else if(fi.FieldType == typeof(byte) || + fi.FieldType == typeof(sbyte)) + { + // Do nothing, can't byteswap them! + } + else if(fi.FieldType == typeof(Guid)) + { + // TODO: Swap GUID + } + + // TODO: Swap arrays + else if(fi.FieldType.IsValueType && + !fi.FieldType.IsEnum && + !fi.FieldType.IsArray) + { + object obj = fi.GetValue(str); + object strc = SwapStructureMembersEndian(obj); + fi.SetValue(str, strc); + } + + return str; + } + + /// Swaps all fields in an structure considering them to follow PDP endian conventions + /// Source structure + /// Resulting structure + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static object SwapStructureMembersEndianPdp(object str) + { + Type t = str.GetType(); + FieldInfo[] fieldInfo = t.GetFields(); + + foreach(FieldInfo fi in fieldInfo) + if(fi.FieldType == typeof(short) || + fi.FieldType == typeof(long) || + fi.FieldType == typeof(ushort) || + fi.FieldType == typeof(ulong) || + fi.FieldType == typeof(float) || + fi.FieldType == typeof(double) || + fi.FieldType == typeof(byte) || + fi.FieldType == typeof(sbyte) || + fi.FieldType == typeof(Guid)) + { + // Do nothing + } + else if(fi.FieldType == typeof(int) || + (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(int))) + { + int x = (int)fi.GetValue(str); + fi.SetValue(str, ((x & 0xffffu) << 16) | ((x & 0xffff0000u) >> 16)); + } + else if(fi.FieldType == typeof(uint) || + (fi.FieldType.IsEnum && fi.FieldType.GetEnumUnderlyingType() == typeof(uint))) + { + uint x = (uint)fi.GetValue(str); + fi.SetValue(str, ((x & 0xffffu) << 16) | ((x & 0xffff0000u) >> 16)); + } + + // TODO: Swap arrays + else if(fi.FieldType.IsValueType && + !fi.FieldType.IsEnum && + !fi.FieldType.IsArray) + { + object obj = fi.GetValue(str); + object strc = SwapStructureMembersEndianPdp(obj); + fi.SetValue(str, strc); + } + + return str; + } + + /// Marshal a structure to little-endian binary data + /// The structure you want to marshal to binary + /// Type of the structure to marshal + /// The byte array representing the given structure + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte[] StructureToByteArrayLittleEndian(T str) where T : struct + { + byte[] buf = new byte[SizeOf()]; + var ptr = GCHandle.Alloc(buf, GCHandleType.Pinned); + System.Runtime.InteropServices.Marshal.StructureToPtr(str, ptr.AddrOfPinnedObject(), false); + ptr.Free(); + + return buf; + } + + /// Marshal a structure to little-endian binary data + /// The structure you want to marshal to binary + /// Type of the structure to marshal + /// The byte array representing the given structure + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte[] StructureToByteArrayBigEndian(T str) where T : struct => + StructureToByteArrayLittleEndian((T)SwapStructureMembersEndian(str)); + + /// Converts a hexadecimal string into a byte array + /// Hexadecimal string + /// Resulting byte array + /// Number of output bytes processed + public static int ConvertFromHexAscii(string hex, out byte[] outBuf) + { + outBuf = null; + + if(hex is null || + hex == "") + return -1; + + int off = 0; + + if(hex[0] == '0' && + (hex[1] == 'x' || hex[1] == 'X')) + { + off = 2; + } + + outBuf = new byte[(hex.Length - off) / 2]; + int count = 0; + + for(int i = off; i < hex.Length; i += 2) + { + char c = hex[i]; + + if(c < '0' || + (c > '9' && c < 'A') || + (c > 'F' && c < 'a') || + c > 'f') + break; + + c -= c >= 'a' && c <= 'f' + ? '\u0057' + : c >= 'A' && c <= 'F' + ? '\u0037' + : '\u0030'; + + outBuf[(i - off) / 2] = (byte)(c << 4); + + c = hex[i + 1]; + + if(c < '0' || + (c > '9' && c < 'A') || + (c > 'F' && c < 'a') || + c > 'f') + break; + + c -= c >= 'a' && c <= 'f' + ? '\u0057' + : c >= 'A' && c <= 'F' + ? '\u0037' + : '\u0030'; + + outBuf[(i - off) / 2] += (byte)c; + + count++; + } + + return count; } } \ No newline at end of file diff --git a/MarshallingPropertiesAttribute.cs b/MarshallingPropertiesAttribute.cs index 3110674..bb9f7c3 100644 --- a/MarshallingPropertiesAttribute.cs +++ b/MarshallingPropertiesAttribute.cs @@ -38,25 +38,24 @@ using System; -namespace Aaru.Helpers +namespace Aaru.Helpers; + +/// +/// Defines properties to help marshalling structs from binary data +[AttributeUsage(AttributeTargets.Struct)] +public sealed class MarshallingPropertiesAttribute : Attribute { /// /// Defines properties to help marshalling structs from binary data - [AttributeUsage(AttributeTargets.Struct)] - public sealed class MarshallingPropertiesAttribute : Attribute + /// Defines properties to help marshalling structs from binary data + public MarshallingPropertiesAttribute(BitEndian endian) { - /// - /// Defines properties to help marshalling structs from binary data - /// Defines properties to help marshalling structs from binary data - public MarshallingPropertiesAttribute(BitEndian endian) - { - Endian = endian; - HasReferences = true; - } - - /// c - public BitEndian Endian { get; } - /// Tells if the structure, or any nested structure, has any non-value type (e.g. arrays, strings, etc). - public bool HasReferences { get; set; } + Endian = endian; + HasReferences = true; } + + /// c + public BitEndian Endian { get; } + /// Tells if the structure, or any nested structure, has any non-value type (e.g. arrays, strings, etc). + public bool HasReferences { get; set; } } \ No newline at end of file diff --git a/PrintHex.cs b/PrintHex.cs index c3ca7cd..85476c8 100644 --- a/PrintHex.cs +++ b/PrintHex.cs @@ -33,104 +33,103 @@ using System.Text; using Aaru.Console; -namespace Aaru.Helpers +namespace Aaru.Helpers; + +/// Helper operations to get hexadecimal representations of byte arrays +public static class PrintHex { - /// Helper operations to get hexadecimal representations of byte arrays - public static class PrintHex + /// Prints a byte array as hexadecimal values to the console + /// Array + /// Width of line + public static void PrintHexArray(byte[] array, int width = 16) => + AaruConsole.WriteLine(ByteArrayToHexArrayString(array, width)); + + /// Prints a byte array as hexadecimal values to a string + /// Array + /// Width of line + /// Use ANSI escape colors for sections + /// String containing hexadecimal values + public static string ByteArrayToHexArrayString(byte[] array, int width = 16, bool color = false) { - /// Prints a byte array as hexadecimal values to the console - /// Array - /// Width of line - public static void PrintHexArray(byte[] array, int width = 16) => - AaruConsole.WriteLine(ByteArrayToHexArrayString(array, width)); + if(array is null) + return null; - /// Prints a byte array as hexadecimal values to a string - /// Array - /// Width of line - /// Use ANSI escape colors for sections - /// String containing hexadecimal values - public static string ByteArrayToHexArrayString(byte[] array, int width = 16, bool color = false) + // TODO: Color list + // TODO: Allow to change width + string str = "Offset"; + int rows = array.Length / 16; + int last = array.Length % 16; + int offsetLength = $"{array.Length:X}".Length; + var sb = new StringBuilder(); + + if(last > 0) + rows++; + + if(last == 0) + last = 16; + + if(offsetLength < str.Length) + offsetLength = str.Length; + + while(str.Length < offsetLength) + str += ' '; + + if(color) + sb.Append("\u001b[36m"); + + sb.Append(str); + sb.Append(" "); + + for(int i = 0; i < 16; i++) { - if(array is null) - return null; + sb.AppendFormat(" {0:X2}", i); + } - // TODO: Color list - // TODO: Allow to change width - string str = "Offset"; - int rows = array.Length / 16; - int last = array.Length % 16; - int offsetLength = $"{array.Length:X}".Length; - var sb = new StringBuilder(); + if(color) + sb.Append("\u001b[0m"); - if(last > 0) - rows++; + sb.AppendLine(); - if(last == 0) - last = 16; + int b = 0; - if(offsetLength < str.Length) - offsetLength = str.Length; - - while(str.Length < offsetLength) - str += ' '; + string format = $"{{0:X{offsetLength}}}"; + for(int i = 0; i < rows; i++) + { if(color) sb.Append("\u001b[36m"); - sb.Append(str); - sb.Append(" "); - - for(int i = 0; i < 16; i++) - { - sb.AppendFormat(" {0:X2}", i); - } + sb.AppendFormat(format, b); if(color) sb.Append("\u001b[0m"); - sb.AppendLine(); + sb.Append(" "); + int lastBytes = i == rows - 1 ? last : 16; + int lastSpaces = 16 - lastBytes; - int b = 0; - - string format = $"{{0:X{offsetLength}}}"; - - for(int i = 0; i < rows; i++) + for(int j = 0; j < lastBytes; j++) { - if(color) - sb.Append("\u001b[36m"); - - sb.AppendFormat(format, b); - - if(color) - sb.Append("\u001b[0m"); - - sb.Append(" "); - int lastBytes = i == rows - 1 ? last : 16; - int lastSpaces = 16 - lastBytes; - - for(int j = 0; j < lastBytes; j++) - { - sb.AppendFormat(" {0:X2}", array[b]); - b++; - } - - for(int j = 0; j < lastSpaces; j++) - sb.Append(" "); - - b -= lastBytes; - sb.Append(" "); - - for(int j = 0; j < lastBytes; j++) - { - int v = array[b]; - sb.Append((v > 31 && v < 127) || v > 159 ? (char)v : '.'); - b++; - } - - sb.AppendLine(); + sb.AppendFormat(" {0:X2}", array[b]); + b++; } - return sb.ToString(); + for(int j = 0; j < lastSpaces; j++) + sb.Append(" "); + + b -= lastBytes; + sb.Append(" "); + + for(int j = 0; j < lastBytes; j++) + { + int v = array[b]; + sb.Append((v > 31 && v < 127) || v > 159 ? (char)v : '.'); + b++; + } + + sb.AppendLine(); } + + return sb.ToString(); } } \ No newline at end of file diff --git a/StringHandlers.cs b/StringHandlers.cs index 560c662..21a8775 100644 --- a/StringHandlers.cs +++ b/StringHandlers.cs @@ -33,153 +33,152 @@ using System; using System.Text; -namespace Aaru.Helpers +namespace Aaru.Helpers; + +/// Helper operations to work with strings +public static class StringHandlers { - /// Helper operations to work with strings - public static class StringHandlers + /// Converts a null-terminated (aka C string) ASCII byte array to a C# string + /// The corresponding C# string + /// A null-terminated (aka C string) ASCII byte array + public static string CToString(byte[] cString) => CToString(cString, Encoding.ASCII); + + /// Converts a null-terminated (aka C string) byte array with the specified encoding to a C# string + /// The corresponding C# string + /// A null-terminated (aka C string) byte array in the specified encoding + /// Encoding. + /// Set if encoding uses 16-bit characters. + /// Start decoding at this position + public static string CToString(byte[] cString, Encoding encoding, bool twoBytes = false, int start = 0) { - /// Converts a null-terminated (aka C string) ASCII byte array to a C# string - /// The corresponding C# string - /// A null-terminated (aka C string) ASCII byte array - public static string CToString(byte[] cString) => CToString(cString, Encoding.ASCII); + if(cString == null) + return null; - /// Converts a null-terminated (aka C string) byte array with the specified encoding to a C# string - /// The corresponding C# string - /// A null-terminated (aka C string) byte array in the specified encoding - /// Encoding. - /// Set if encoding uses 16-bit characters. - /// Start decoding at this position - public static string CToString(byte[] cString, Encoding encoding, bool twoBytes = false, int start = 0) + int len = 0; + + for(int i = start; i < cString.Length; i++) { - if(cString == null) - return null; - - int len = 0; - - for(int i = start; i < cString.Length; i++) - { - if(cString[i] == 0) - if(twoBytes) + if(cString[i] == 0) + if(twoBytes) + { + if(i + 1 < cString.Length && + cString[i + 1] == 0) { - if(i + 1 < cString.Length && - cString[i + 1] == 0) - { - len++; + len++; - break; - } - } - else break; - - len++; - } - - if(twoBytes && len % 2 > 0) - len--; - - byte[] dest = new byte[len]; - Array.Copy(cString, start, dest, 0, len); - - return len == 0 ? "" : encoding.GetString(dest); - } - - /// Converts a length-prefixed (aka Pascal string) ASCII byte array to a C# string - /// The corresponding C# string - /// A length-prefixed (aka Pascal string) ASCII byte array - public static string PascalToString(byte[] pascalString) => PascalToString(pascalString, Encoding.ASCII); - - /// Converts a length-prefixed (aka Pascal string) ASCII byte array to a C# string - /// The corresponding C# string - /// A length-prefixed (aka Pascal string) ASCII byte array - /// Encoding. - /// Start decoding at this position - public static string PascalToString(byte[] pascalString, Encoding encoding, int start = 0) - { - if(pascalString == null) - return null; - - byte length = pascalString[start]; - int len = 0; - - for(int i = start + 1; i < length + 1 && i < pascalString.Length; i++) - { - if(pascalString[i] == 0) - break; - - len++; - } - - byte[] dest = new byte[len]; - Array.Copy(pascalString, start + 1, dest, 0, len); - - return len == 0 ? "" : encoding.GetString(dest); - } - - /// Converts a space (' ', 0x20, ASCII SPACE) padded ASCII byte array to a C# string - /// The corresponding C# string - /// A space (' ', 0x20, ASCII SPACE) padded ASCII byte array - public static string SpacePaddedToString(byte[] spacePaddedString) => - SpacePaddedToString(spacePaddedString, Encoding.ASCII); - - /// Converts a space (' ', 0x20, ASCII SPACE) padded ASCII byte array to a C# string - /// The corresponding C# string - /// A space (' ', 0x20, ASCII SPACE) padded ASCII byte array - /// Encoding. - /// Start decoding at this position - public static string SpacePaddedToString(byte[] spacePaddedString, Encoding encoding, int start = 0) - { - if(spacePaddedString == null) - return null; - - int len = start; - - for(int i = spacePaddedString.Length; i >= start; i--) - { - if(i == start) - return ""; - - if(spacePaddedString[i - 1] == 0x20) - continue; - - len = i; - - break; - } - - return len == 0 ? "" : encoding.GetString(spacePaddedString, start, len); - } - - /// Converts an OSTA compressed unicode byte array to a C# string - /// The C# string. - /// OSTA compressed unicode byte array. - public static string DecompressUnicode(byte[] dstring) - { - ushort unicode; - byte compId = dstring[0]; - string temp = ""; - - if(compId != 8 && - compId != 16) - return null; - - for(int byteIndex = 1; byteIndex < dstring.Length;) - { - if(compId == 16) - unicode = (ushort)(dstring[byteIndex++] << 8); + } + } else - unicode = 0; - - if(byteIndex < dstring.Length) - unicode |= dstring[byteIndex++]; - - if(unicode == 0) break; - temp += Encoding.Unicode.GetString(BitConverter.GetBytes(unicode)); - } - - return temp; + len++; } + + if(twoBytes && len % 2 > 0) + len--; + + byte[] dest = new byte[len]; + Array.Copy(cString, start, dest, 0, len); + + return len == 0 ? "" : encoding.GetString(dest); + } + + /// Converts a length-prefixed (aka Pascal string) ASCII byte array to a C# string + /// The corresponding C# string + /// A length-prefixed (aka Pascal string) ASCII byte array + public static string PascalToString(byte[] pascalString) => PascalToString(pascalString, Encoding.ASCII); + + /// Converts a length-prefixed (aka Pascal string) ASCII byte array to a C# string + /// The corresponding C# string + /// A length-prefixed (aka Pascal string) ASCII byte array + /// Encoding. + /// Start decoding at this position + public static string PascalToString(byte[] pascalString, Encoding encoding, int start = 0) + { + if(pascalString == null) + return null; + + byte length = pascalString[start]; + int len = 0; + + for(int i = start + 1; i < length + 1 && i < pascalString.Length; i++) + { + if(pascalString[i] == 0) + break; + + len++; + } + + byte[] dest = new byte[len]; + Array.Copy(pascalString, start + 1, dest, 0, len); + + return len == 0 ? "" : encoding.GetString(dest); + } + + /// Converts a space (' ', 0x20, ASCII SPACE) padded ASCII byte array to a C# string + /// The corresponding C# string + /// A space (' ', 0x20, ASCII SPACE) padded ASCII byte array + public static string SpacePaddedToString(byte[] spacePaddedString) => + SpacePaddedToString(spacePaddedString, Encoding.ASCII); + + /// Converts a space (' ', 0x20, ASCII SPACE) padded ASCII byte array to a C# string + /// The corresponding C# string + /// A space (' ', 0x20, ASCII SPACE) padded ASCII byte array + /// Encoding. + /// Start decoding at this position + public static string SpacePaddedToString(byte[] spacePaddedString, Encoding encoding, int start = 0) + { + if(spacePaddedString == null) + return null; + + int len = start; + + for(int i = spacePaddedString.Length; i >= start; i--) + { + if(i == start) + return ""; + + if(spacePaddedString[i - 1] == 0x20) + continue; + + len = i; + + break; + } + + return len == 0 ? "" : encoding.GetString(spacePaddedString, start, len); + } + + /// Converts an OSTA compressed unicode byte array to a C# string + /// The C# string. + /// OSTA compressed unicode byte array. + public static string DecompressUnicode(byte[] dstring) + { + ushort unicode; + byte compId = dstring[0]; + string temp = ""; + + if(compId != 8 && + compId != 16) + return null; + + for(int byteIndex = 1; byteIndex < dstring.Length;) + { + if(compId == 16) + unicode = (ushort)(dstring[byteIndex++] << 8); + else + unicode = 0; + + if(byteIndex < dstring.Length) + unicode |= dstring[byteIndex++]; + + if(unicode == 0) + break; + + temp += Encoding.Unicode.GetString(BitConverter.GetBytes(unicode)); + } + + return temp; } } \ No newline at end of file diff --git a/Swapping.cs b/Swapping.cs index 3949872..3bc76ee 100644 --- a/Swapping.cs +++ b/Swapping.cs @@ -32,81 +32,80 @@ using System.Runtime.CompilerServices; -namespace Aaru.Helpers +namespace Aaru.Helpers; + +/// Helper operations to work with swapping endians +public static class Swapping { - /// Helper operations to work with swapping endians - public static class Swapping + /// Gets the PDP endian equivalent of the given little endian unsigned integer + /// Little endian unsigned integer + /// PDP unsigned integer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint PDPFromLittleEndian(uint x) => ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16); + + /// Gets the PDP endian equivalent of the given big endian unsigned integer + /// Big endian unsigned integer + /// PDP unsigned integer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint PDPFromBigEndian(uint x) => ((x & 0xff00ff) << 8) | ((x & 0xff00ff00) >> 8); + + /// Swaps the endian of the specified unsigned short integer + /// Unsigned short integer + /// Swapped unsigned short integer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort Swap(ushort x) => (ushort)((x << 8) | (x >> 8)); + + /// Swaps the endian of the specified signed short integer + /// Signed short integer + /// Swapped signed short integer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static short Swap(short x) => (short)((x << 8) | ((x >> 8) & 0xFF)); + + /// Swaps the endian of the specified unsigned integer + /// Unsigned integer + /// Swapped unsigned integer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Swap(uint x) { - /// Gets the PDP endian equivalent of the given little endian unsigned integer - /// Little endian unsigned integer - /// PDP unsigned integer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint PDPFromLittleEndian(uint x) => ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16); + x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0xFF00FF); - /// Gets the PDP endian equivalent of the given big endian unsigned integer - /// Big endian unsigned integer - /// PDP unsigned integer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint PDPFromBigEndian(uint x) => ((x & 0xff00ff) << 8) | ((x & 0xff00ff00) >> 8); + return (x << 16) | (x >> 16); + } - /// Swaps the endian of the specified unsigned short integer - /// Unsigned short integer - /// Swapped unsigned short integer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ushort Swap(ushort x) => (ushort)((x << 8) | (x >> 8)); + /// Swaps the endian of the specified signed integer + /// Signed integer + /// Swapped signed integer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int Swap(int x) + { + x = (int)(((x << 8) & 0xFF00FF00) | (((uint)x >> 8) & 0xFF00FF)); - /// Swaps the endian of the specified signed short integer - /// Signed short integer - /// Swapped signed short integer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static short Swap(short x) => (short)((x << 8) | ((x >> 8) & 0xFF)); + return (int)(((uint)x << 16) | (((uint)x >> 16) & 0xFFFF)); + } - /// Swaps the endian of the specified unsigned integer - /// Unsigned integer - /// Swapped unsigned integer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint Swap(uint x) - { - x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0xFF00FF); + /// Swaps the endian of the specified unsigned long integer + /// Unsigned long integer + /// Swapped unsigned long integer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ulong Swap(ulong x) + { + x = ((x & 0x00000000FFFFFFFF) << 32) | ((x & 0xFFFFFFFF00000000) >> 32); + x = ((x & 0x0000FFFF0000FFFF) << 16) | ((x & 0xFFFF0000FFFF0000) >> 16); + x = ((x & 0x00FF00FF00FF00FF) << 8) | ((x & 0xFF00FF00FF00FF00) >> 8); - return (x << 16) | (x >> 16); - } + return x; + } - /// Swaps the endian of the specified signed integer - /// Signed integer - /// Swapped signed integer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int Swap(int x) - { - x = (int)(((x << 8) & 0xFF00FF00) | (((uint)x >> 8) & 0xFF00FF)); + /// Swaps the endian of the specified signed long integer + /// Signed long integer + /// Swapped signed long integer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static long Swap(long x) + { + x = ((x & 0x00000000FFFFFFFF) << 32) | (long)(((ulong)x & 0xFFFFFFFF00000000) >> 32); + x = ((x & 0x0000FFFF0000FFFF) << 16) | (long)(((ulong)x & 0xFFFF0000FFFF0000) >> 16); + x = ((x & 0x00FF00FF00FF00FF) << 8) | (long)(((ulong)x & 0xFF00FF00FF00FF00) >> 8); - return (int)(((uint)x << 16) | (((uint)x >> 16) & 0xFFFF)); - } - - /// Swaps the endian of the specified unsigned long integer - /// Unsigned long integer - /// Swapped unsigned long integer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong Swap(ulong x) - { - x = ((x & 0x00000000FFFFFFFF) << 32) | ((x & 0xFFFFFFFF00000000) >> 32); - x = ((x & 0x0000FFFF0000FFFF) << 16) | ((x & 0xFFFF0000FFFF0000) >> 16); - x = ((x & 0x00FF00FF00FF00FF) << 8) | ((x & 0xFF00FF00FF00FF00) >> 8); - - return x; - } - - /// Swaps the endian of the specified signed long integer - /// Signed long integer - /// Swapped signed long integer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long Swap(long x) - { - x = ((x & 0x00000000FFFFFFFF) << 32) | (long)(((ulong)x & 0xFFFFFFFF00000000) >> 32); - x = ((x & 0x0000FFFF0000FFFF) << 16) | (long)(((ulong)x & 0xFFFF0000FFFF0000) >> 16); - x = ((x & 0x00FF00FF00FF00FF) << 8) | (long)(((ulong)x & 0xFF00FF00FF00FF00) >> 8); - - return x; - } + return x; } } \ No newline at end of file