diff --git a/BigEndianMarshal.cs b/BigEndianMarshal.cs deleted file mode 100644 index a4c5569..0000000 --- a/BigEndianMarshal.cs +++ /dev/null @@ -1,141 +0,0 @@ -// /*************************************************************************** -// The Disc Image Chef -// ---------------------------------------------------------------------------- -// -// Filename : BigEndianMarshal.cs -// Author(s) : Natalia Portillo -// -// Component : Helpers. -// -// --[ Description ] ---------------------------------------------------------- -// -// Provides marshalling for big-endian data. -// -// --[ License ] -------------------------------------------------------------- -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, see . -// -// ---------------------------------------------------------------------------- -// Copyright © 2011-2019 Natalia Portillo -// ****************************************************************************/ - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -namespace DiscImageChef -{ - public static class BigEndianMarshal - { - /// - /// Marshals a big endian structure from a byte array. - /// Nested structures are still marshalled as little endian. - /// - /// The structure. - /// Byte array. - /// Structure type. - public static T ByteArrayToStructureBigEndian(byte[] bytes) where T : struct - { - GCHandle ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned); - T str = (T)Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)); - ptr.Free(); - return (T)SwapStructureMembersEndian(str); - } - - /// - /// Swaps endian of structure members that correspond to numerical types. - /// Does not traverse nested structures. - /// - /// The structure with its members endian swapped. - /// The structure. - public static object SwapStructureMembersEndian(object str) - { - Type t = str.GetType(); - FieldInfo[] fieldInfo = t.GetFields(); - foreach(FieldInfo fi in fieldInfo) - if(fi.FieldType == typeof(short)) - { - short x = (short)fi.GetValue(str); - fi.SetValue(str, (short)((x << 8) | ((x >> 8) & 0xFF))); - } - else if(fi.FieldType == 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)) - { - 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)) - { - ushort x = (ushort)fi.GetValue(str); - fi.SetValue(str, (ushort)((x << 8) | (x >> 8))); - } - else if(fi.FieldType == 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)) - { - 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 and enums - else if(fi.FieldType.IsValueType && !fi.FieldType.IsEnum && !fi.FieldType.IsArray) - { - object obj = fi.GetValue(str); - Type ty = obj.GetType(); - object strc = SwapStructureMembersEndian(obj); - fi.SetValue(str, strc); - } - - return str; - } - } -} \ No newline at end of file diff --git a/BitEndian.cs b/BitEndian.cs new file mode 100644 index 0000000..af0324e --- /dev/null +++ b/BitEndian.cs @@ -0,0 +1,51 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : BitEndian.cs +// Author(s) : Natalia Portillo +// +// Component : Common types. +// +// --[ Description ] ---------------------------------------------------------- +// +// Defines enumerations of bit endianness. +// +// --[ License ] -------------------------------------------------------------- +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2019 Natalia Portillo +// ****************************************************************************/ + +namespace DiscImageChef.Helpers +{ + /// 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 + } +} \ No newline at end of file diff --git a/DiscImageChef.Helpers.csproj b/DiscImageChef.Helpers.csproj index 789bb5b..3ccae75 100644 --- a/DiscImageChef.Helpers.csproj +++ b/DiscImageChef.Helpers.csproj @@ -48,12 +48,14 @@ + + + - @@ -70,6 +72,7 @@ + diff --git a/Marshal.cs b/Marshal.cs new file mode 100644 index 0000000..bc08634 --- /dev/null +++ b/Marshal.cs @@ -0,0 +1,283 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : Marshal.cs +// Author(s) : Natalia Portillo +// +// Component : Helpers. +// +// --[ Description ] ---------------------------------------------------------- +// +// Provides marshalling for binary data. +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2019 Natalia Portillo +// ****************************************************************************/ + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace DiscImageChef.Helpers +{ + /// Provides methods to marshal binary data into C# structs + public static class Marshal + { + static int count; + + /// + /// 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 + public static T ByteArrayToStructureLittleEndian(byte[] bytes) where T : struct + { + GCHandle ptr = GCHandle.Alloc(bytes, GCHandleType.Pinned); + T str = + (T)System.Runtime.InteropServices.Marshal.PtrToStructure(ptr.AddrOfPinnedObject(), typeof(T)); + ptr.Free(); + return str; + } + + /// + /// 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 + public static T ByteArrayToStructureBigEndian(byte[] bytes) where T : struct + { + GCHandle 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 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 + public static T ByteArrayToStructurePdpEndian(byte[] bytes) where T : struct + { + { + GCHandle 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 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 + public static T SpanToStructureLittleEndian(ReadOnlySpan bytes) where T : struct => + MemoryMarshal.Read(bytes); + + /// + /// 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 + public static T SpanToStructureBigEndian(ReadOnlySpan bytes) where T : struct + { + T str = SpanToStructureLittleEndian(bytes); + 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 + public static T SpanToStructurePdpEndian(ReadOnlySpan bytes) where T : struct + { + object str = SpanToStructureLittleEndian(bytes); + 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 + /// + 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); + + break; + case BitEndian.Big: + return properties.HasReferences + ? ByteArrayToStructureBigEndian(bytes) + : SpanToStructureBigEndian(bytes); + + break; + + case BitEndian.Pdp: + return properties.HasReferences + ? ByteArrayToStructurePdpEndian(bytes) + : SpanToStructurePdpEndian(bytes); + default: throw new ArgumentOutOfRangeException(); + } + } + + /// + /// Swaps all members of a structure + /// + /// + /// + public static object SwapStructureMembersEndian(object str) + { + Type t = str.GetType(); + FieldInfo[] fieldInfo = t.GetFields(); + foreach(FieldInfo fi in fieldInfo) + if(fi.FieldType == typeof(short)) + { + short x = (short)fi.GetValue(str); + fi.SetValue(str, (short)((x << 8) | ((x >> 8) & 0xFF))); + } + else if(fi.FieldType == 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)) + { + 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)) + { + ushort x = (ushort)fi.GetValue(str); + fi.SetValue(str, (ushort)((x << 8) | (x >> 8))); + } + else if(fi.FieldType == 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)) + { + 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 and enums + 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; + } + + 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)) + { + int x = (int)fi.GetValue(str); + fi.SetValue(str, ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16)); + } + else if(fi.FieldType == typeof(uint)) + { + uint x = (uint)fi.GetValue(str); + fi.SetValue(str, ((x & 0xffff) << 16) | ((x & 0xffff0000) >> 16)); + } + // TODO: Swap arrays and enums + else if(fi.FieldType.IsValueType && !fi.FieldType.IsEnum && !fi.FieldType.IsArray) + { + System.Console.WriteLine("PDP {0}", count++); + + object obj = fi.GetValue(str); + object strc = SwapStructureMembersEndianPdp(obj); + fi.SetValue(str, strc); + } + + return str; + } + } +} \ No newline at end of file diff --git a/MarshallingPropertiesAttribute.cs b/MarshallingPropertiesAttribute.cs new file mode 100644 index 0000000..110185a --- /dev/null +++ b/MarshallingPropertiesAttribute.cs @@ -0,0 +1,64 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : MarshallingPropertiesAttribute.cs +// Author(s) : Natalia Portillo +// +// Component : Common types. +// +// --[ Description ] ---------------------------------------------------------- +// +// Declares properties of structs for marshalling. +// +// --[ License ] -------------------------------------------------------------- +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2019 Natalia Portillo +// ****************************************************************************/ + +using System; + +namespace DiscImageChef.Helpers +{ + /// + /// Defines properties to help marshalling structs from binary data + /// + [AttributeUsage(AttributeTargets.Struct)] + public class MarshallingPropertiesAttribute : Attribute + { + /// 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; } + + /// 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; + } + } +} \ No newline at end of file