diff --git a/ASAM.MDF.sln b/ASAM.MDF.sln index 66b0601..ffe36f5 100644 --- a/ASAM.MDF.sln +++ b/ASAM.MDF.sln @@ -1,11 +1,12 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2018 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34622.214 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASAM.MDF", "ASAM.MDF\ASAM.MDF.csproj", "{B48EFA8A-CCE6-486F-BD55-F8B8EB44E8DC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASAM.MDF.Tests", "ASAM.MDF.Tests\ASAM.MDF.Tests.csproj", "{8092B28D-5006-45E1-A489-32AE0FEBEE13}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ASAM.MDF.Tests", "ASAM.MDF.Tests\ASAM.MDF.Tests.csproj", "{8092B28D-5006-45E1-A489-32AE0FEBEE13}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DebugOpenFileMdf", "DebugOpenFileMdf\DebugOpenFileMdf.csproj", "{E24CDFA8-18C8-47E5-ACFA-D1829C6A120F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +22,10 @@ Global {8092B28D-5006-45E1-A489-32AE0FEBEE13}.Debug|Any CPU.Build.0 = Debug|Any CPU {8092B28D-5006-45E1-A489-32AE0FEBEE13}.Release|Any CPU.ActiveCfg = Release|Any CPU {8092B28D-5006-45E1-A489-32AE0FEBEE13}.Release|Any CPU.Build.0 = Release|Any CPU + {E24CDFA8-18C8-47E5-ACFA-D1829C6A120F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E24CDFA8-18C8-47E5-ACFA-D1829C6A120F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E24CDFA8-18C8-47E5-ACFA-D1829C6A120F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E24CDFA8-18C8-47E5-ACFA-D1829C6A120F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ASAM.MDF/ASAM.MDF.csproj b/ASAM.MDF/ASAM.MDF.csproj index 507ac53..3c73593 100644 --- a/ASAM.MDF/ASAM.MDF.csproj +++ b/ASAM.MDF/ASAM.MDF.csproj @@ -1,6 +1,6 @@  - net6.0-windows + net6.0 Library false diff --git a/ASAM.MDF/Libary/Block.cs b/ASAM.MDF/Libary/Block.cs index c22ad8d..c286057 100644 --- a/ASAM.MDF/Libary/Block.cs +++ b/ASAM.MDF/Libary/Block.cs @@ -1,6 +1,8 @@ namespace ASAM.MDF.Libary { using System; + using System.Collections.Generic; + using System.Linq; using System.Text; /// @@ -8,6 +10,8 @@ /// public abstract class Block { + internal List> listAddressesV23; + internal List> listAddressesV4; protected Block(Mdf mdf) { if (mdf == null) @@ -22,10 +26,10 @@ protected Block(Mdf mdf) public string Identifier { get; protected set; } [MdfVersion(400, 0)] public uint Reserved { get; set; } - public ulong Size { get; private set; } + public ulong Size { get; protected set; } [MdfVersion(400, 0)] public ulong LinksCount { get; private set; } - public ulong BlockAddress { get; private set; } + public int BlockAddress { get; set; } internal virtual ushort GetSize() { @@ -35,29 +39,44 @@ internal virtual int GetSizeTotal() { return GetSize(); } - internal void Read() + internal virtual void Read() { BlockAddress = Mdf.position; + if (Mdf.IDBlock.Version >= 400) - { ReadV4(); - return; - } + else + ReadV23(); + } + + internal virtual void ReadV23() + { Identifier = Mdf.GetString(2); // blockaddress = 0 Size = Mdf.ReadU16(); - - if (Size <= 4) - throw new FormatException(); } + internal virtual void Write(byte[] array, ref int index) { var bytesIdentifier = Encoding.UTF8.GetBytes(Identifier); var bytesSize = BitConverter.GetBytes(GetSize()); + ValidateEncodingLength(ref bytesIdentifier, 2); + Array.Copy(bytesIdentifier, 0, array, index, bytesIdentifier.Length); Array.Copy(bytesSize, 0, array, index + 2, bytesSize.Length); } + internal virtual void Write(List array) + { + var size = array.Count; + + var bytesIdentifier = Encoding.UTF8.GetBytes(Identifier); + var bytesSize = BitConverter.GetBytes(size); + + array.InsertRange(0, bytesIdentifier); + array.InsertRange(1, bytesSize); + } + /// /// Sets the string value. /// @@ -89,7 +108,7 @@ protected MdfVersionAttribute RequiredVersion(Type type, string property) return (MdfVersionAttribute)Attribute.GetCustomAttribute(type.GetProperty(property), typeof(MdfVersionAttribute)); } - private void ReadV4() + internal virtual void ReadV4() { IdHash = Mdf.ReadU16(); Identifier = Mdf.GetString(2); // blockaddress = 0 @@ -97,5 +116,10 @@ private void ReadV4() Size = Mdf.ReadU64(); LinksCount = Mdf.ReadU64(); } + public void ValidateEncodingLength(ref byte[] bytes, int countConstraints) + { + if (bytes.Length > countConstraints) + bytes = bytes.Take(countConstraints).ToArray(); + } } } diff --git a/ASAM.MDF/Libary/ChannelBlock.cs b/ASAM.MDF/Libary/ChannelBlock.cs index 4056d58..f0c5320 100644 --- a/ASAM.MDF/Libary/ChannelBlock.cs +++ b/ASAM.MDF/Libary/ChannelBlock.cs @@ -1,72 +1,80 @@ namespace ASAM.MDF.Libary { using System; - using System.IO; - using System.Text; - + using System.Collections.Generic; + using System.Linq; using ASAM.MDF.Libary.Types; - public class ChannelBlock : Block, INext + public class ChannelBlock : Block, INext, IPrevious, IParent { + public delegate void ChanelHandlerRemovedAddress(ChannelBlock block, List bytes); + private const int MIN_VERSION_LONG_SIGNAL_NAME = 212; private const int MIN_VERSION_DISPLAY_NAME = 300; private const int MIN_VERSION_ADDITIONAL_BYTE_OFFSET = 300; - private ulong ptrNextChannelBlock; - - public ulong ConponentAddress { get; private set; } - public ulong TextBlockChanelName { get; private set; } - - private ulong ptrChannelConversionBlock; - private ulong ptrDataBlockSignal; - private ulong ptrUnit; - private ulong ptrTextBlockComment; - private ulong ptrAttachment; - private ulong ptrDefaultDGBlock; - private ulong ptrDefaultCGBlock; - private ulong ptrDefaultCurrentChanelBlock; - private ulong ptrChannelExtensionBlock; - private ulong ptrChannelDependencyBlock; - private ulong ptrChannelComment; - private ulong ptrLongSignalName; - private ulong ptrDisplayName; + internal PointerAddress ptrNextChannelBlock; + internal PointerAddress ptrChannelConversionBlock; + internal PointerAddress ptrDataBlockSignal; + internal PointerAddress ptrUnit; + internal PointerAddress ptrTextBlockComment; + internal PointerAddress ptrAttachment; + internal PointerAddress ptrChannelExtensionBlock; + internal PointerAddress ptrChannelDependencyBlock; + internal PointerAddress ptrChannelComment; + internal PointerAddress ptrLongSignalName; + internal PointerAddress ptrDisplayName; + internal PointerAddress ptrComponentAddress; + internal PointerAddress ptrTextBlockChanelName; + + internal PointerAddress ptrNextChannelBlockV4; + internal PointerAddress ptrChannelConversionBlockV4; + internal PointerAddress ptrDataBlockSignalV4; + internal PointerAddress ptrUnitV4; + internal PointerAddress ptrTextBlockCommentV4; + internal PointerAddress ptrAttachmentV4; + internal PointerAddress ptrChannelExtensionBlockV4; + internal PointerAddress ptrChannelDependencyBlockV4; + internal PointerAddress ptrChannelCommentV4; + internal PointerAddress ptrDisplayNameV4; + internal PointerAddress ptrComponentAddressV4; + internal PointerAddress ptrTextBlockChanelNameV4; + private string signalName; private string signalDescription; - + private byte SyncType; + private byte DataType; private ChannelConversionBlock channelConversion; private ChannelBlock next; private ChannelBlock(Mdf mdf) : base(mdf) - { - } + { } + + public event ChanelHandlerRemovedAddress ChanelsRemovedAddress; public ChannelBlock Next { get { - if (next == null && ptrNextChannelBlock != 0) - next = Read(Mdf, ptrNextChannelBlock); + if (Mdf.IDBlock.Version >= 400) + { + if (next == null && ptrNextChannelBlockV4 != null && ptrNextChannelBlockV4.address != 0 && ptrNextChannelBlockV4.address < (ulong)Mdf.Data.Length) + next = Read(Mdf, (int)ptrNextChannelBlockV4.address); + } + else if (next == null && ptrNextChannelBlock != null && ptrNextChannelBlock.address != 0 && ptrNextChannelBlock.address < (uint)Mdf.Data.Length) + next = Read(Mdf, (int)ptrNextChannelBlock.address); return next; } } - public ChannelConversionBlock ChannelConversion - { - get - { - return channelConversion; - } - set { channelConversion = value; } - } + public ChannelBlock Previous { get; set; } + public ChannelConversionBlock ChannelConversion { get => channelConversion; set => channelConversion = value; } public ChannelExtensionBlock SourceDepending { get; private set; } public ChannelDependencyBlock Dependency { get; private set; } - public TextBlock Comment { get; private set; } + public TextBlock Comment { get; set; } public ChannelTypeV3 TypeV3 { get; set; } public ChannelTypeV4 TypeV4 { get; set; } - private byte ptrSyncType; - private byte ptrDataType; - public string SignalName { get { return signalName; } @@ -101,6 +109,7 @@ public string SignalDescription public TextBlock DisplayName { get; private set; } public uint AdditionalByteOffset { get; set; } public TextBlock Unit { get; private set; } + public ChannelGroupBlock Parent { get; set; } public static ChannelBlock Create(Mdf mdf) { @@ -111,134 +120,221 @@ public static ChannelBlock Create(Mdf mdf) SignalDescription = "", }; } - public static ChannelBlock Read(Mdf mdf, ulong position) + public static ChannelBlock Read(Mdf mdf, int position) { mdf.UpdatePosition(position); var block = new ChannelBlock(mdf); - block.Read(); - block.next = null; block.SourceDepending = null; block.Dependency = null; block.Comment = null; - if (mdf.IDBlock.Version >= 400) + block.Read(); + return block; + } + internal override void ReadV23() + { + base.ReadV23(); + + listAddressesV23 = new List>(); + + ptrNextChannelBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), 4); + ptrChannelConversionBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrNextChannelBlock.offset + 4); + ptrChannelExtensionBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrChannelConversionBlock.offset + 4); + ptrChannelDependencyBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrChannelExtensionBlock.offset + 4); + ptrChannelComment = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrChannelDependencyBlock.offset + 4); + TypeV3 = (ChannelTypeV3)Mdf.ReadU16(); + SignalName = Mdf.GetString(32); + SignalDescription = Mdf.GetString(128); + BitOffset = Mdf.ReadU16(); + NumberOfBits = Mdf.ReadU16(); + SignalTypeV3 = (SignalTypeV3)Mdf.ReadU16(); + ValueRange = Mdf.ReadBoolean(); + + listAddressesV23.AddRange(new PointerAddress[] { - ReadV4(mdf, block); - return block; - } - block.ptrNextChannelBlock = mdf.ReadU32(); - block.ptrChannelConversionBlock = mdf.ReadU32(); - block.ptrChannelExtensionBlock = mdf.ReadU32(); - block.ptrChannelDependencyBlock = mdf.ReadU32(); - block.ptrChannelComment = mdf.ReadU32(); - block.TypeV3 = (ChannelTypeV3)mdf.ReadU16(); - block.SignalName = mdf.GetString(32); - block.SignalDescription = mdf.GetString(128); - block.BitOffset = mdf.ReadU16(); - block.NumberOfBits = mdf.ReadU16(); - block.SignalTypeV3 = (SignalTypeV3)mdf.ReadU16(); - block.ValueRange = mdf.ReadBoolean(); - - if (block.ValueRange) + ptrNextChannelBlock, + ptrChannelConversionBlock, + ptrChannelExtensionBlock, + ptrChannelDependencyBlock, + ptrChannelComment + }); + + if (ValueRange) { - block.MinValue = mdf.ReadDouble(); - block.MaxValue = mdf.ReadDouble(); + MinValue = Mdf.ReadDouble(); + MaxValue = Mdf.ReadDouble(); } else { - mdf.UpdatePosition(mdf.position + 16); + Mdf.UpdatePosition(Mdf.position + 16); + } + SampleRate = Mdf.ReadDouble(); + + var offset = ptrChannelComment.offset + 2 + 32 + 128 + 2 + 2 + 2 + 2 + 16 + 8; + if (Mdf.IDBlock.Version >= MIN_VERSION_LONG_SIGNAL_NAME) + { + ptrLongSignalName = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), offset); + offset += 4; + listAddressesV23.Add(ptrLongSignalName); + + } + if (Mdf.IDBlock.Version >= MIN_VERSION_DISPLAY_NAME) + { + ptrDisplayName = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrChannelComment.offset + offset); + listAddressesV23.Add(ptrDisplayName); } + if (Mdf.IDBlock.Version >= MIN_VERSION_ADDITIONAL_BYTE_OFFSET) + AdditionalByteOffset = Mdf.ReadU16(); - block.SampleRate = mdf.ReadDouble(); + if (ptrLongSignalName != null && ptrLongSignalName.address != 0) + LongSignalName = TextBlock.Read(Mdf, (int)ptrLongSignalName.address); - if (mdf.IDBlock.Version >= MIN_VERSION_LONG_SIGNAL_NAME) - block.ptrLongSignalName = mdf.ReadU32(); + if (ptrChannelComment.address != 0) + Comment = TextBlock.Read(Mdf, (int)ptrChannelComment.address); - if (mdf.IDBlock.Version >= MIN_VERSION_DISPLAY_NAME) - block.ptrDisplayName = mdf.ReadU32(); + if (channelConversion == null && ptrChannelConversionBlock.address != 0) + ChannelConversion = ChannelConversionBlock.Read(Mdf, (int)ptrChannelConversionBlock.address); + //if (ptrChannelExtensionBlock != 0) + //{ + // if (Mdf.IDBlock.Version == 400) - if (mdf.IDBlock.Version >= MIN_VERSION_ADDITIONAL_BYTE_OFFSET) - block.AdditionalByteOffset = mdf.ReadU16(); + // SourceDepending = new ChannelExtensionBlock(Mdf, ptrChannelExtensionBlock); + //} + } - if (block.TextBlockChanelName != 0) - block.LongSignalName = TextBlock.Read(mdf, block.TextBlockChanelName); + internal override void ReadV4() + { + base.ReadV4(); + + listAddressesV4 = new List>(); + + ptrNextChannelBlockV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), 24); + ptrComponentAddressV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrNextChannelBlockV4.offset + 8); + ptrTextBlockChanelNameV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrComponentAddressV4.offset + 8); + ptrChannelExtensionBlockV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrTextBlockChanelNameV4.offset + 8); + ptrChannelConversionBlockV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrChannelExtensionBlockV4.offset + 8); + ptrDataBlockSignalV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrChannelConversionBlockV4.offset + 8); + ptrUnitV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrDataBlockSignalV4.offset + 8); + ptrTextBlockCommentV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrUnitV4.offset + 8); + //ptrAttachment = Mdf.ReadU64(); + //ptrDefaultDGBlock = Mdf.ReadU64(); + //ptrDefaultCGBlock = Mdf.ReadU64(); + //ptrDefaultCurrentChanelBlock = Mdf.ReadU64(); + TypeV4 = (ChannelTypeV4)Mdf.ReadByte(); + SyncType = Mdf.ReadByte(); + SignalTypeV4 = (SignalTypeV4)Mdf.ReadByte(); + BitOffset = Mdf.ReadByte(); + AdditionalByteOffset = Mdf.ReadU32(); + NumberOfBits = (ushort)Mdf.ReadU32(); + ChannelFlags = Mdf.ReadU32(); + InvalidBitPos = Mdf.ReadU32(); + Precision = Mdf.ReadByte(); + Reserved1 = Mdf.ReadByte(); + AttachmentCount = Mdf.ReadU16(); + ValRangeMin = Mdf.ReadDouble(); + ValRangeMax = Mdf.ReadDouble(); + LimitMin = Mdf.ReadDouble(); + LimitMax = Mdf.ReadDouble(); + LimitMinExt = Mdf.ReadDouble(); + LimitMaxExt = Mdf.ReadDouble(); + + listAddressesV4.AddRange(new PointerAddress[] + { + ptrNextChannelBlockV4, + ptrComponentAddressV4, + ptrTextBlockChanelNameV4, + ptrChannelExtensionBlockV4, + ptrChannelConversionBlockV4, + ptrDataBlockSignalV4, + ptrUnitV4, + ptrTextBlockCommentV4 + }); + + if (ptrTextBlockChanelNameV4.address != 0) + LongSignalName = TextBlock.Read(Mdf, (int)ptrTextBlockChanelNameV4.address); + + if (ptrUnitV4.address != 0) + Unit = TextBlock.Read(Mdf, (int)ptrUnitV4.address); + + if (ptrTextBlockCommentV4.address != 0) + Comment = TextBlock.Read(Mdf, (int)ptrTextBlockCommentV4.address); + + if (channelConversion == null && ptrChannelConversionBlockV4.address != 0) + ChannelConversion = ChannelConversionBlock.Read(Mdf, (int)ptrChannelConversionBlockV4.address); + } + /// + /// Set this address 0 for previous channel. Lost address + /// + /// Copied modified the entire array of mdf bytes + public List Remove(List bytes) + { + if (TypeV3 == ChannelTypeV3.Time) + return bytes; - if (block.ptrUnit != 0) - block.Unit = TextBlock.Read(mdf, block.ptrUnit); + var previous = Previous; + if (previous == null && next != null)// first of list node channels: [X channel]->[1 channel]->[2 channel]->...->[n channel] + { + ChanelsRemovedAddress?.Invoke(this, bytes); - if (block.ptrTextBlockComment != 0) - block.Comment = TextBlock.Read(mdf, block.ptrTextBlockComment); + next.Previous = null; - if (block.ptrLongSignalName != 0) - block.LongSignalName = TextBlock.Read(mdf, block.ptrLongSignalName); + return bytes; + } + else if (previous == null && next == null) // [null] -> [X channel] -> [null] + { + ChanelsRemovedAddress?.Invoke(this, bytes); - if (block.channelConversion == null && block.ptrChannelConversionBlock != 0) - block.ChannelConversion = ChannelConversionBlock.Read(block.Mdf, block.ptrChannelConversionBlock); - //if (block.ptrChannelExtensionBlock != 0) - //{ - // if (mdf.IDBlock.Version == 400) + return bytes; + } - // block.SourceDepending = new ChannelExtensionBlock(mdf, block.ptrChannelExtensionBlock); - //} + if (Mdf.IDBlock.Version >= 400) + bytes = RemoveV4(bytes, previous); + else + bytes = RemoveV23(bytes, previous); - return block; + previous.next = next; + + if (next != null) + next.Previous = previous; + + ChanelsRemovedAddress?.Invoke(this, bytes); + + return bytes; + } + + private List RemoveV23(List bytes, ChannelBlock previous) + { + var thisPointer = previous.BlockAddress + previous.ptrNextChannelBlock.offset; + + var newbytes = BitConverter.GetBytes(ptrNextChannelBlock.address); + for (int i = thisPointer, j = 0; j < newbytes.Length; i++, j++) + bytes[i] = newbytes[j]; + + previous.ptrNextChannelBlock.address = ptrNextChannelBlock.address; + ptrNextChannelBlock.address = 0; + + return bytes; } - private static void ReadV4(Mdf mdf, ChannelBlock block) + private List RemoveV4(List bytes, ChannelBlock previous) { - block.ptrNextChannelBlock = mdf.ReadU64(); - block.ConponentAddress = mdf.ReadU64(); - block.TextBlockChanelName = mdf.ReadU64(); - block.ptrChannelExtensionBlock = mdf.ReadU64(); - block.ptrChannelConversionBlock = mdf.ReadU64(); - block.ptrDataBlockSignal = mdf.ReadU64(); - block.ptrUnit = mdf.ReadU64(); - block.ptrTextBlockComment = mdf.ReadU64(); - //block.ptrAttachment = mdf.ReadU64(); - //block.ptrDefaultDGBlock = mdf.ReadU64(); - //block.ptrDefaultCGBlock = mdf.ReadU64(); - //block.ptrDefaultCurrentChanelBlock = mdf.ReadU64(); - block.TypeV4 = (ChannelTypeV4)mdf.ReadByte(); - block.ptrSyncType = mdf.ReadByte(); - block.SignalTypeV4 = (SignalTypeV4)mdf.ReadByte(); - block.BitOffset = mdf.ReadByte(); - block.AdditionalByteOffset = mdf.ReadU32(); - block.NumberOfBits = (ushort)mdf.ReadU32(); - block.ChannelFlags = mdf.ReadU32(); - block.InvalidBitPos = mdf.ReadU32(); - block.Precision = mdf.ReadByte(); - block.Reserved1 = mdf.ReadByte(); - block.AttachmentCount = mdf.ReadU16(); - block.ValRangeMin = mdf.ReadDouble(); - block.ValRangeMax = mdf.ReadDouble(); - block.LimitMin = mdf.ReadDouble(); - block.LimitMax = mdf.ReadDouble(); - block.LimitMinExt = mdf.ReadDouble(); - block.LimitMaxExt = mdf.ReadDouble(); - - if (block.TextBlockChanelName != 0) - block.LongSignalName = TextBlock.Read(mdf, block.TextBlockChanelName); - - if (block.ptrUnit != 0) - block.Unit = TextBlock.Read(mdf, block.ptrUnit); - - if (block.ptrTextBlockComment != 0) - block.Comment = TextBlock.Read(mdf, block.ptrTextBlockComment); - - if (block.ptrLongSignalName != 0) - block.LongSignalName = TextBlock.Read(mdf, block.ptrLongSignalName); - - if (block.channelConversion == null && block.ptrChannelConversionBlock != 0) - block.ChannelConversion = ChannelConversionBlock.Read(block.Mdf, block.ptrChannelConversionBlock); + var thisPointer = previous.BlockAddress + previous.ptrNextChannelBlockV4.offset; // this pointer on prev channel next + + var newbytes = BitConverter.GetBytes(ptrNextChannelBlockV4.address); + for (int i = thisPointer, j = 0; j < newbytes.Length; i++, j++) + bytes[i] = newbytes[j]; + + previous.ptrNextChannelBlockV4.address = ptrNextChannelBlockV4.address; + + return bytes; } public override string ToString() { return SignalName; } - internal override ushort GetSize() { // Base size. @@ -252,6 +348,7 @@ internal override ushort GetSize() // 3.00 return 228; } + internal override int GetSizeTotal() { var size = base.GetSizeTotal(); @@ -259,12 +356,21 @@ internal override int GetSizeTotal() if (channelConversion != null) size += channelConversion.GetSizeTotal(); + if (Comment != null) + size += Comment.GetSizeTotal(); + + if (LongSignalName != null) + size += LongSignalName.GetSizeTotal(); + + if (DisplayName != null) + size += DisplayName.GetSizeTotal(); + return size; } internal override void Write(byte[] array, ref int index) { base.Write(array, ref index); - + var bytesChannelType = BitConverter.GetBytes((ushort)TypeV3); var bytesSignalName = Mdf.IDBlock.Encoding.GetBytes(SignalName); var bytesSignalDesc = Mdf.IDBlock.Encoding.GetBytes(SignalDescription); @@ -276,6 +382,9 @@ internal override void Write(byte[] array, ref int index) var bytesMaxValue = BitConverter.GetBytes(MaxValue); var bytesSampleRate = BitConverter.GetBytes(SampleRate); + ValidateEncodingLength(ref bytesSignalName, 32); + ValidateEncodingLength(ref bytesSignalDesc, 128); + Array.Copy(bytesChannelType, 0, array, index + 24, bytesChannelType.Length); Array.Copy(bytesSignalName, 0, array, index + 26, bytesSignalName.Length); Array.Copy(bytesSignalDesc, 0, array, index + 58, bytesSignalDesc.Length); @@ -287,14 +396,8 @@ internal override void Write(byte[] array, ref int index) Array.Copy(bytesMaxValue, 0, array, index + 202, bytesMaxValue.Length); Array.Copy(bytesSampleRate, 0, array, index + 210, bytesSampleRate.Length); - if (Mdf.IDBlock.Version >= 212) - { - // TODO: LongSignalName. - } - if (Mdf.IDBlock.Version >= 300) { - // TODO: DisplayName. var bytesAdditionalOffset = BitConverter.GetBytes(AdditionalByteOffset); Array.Copy(bytesAdditionalOffset, 0, array, index + 226, bytesAdditionalOffset.Length); @@ -302,6 +405,17 @@ internal override void Write(byte[] array, ref int index) index += GetSize(); } + internal void WriteComment(byte[] array, ref int index, int blockIndex) + { + if (Comment == null) + return; + + var bytesComment = BitConverter.GetBytes(index); + + Array.Copy(bytesComment, 0, array, blockIndex + 20, bytesComment.Length); + + Comment.Write(array, ref index); + } internal void WriteChannelConversion(byte[] array, ref int index, int blockIndex) { if (channelConversion == null) @@ -313,11 +427,75 @@ internal void WriteChannelConversion(byte[] array, ref int index, int blockIndex ChannelConversion.Write(array, ref index); } + internal void WriteLongSignalName(byte[] array, ref int index, int blockIndex) + { + if (LongSignalName == null || Mdf.IDBlock.Version < 212) + return; + + var bytesptrLongSignalName = BitConverter.GetBytes(index); + + var offset = 20 + 2 + 32 + 128 + 2 + 2 + 2 + 2 + 16 + 8; + + Array.Copy(bytesptrLongSignalName, 0, array, blockIndex + offset, bytesptrLongSignalName.Length); + + LongSignalName.Write(array, ref index); + } + internal void WriteDisplayName(byte[] array, ref int index, int blockIndex) + { + if (DisplayName == null || Mdf.IDBlock.Version < 300) + return; + + var bytesptrDisplayName = BitConverter.GetBytes(index); + var offset = 20 + 2 + 32 + 128 + 2 + 2 + 2 + 2 + 16 + 8; + + if (LongSignalName != null) + offset += 4; + + Array.Copy(bytesptrDisplayName, 0, array, blockIndex + offset, bytesptrDisplayName.Length); + + DisplayName.Write(array, ref index); + } internal void WriteNextChannelLink(byte[] array, int index, int blockIndex) { var bytesNextChannelLink = BitConverter.GetBytes(index); Array.Copy(bytesNextChannelLink, 0, array, blockIndex + 4, bytesNextChannelLink.Length); } + + internal void ChannelUpdateAddress(int indexDeleted, List bytes, ulong countDeleted) + { + if (Mdf.IDBlock.Version >= 400) + ChannelUpdateAddressV4(indexDeleted,bytes, countDeleted); + else + ChannelUpdateAddressV23(indexDeleted, bytes, (uint)countDeleted); + + ChannelConversion?.ChannelConversionUpdateAddress(indexDeleted, bytes, countDeleted); + } + + private void ChannelUpdateAddressV23(int indexDeleted, List bytes, uint countDeleted) + { + foreach (var ptr in listAddressesV23) + { + if ((int)ptr.address >= indexDeleted) + { + ptr.address -= countDeleted; + + this.CopyAddress(ptr, bytes, indexDeleted, countDeleted); + } + } + } + + private void ChannelUpdateAddressV4(int indexDeleted, List bytes, ulong countDeleted) + { + foreach (var ptr in listAddressesV4) + { + if ((int)ptr.address >= indexDeleted) + { + ptr.address -= countDeleted; + + this.CopyAddress(ptr, bytes, indexDeleted, countDeleted); + } + } + } } } diff --git a/ASAM.MDF/Libary/ChannelCollection.cs b/ASAM.MDF/Libary/ChannelCollection.cs index db3d0de..1c37344 100644 --- a/ASAM.MDF/Libary/ChannelCollection.cs +++ b/ASAM.MDF/Libary/ChannelCollection.cs @@ -2,27 +2,29 @@ { using System; using System.Collections.Generic; + using System.Linq; public class ChannelCollection : IList { private List items = new List(); - public ChannelCollection(Mdf mdf) + public ChannelCollection(Mdf mdf, ChannelGroupBlock parent) { if (mdf == null) throw new ArgumentNullException("mdf"); - } - public Mdf Mdf { get; private set; } - public int Count - { - get { return items.Count; } + Parent = parent; } + + public Mdf Mdf { get; } + public int Count => items.Count; public bool IsReadOnly { get { throw new NotImplementedException(); } } + ChannelGroupBlock Parent { get; } + public ChannelBlock this[int index] { get @@ -35,9 +37,13 @@ public ChannelBlock this[int index] } } - internal void Read(ChannelBlock cnBlock) + internal void Read(ChannelBlock cnBlock, ChannelBlock.ChanelHandlerRemovedAddress action) { - items = Common.BuildBlockList(null, cnBlock); + items = Common.BuildBlockList(null, cnBlock, Parent); + + if (action != null) + foreach (var item in items) + item.ChanelsRemovedAddress += (ch, bytes) => action(ch, bytes); } internal void Write(byte[] array, ref int index) { @@ -56,6 +62,9 @@ internal void Write(byte[] array, ref int index) block.Write(array, ref index); block.WriteChannelConversion(array, ref index, prevIndex); + block.WriteComment(array, ref index, prevIndex); + block.WriteLongSignalName(array, ref index, prevIndex); + block.WriteDisplayName(array, ref index, prevIndex); } } diff --git a/ASAM.MDF/Libary/ChannelConversionBlock.cs b/ASAM.MDF/Libary/ChannelConversionBlock.cs index a9f438d..8e7c044 100644 --- a/ASAM.MDF/Libary/ChannelConversionBlock.cs +++ b/ASAM.MDF/Libary/ChannelConversionBlock.cs @@ -3,11 +3,22 @@ using System; using System.Collections.Generic; using System.IO; - + using System.Linq; + using System.Security.Cryptography; using ASAM.MDF.Libary.Types; public class ChannelConversionBlock : Block { + internal PointerAddress ptrTextBlockName; + internal PointerAddress ptrTextBlockUnit; + internal PointerAddress ptrFileComment; + internal PointerAddress ptrInverseConversion; + + internal PointerAddress ptrTextBlockNameV4; + internal PointerAddress ptrTextBlockUnitV4; + internal PointerAddress ptrFileCommentV4; + internal PointerAddress ptrInverseConversionV4; + private string physicalUnit; private ChannelConversionBlock(Mdf mdf) : base(mdf) @@ -30,13 +41,6 @@ public string PhysicalUnit public ushort SizeInformation { get; private set; } public ushort ValParamCount { get; private set; } public ConversionData AdditionalConversionData { get; internal set; } - public ulong TextBlockName { get; private set; } - public ulong TextBlockUnit { get; private set; } - - private ulong ptrFileComment; - private int indexPointer; - - public ulong InverseConversion { get; private set; } public TextBlock FileComment { get; private set; } public TextBlock ConversionUnit { get; private set; } public TextBlock ConversionName { get; private set; } @@ -51,67 +55,80 @@ public static ChannelConversionBlock Create(Mdf mdf) PhysicalUnit = "", }; } - internal static ChannelConversionBlock Read(Mdf mdf, ulong position) + internal static ChannelConversionBlock Read(Mdf mdf, int position) { mdf.position = position; var block = new ChannelConversionBlock(mdf); + block.Read(); + return block; + } + + internal override void ReadV23() + { + base.ReadV23(); + + PhysicalValueRangeValid = Mdf.Read16() != 0; + MinPhysicalValue = Mdf.ReadDouble(); + MaxPhysicalValue = Mdf.ReadDouble(); + PhysicalUnit = Mdf.GetString(20); + ConversionType = (ConversionType)Mdf.ReadU16(); + SizeInformation = Mdf.ReadU16(); - if (mdf.IDBlock.Version >= 400) + if (SizeInformation > 0) { - - block.TextBlockName = mdf.ReadU64(); - block.TextBlockUnit = mdf.ReadU64(); - block.ptrFileComment = mdf.ReadU64(); - block.InverseConversion = mdf.ReadU64(); - var lastPosAddress = mdf.position; + AdditionalConversionData.Data = new byte[ConversionData.GetEstimatedParametersSize(ConversionType)]; + Array.Copy(Mdf.Data, Mdf.position, AdditionalConversionData.Data, 0, AdditionalConversionData.Data.Length); + } + } - if (block.LinksCount > 4) - mdf.UpdatePosition(lastPosAddress + (block.LinksCount - 4) * 8); + internal override void ReadV4() + { + base.ReadV4(); - block.ConversionType4 = (ConversionType4)mdf.ReadByte(); - block.Precision = mdf.ReadByte(); - block.Flags = mdf.ReadU16(); - block.SizeInformation = mdf.ReadU16(); - block.ValParamCount = mdf.ReadU16(); - block.MinPhysicalValue = mdf.ReadDouble(); - block.MaxPhysicalValue = mdf.ReadDouble(); + listAddressesV4 = new List>(); - block.indexPointer = (int)mdf.position; + ptrTextBlockNameV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), 24); + ptrTextBlockUnitV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf),ptrTextBlockName.offset + 8); + ptrFileCommentV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrTextBlockUnit.offset + 8); + ptrInverseConversionV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrFileComment.offset + 8); + var lastPosAddress = Mdf.position; - block.AdditionalConversionData.Data = new byte[block.ValParamCount * 8]; + if (LinksCount > 4) + Mdf.UpdatePosition(lastPosAddress + ((int)LinksCount - 4) * 8); - Array.Copy(mdf.Data, block.indexPointer, block.AdditionalConversionData.Data, 0, block.AdditionalConversionData.Data.Length); - } - else + ConversionType4 = (ConversionType4)Mdf.ReadByte(); + Precision = Mdf.ReadByte(); + Flags = Mdf.ReadU16(); + SizeInformation = Mdf.ReadU16(); + ValParamCount = Mdf.ReadU16(); + MinPhysicalValue = Mdf.ReadDouble(); + MaxPhysicalValue = Mdf.ReadDouble(); + + listAddressesV4.AddRange(new PointerAddress[] { - block.PhysicalValueRangeValid = mdf.Read16() != 0; - block.MinPhysicalValue = mdf.ReadDouble(); - block.MaxPhysicalValue = mdf.ReadDouble(); - block.PhysicalUnit = mdf.GetString(20); - block.ConversionType = (ConversionType)mdf.ReadU16(); - block.SizeInformation = mdf.ReadU16(); - - if (block.SizeInformation > 0) - { - block.AdditionalConversionData.Data = new byte[ConversionData.GetEstimatedParametersSize(block.ConversionType)]; + ptrTextBlockNameV4, + ptrTextBlockUnitV4, + ptrFileCommentV4, + ptrInverseConversionV4, + }); - Array.Copy(mdf.Data, (int)mdf.position, block.AdditionalConversionData.Data, 0, block.AdditionalConversionData.Data.Length); - } - } - if (block.ptrFileComment != 0) - block.FileComment = TextBlock.Read(mdf, block.ptrFileComment); - - if (block.TextBlockName != 0) - block.ConversionName = TextBlock.Read(mdf, block.TextBlockName); - - if (block.TextBlockUnit != 0) - block.ConversionUnit = TextBlock.Read(mdf, block.TextBlockUnit); + var indexPointer = Mdf.position; - return block; - } + AdditionalConversionData.Data = new byte[ValParamCount * 8]; + + Array.Copy(Mdf.Data, indexPointer, AdditionalConversionData.Data, 0, AdditionalConversionData.Data.Length); + + if (ptrFileCommentV4.address != 0) + FileComment = TextBlock.Read(Mdf, (int)ptrFileCommentV4.address); + if (ptrTextBlockNameV4.address != 0) + ConversionName = TextBlock.Read(Mdf, (int)ptrTextBlockNameV4.address); + + if (ptrTextBlockUnitV4.address != 0) + ConversionUnit = TextBlock.Read(Mdf, (int)ptrTextBlockUnitV4.address); + } internal override ushort GetSize() { ushort size = 46; @@ -130,6 +147,8 @@ internal override void Write(byte[] array, ref int index) var bytesMaxValue = BitConverter.GetBytes(MaxPhysicalValue); var bytesPhyUnit = Mdf.IDBlock.Encoding.GetBytes(PhysicalUnit); var bytesConversionType = BitConverter.GetBytes((ushort)ConversionType); + + ValidateEncodingLength(ref bytesPhyUnit, 20); Array.Copy(bytesValueRange, 0, array, index + 4, bytesValueRange.Length); Array.Copy(bytesMinValue, 0, array, index + 6, bytesMinValue.Length); @@ -147,5 +166,24 @@ internal override void Write(byte[] array, ref int index) index += GetSize(); } + + internal void ChannelConversionUpdateAddress(int indexDeleted, List bytes, ulong countDeleted) + { + if (Mdf.IDBlock.Version >= 400) + ChannelConversionUpdateAddressV4(indexDeleted, bytes, countDeleted); + } + + private void ChannelConversionUpdateAddressV4(int indexDeleted, List bytes, ulong countDeleted) + { + foreach (var ptr in listAddressesV4) + { + if ((int)ptr.address >= indexDeleted) + { + ptr.address -= countDeleted; + + this.CopyAddress(ptr, bytes, indexDeleted, countDeleted); + } + } + } } } diff --git a/ASAM.MDF/Libary/ChannelExtensionBlock.cs b/ASAM.MDF/Libary/ChannelExtensionBlock.cs index a3aef21..54550d7 100644 --- a/ASAM.MDF/Libary/ChannelExtensionBlock.cs +++ b/ASAM.MDF/Libary/ChannelExtensionBlock.cs @@ -12,8 +12,7 @@ public class ChannelExtensionBlock : Block public DimBlockSupplement DimBlockSupplement { get; private set; } public VectorCanBlockSupplement VectorCanBlockSupplement { get; private set; } - public ChannelExtensionBlock(Mdf mdf, ulong position) - : base(mdf) + public ChannelExtensionBlock(Mdf mdf, ulong position) : base(mdf) { byte[] data = new byte[Size - 4]; data = Mdf.Data.Take(data.Length).ToArray(); @@ -21,13 +20,13 @@ public ChannelExtensionBlock(Mdf mdf, ulong position) Type = (ExtensionType)BitConverter.ToUInt16(data, 0); if (Type == ExtensionType.DIM) - { - DimBlockSupplement = new DimBlockSupplement(mdf); - } - else if (Type == ExtensionType.VectorCAN) - { - VectorCanBlockSupplement = new VectorCanBlockSupplement(mdf); - } + { + DimBlockSupplement = new DimBlockSupplement(mdf); + } + else if (Type == ExtensionType.VectorCAN) + { + VectorCanBlockSupplement = new VectorCanBlockSupplement(mdf); + } } } } diff --git a/ASAM.MDF/Libary/ChannelGroupBlock.cs b/ASAM.MDF/Libary/ChannelGroupBlock.cs index 832223b..4fd750a 100644 --- a/ASAM.MDF/Libary/ChannelGroupBlock.cs +++ b/ASAM.MDF/Libary/ChannelGroupBlock.cs @@ -1,42 +1,59 @@ namespace ASAM.MDF.Libary { using System; - using System.IO; + using System.Collections.Generic; + using System.Net; - public class ChannelGroupBlock : Block, INext + public class ChannelGroupBlock : Block, INext, IPrevious, IParent { - private ulong ptrNextChannelGroup; - private ulong ptrFirstChannelBlock; - private ulong ptrTextName; - private ulong ptrTextBlock; - private ulong ptrSourceInfo; + public delegate void ChannelGroupBlockHandler(ChannelGroupBlock block, List bytes); + + internal PointerAddress ptrNextChannelGroup; + internal PointerAddress ptrFirstChannelBlock; + internal PointerAddress ptrTextName; + internal PointerAddress ptrTextBlock; + internal PointerAddress ptrSourceInfo; + internal PointerAddress ptrFirstSampleReductionBlock; + + internal PointerAddress ptrNextChannelGroupV4; + internal PointerAddress ptrFirstChannelBlockV4; + internal PointerAddress ptrTextNameV4; + internal PointerAddress ptrTextBlockV4; + internal PointerAddress ptrSourceInfoV4; + internal PointerAddress ptrFirstSampleReductionBlockV4; + private char pathSeparator; - private ulong ptrFirstSampleReductionBlock; private ChannelGroupBlock next; private ChannelGroupBlock(Mdf mdf) : base(mdf) { + Channels = new ChannelCollection(mdf, this); } public ChannelGroupBlock Next { get { - if (next == null && ptrNextChannelGroup != 0) - next = Read(Mdf, ptrNextChannelGroup); + if (Mdf.IDBlock.Version >= 400) + { + if (next == null && ptrNextChannelGroupV4 != null && ptrNextChannelGroupV4.address != 0) + next = Read(Mdf, (int)ptrNextChannelGroupV4.address); + } + else if (next == null && ptrNextChannelGroup != null && ptrNextChannelGroup.address != 0) + next = Read(Mdf, (int)ptrNextChannelGroup.address); return next; } } + public event ChannelGroupBlockHandler ChannelGroupRemove; + public ChannelGroupBlock Previous { get; set; } public ChannelCollection Channels { get; private set; } public TextBlock Comment { get; set; } public ulong RecordID { get; private set; } public ulong CycleCount { get; private set; } public ushort Flags { get; private set; } - - public uint Reserved1 { get; private set; } public uint DataBytes { get; private set; } public uint InvalidBytes { get; private set; } @@ -45,86 +62,164 @@ public ChannelGroupBlock Next public uint NumRecords { get; set; } public SampleReductionCollection SampleReductions { get; private set; } public TextBlock TextName { get; private set; } + public DataGroupBlock Parent { get; set; } + public SourceInformation SourceInformation { get; private set; } public static ChannelGroupBlock Create(Mdf mdf) { - return new ChannelGroupBlock(mdf) - { - Channels = new ChannelCollection(mdf), - Identifier = "CG", - }; + return new ChannelGroupBlock(mdf) { Identifier = "CG" }; } - internal static ChannelGroupBlock Read(Mdf mdf, ulong position) + internal static ChannelGroupBlock Read(Mdf mdf, int position) { mdf.UpdatePosition(position); var block = new ChannelGroupBlock(mdf); - block.Read(); - block.next = null; - block.Channels = new ChannelCollection(mdf); block.Comment = null; block.SampleReductions = null; - if (mdf.IDBlock.Version >= 400) + block.Read(); + + return block; + } + internal override void ReadV23() + { + base.ReadV23(); + + listAddressesV23 = new List>(); + + ptrNextChannelGroup = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), 4); + ptrFirstChannelBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrNextChannelGroup.offset + 4); + ptrTextBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrFirstChannelBlock.offset + 4); + RecordID = Mdf.ReadU16(); + NumChannels = Mdf.ReadU16(); + RecordSize = Mdf.ReadU16(); + NumRecords = Mdf.ReadU32(); + + listAddressesV23.AddRange(new PointerAddress[] + { + ptrNextChannelGroup, + ptrFirstChannelBlock, + ptrTextBlock, + }); + + if (Size >= 26) { - ReadV4(mdf, block); - return block; + ptrFirstSampleReductionBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrTextBlock.offset + 2 + 2 + 2 + 4); + listAddressesV23.Add(ptrFirstSampleReductionBlock); } - block.ptrNextChannelGroup = mdf.ReadU32(); - block.ptrFirstChannelBlock = mdf.ReadU32(); - block.ptrTextBlock = mdf.ReadU32(); - block.RecordID = mdf.ReadU16(); - block.NumChannels = mdf.ReadU16(); - block.RecordSize = mdf.ReadU16(); - block.NumRecords = mdf.ReadU32(); - if (block.Size >= 26) - block.ptrFirstSampleReductionBlock = mdf.ReadU32(); + if (ptrTextBlock.address != 0) + Comment = TextBlock.Read(Mdf, (int)ptrTextBlock.address); - if (block.ptrTextBlock != 0) - block.Comment = TextBlock.Read(mdf, block.ptrTextBlock); + if (ptrFirstChannelBlock.address != 0) + { + var chBlock = ChannelBlock.Read(Mdf, (int)ptrFirstChannelBlock.address); + //chBlock.ChanelsRemovedAddress += (ch, bytes) => ChBlock_ChanelsRemovedAddress(ch, bytes); + + Channels.Read(chBlock, ChBlock_ChanelsRemovedAddress); + } + } + internal override void ReadV4() + { + base.ReadV4(); + + listAddressesV4 = new List>(); + + ptrNextChannelGroupV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), 24); + ptrFirstChannelBlockV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrNextChannelGroupV4.offset + 8); + ptrTextNameV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrFirstChannelBlockV4.offset + 8); + ptrSourceInfoV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrTextNameV4.offset + 8); + ptrFirstSampleReductionBlockV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrSourceInfoV4.offset + 8); + ptrTextBlockV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrFirstSampleReductionBlockV4.offset + 8); + RecordID = Mdf.ReadU64(); + CycleCount = Mdf.ReadU64(); + Flags = Mdf.ReadU16(); + pathSeparator = Mdf.ReadChar(); + Reserved1 = Mdf.ReadU32(); + DataBytes = Mdf.ReadU32(); + InvalidBytes = Mdf.ReadU32(); + + listAddressesV4.AddRange(new PointerAddress[] + { + ptrNextChannelGroupV4, + ptrFirstChannelBlockV4, + ptrTextNameV4, + ptrSourceInfoV4, + ptrFirstSampleReductionBlockV4, + ptrTextBlockV4, + }); - if (block.ptrTextName != 0) - block.TextName = TextBlock.Read(mdf, block.ptrTextName); + if (ptrSourceInfoV4.address != 0) + SourceInformation = SourceInformation.Read(Mdf, (int)ptrSourceInfoV4.address); - if (block.ptrFirstChannelBlock != 0) - block.Channels.Read(ChannelBlock.Read(mdf, block.ptrFirstChannelBlock)); + if (ptrTextBlockV4.address != 0) + Comment = TextBlock.Read(Mdf, (int)ptrTextBlockV4.address); - //if (m_ptrFirstSampleReductionBlock != 0) - //{ - // mdf.Data.Position = m_ptrFirstSampleReductionBlock; - // SampleReductions = new SampleReductionCollection(mdf, new SampleReductionBlock(mdf)); - //} + if (ptrTextNameV4.address != 0) + TextName = TextBlock.Read(Mdf, (int)ptrTextNameV4.address); - return block; + if (ptrFirstChannelBlockV4.address != 0) + Channels.Read(ChannelBlock.Read(Mdf, (int)ptrFirstChannelBlockV4.address), ChBlock_ChanelsRemovedAddress); + } + private void ChBlock_ChanelsRemovedAddress(ChannelBlock block, List bytes) + { + if (Mdf.IDBlock.Version >= 400) + RemoveChannelsV4(block, bytes); + else + RemoveChannelsV23(block, bytes); + } + + private void RemoveChannelsV23(ChannelBlock block, List bytes) + { + NumChannels -= 1; + + if (NumChannels > 1) + { + var thisPointer = BlockAddress; + if (Previous != null) + thisPointer = (int)Previous.ptrNextChannelGroup.address; + else + thisPointer = (int)Parent.ptrFirstChannelGroupBlock.address; + + var addressNumChannels = thisPointer + ptrTextBlock.offset + 4 + 2/*RecordID*/; + var newbytes = BitConverter.GetBytes(NumChannels); + + for (int i = addressNumChannels, j = 0; j < newbytes.Length; i++, j++) + bytes[i] = newbytes[j]; + } + else + ChannelGroupRemove?.Invoke(this, bytes); } - private static void ReadV4(Mdf mdf, ChannelGroupBlock block) + private void RemoveChannelsV4(ChannelBlock block, List bytes) { - block.ptrNextChannelGroup = mdf.ReadU64(); - block.ptrFirstChannelBlock = mdf.ReadU64(); - block.ptrTextName = mdf.ReadU64(); - block.ptrSourceInfo = mdf.ReadU64(); - block.ptrFirstSampleReductionBlock = mdf.ReadU64(); - block.ptrTextBlock = mdf.ReadU64(); - block.RecordID = mdf.ReadU64(); - block.CycleCount = mdf.ReadU64(); - block.Flags = mdf.ReadU16(); - block.pathSeparator = mdf.ReadChar(); - block.Reserved1 = mdf.ReadU32(); - block.DataBytes = mdf.ReadU32(); - block.InvalidBytes = mdf.ReadU32(); + if (Channels[0] == block) //change first channel address on channelGroup + { + var nextBlock = block.Next; + var ptrFirst = 0ul; + + if (nextBlock != null) + ptrFirst = (ulong)block.Next.BlockAddress; + + var addressOfFirstChannel = BlockAddress + ptrFirstChannelBlockV4.offset; + var bytesFirstChannelAddress = BitConverter.GetBytes(ptrFirst); - if (block.ptrTextBlock != 0) - block.Comment = TextBlock.Read(mdf, block.ptrTextBlock); + for (int i = addressOfFirstChannel, j = 0; j < bytesFirstChannelAddress.Length; i++, j++) + bytes[i] = bytesFirstChannelAddress[j]; - if (block.ptrTextName != 0) - block.TextName = TextBlock.Read(mdf, block.ptrTextName); + } + + if (block.Previous == null && block.Next == null) + { + var ptrFirst = 0ul; + var addressOfFirstChannel = BlockAddress + ptrFirstChannelBlockV4.offset; + var bytesFirstChannelAddress = BitConverter.GetBytes(ptrFirst); - if (block.ptrFirstChannelBlock != 0) - block.Channels.Read(ChannelBlock.Read(mdf, block.ptrFirstChannelBlock)); + for (int i = addressOfFirstChannel, j = 0; j < bytesFirstChannelAddress.Length; i++, j++) + bytes[i] = bytesFirstChannelAddress[j]; + } } internal override ushort GetSize() @@ -186,5 +281,58 @@ internal void WriteNextChannelGroupBlockLink(byte[] array, int index, int blockI Array.Copy(bytesNextChannelGroupBlockLink, 0, array, blockIndex + 4, bytesNextChannelGroupBlockLink.Length); } + + internal void ChannelGroupUpdateAddress(int indexDeleted, List bytes, ulong countDeleted) + { + if (Mdf.IDBlock.Version >= 400) + ChannelGroupUpdateAddressV4(indexDeleted, bytes, countDeleted); + else + ChannelGroupUpdateAddressV23(indexDeleted, bytes, (uint)countDeleted); + + SourceInformation?.SourceInformationUpdateAddress(indexDeleted, bytes, countDeleted); + } + + private void ChannelGroupUpdateAddressV23(int indexDeleted, List bytes, uint countDeleted) + { + foreach (var ptr in listAddressesV23) + { + if ((int)ptr.address >= indexDeleted) + { + ptr.address -= countDeleted; + + this.CopyAddress(ptr, bytes, indexDeleted, countDeleted); + } + } + } + + private void ChannelGroupUpdateAddressV4(int indexDeleted, List bytes, ulong countDeleted) + { + foreach (var ptr in listAddressesV4) + { + if ((int)ptr.address >= indexDeleted) + { + ptr.address -= countDeleted; + + this.CopyAddress(ptr, bytes, indexDeleted, countDeleted); + } + } + } + + internal void ClearDataType(List bytes) + { + RecordSize = 0; + var addressRecordSize = BlockAddress + ptrTextBlock.offset + 4 + 2/*RecordID*/ + 2/*NumChannels*/; + var newbytes = BitConverter.GetBytes(RecordSize); + + for (int i = addressRecordSize, j = 0; j < newbytes.Length; i++, j++) + bytes[i] = newbytes[j]; + + NumRecords = 0; + var addressNumRecords = BlockAddress + ptrTextBlock.offset + 4 + 2/*RecordID*/ + 2/*NumChannels*/ + 2/*RecordSize*/; + newbytes = BitConverter.GetBytes(NumRecords); + + for (int i = addressNumRecords, j = 0; j < newbytes.Length; i++, j++) + bytes[i] = newbytes[j]; + } } } diff --git a/ASAM.MDF/Libary/ChannelGroupCollection.cs b/ASAM.MDF/Libary/ChannelGroupCollection.cs index 3825176..f239656 100644 --- a/ASAM.MDF/Libary/ChannelGroupCollection.cs +++ b/ASAM.MDF/Libary/ChannelGroupCollection.cs @@ -7,16 +7,16 @@ public class ChannelGroupCollection : IList { private List items = new List(); - public ChannelGroupCollection(Mdf mdf, DataGroupBlock dataGroupBlock) + public ChannelGroupCollection(Mdf mdf, DataGroupBlock parent) { if (mdf == null) throw new ArgumentNullException("mdf"); Mdf = mdf; - DataGroupBlock = dataGroupBlock; + Parent = parent; } - public DataGroupBlock DataGroupBlock { get; internal set; } + DataGroupBlock Parent { get; } public Mdf Mdf { get; private set; } public int Count { @@ -33,9 +33,13 @@ public ChannelGroupBlock this[int index] set { throw new NotImplementedException(); } } - internal void Read(ChannelGroupBlock channelGroupBlock) + internal void Read(ChannelGroupBlock channelGroupBlock, ChannelGroupBlock.ChannelGroupBlockHandler action) { - items = Common.BuildBlockList(null, channelGroupBlock); + items = Common.BuildBlockList(null, channelGroupBlock, Parent); + + if (action != null) + foreach (ChannelGroupBlock block in items) + block.ChannelGroupRemove += (cg, bytes) => action(cg, bytes); } internal void Write(byte[] array, ref int index) { diff --git a/ASAM.MDF/Libary/Common.cs b/ASAM.MDF/Libary/Common.cs index 0f086fd..2fedfb5 100644 --- a/ASAM.MDF/Libary/Common.cs +++ b/ASAM.MDF/Libary/Common.cs @@ -1,10 +1,12 @@ namespace ASAM.MDF.Libary { + using System; using System.Collections.Generic; + using System.Security; internal static class Common { - internal static List BuildBlockList(List list, T first) where T : INext + internal static List BuildBlockList(List list, T first, U parent) where T : INext, IPrevious, IParent { if (list == null) { @@ -12,8 +14,14 @@ internal static List BuildBlockList(List list, T first) where T : INext T current = first; while (current != null) { + current.Parent = parent; list.Add(current); + + var prevCurrent = current; current = current.Next; + + if (current != null) + current.Previous = prevCurrent; } } diff --git a/ASAM.MDF/Libary/DataBlock.cs b/ASAM.MDF/Libary/DataBlock.cs index 32047f3..1b10f77 100644 --- a/ASAM.MDF/Libary/DataBlock.cs +++ b/ASAM.MDF/Libary/DataBlock.cs @@ -1,4 +1,6 @@ -namespace ASAM.MDF.Libary +using System.Security.Cryptography; + +namespace ASAM.MDF.Libary { public class DataBlock : Block { @@ -7,16 +9,20 @@ public DataBlock(Mdf mdf) : base(mdf) public byte[] DataOfBlock { get; private set; } - public static DataBlock Read(Mdf mdf, ulong position) + public static DataBlock Read(Mdf mdf, int position) { mdf.UpdatePosition(position); var block = new DataBlock(mdf); - block.Read(); - - block.DataOfBlock = mdf.ReadBytes((int)block.Size - 24); + block.Read(); return block; } + internal override void ReadV4() + { + base.ReadV4(); + + DataOfBlock = Mdf.ReadBytes((int)Size - 24); + } } } \ No newline at end of file diff --git a/ASAM.MDF/Libary/DataGroupBlock.cs b/ASAM.MDF/Libary/DataGroupBlock.cs index 9b2dcaf..ed718b8 100644 --- a/ASAM.MDF/Libary/DataGroupBlock.cs +++ b/ASAM.MDF/Libary/DataGroupBlock.cs @@ -2,17 +2,22 @@ { using System; using System.Collections.Generic; - using System.IO; - public class DataGroupBlock : Block, INext + public class DataGroupBlock : Block, INext, IPrevious, IParent { - private DataGroupBlock nextBlock; - private ulong ptrNextDataGroup; - private ulong ptrFirstChannelGroupBlock; - private ulong ptrTriggerBlock; - private ulong ptrDataBlock; - private ulong ptrTextBlock; + internal PointerAddress ptrNextDataGroup; + internal PointerAddress ptrFirstChannelGroupBlock; + internal PointerAddress ptrTriggerBlock; + internal PointerAddress ptrDataBlock; + + internal PointerAddress ptrNextDataGroupV4; + internal PointerAddress ptrFirstChannelGroupBlockV4; + internal PointerAddress ptrTriggerBlockV4; + internal PointerAddress ptrDataBlockV4; + internal PointerAddress ptrTextBlockV4; + private DataRecord[] records; + private DataGroupBlock nextBlock; private DataGroupBlock(Mdf mdf) : base(mdf) { @@ -24,12 +29,18 @@ public DataGroupBlock Next { get { - if (nextBlock == null && ptrNextDataGroup != 0) - nextBlock = Read(Mdf, ptrNextDataGroup); + if (Mdf.IDBlock.Version >= 400) + { + if (nextBlock == null && ptrNextDataGroupV4 != null && ptrNextDataGroupV4?.address != 0) + nextBlock = Read(Mdf, (int)ptrNextDataGroupV4.address); + } + else if (nextBlock == null && ptrNextDataGroup != null && ptrNextDataGroup.address != 0) + nextBlock = Read(Mdf, (int)ptrNextDataGroup.address); return nextBlock; } } + public DataGroupBlock Previous { get; set; } public ChannelGroupCollection ChannelGroups { get; private set; } public DataListCollection DataListColl { get; private set; } public TriggerBlock Trigger { get; set; } @@ -55,6 +66,7 @@ public DataRecord[] Records public TextBlock FileComment { get; private set; } internal DataZippedBlock DataZipped { get; private set; } + public Mdf Parent { get; set; } //public uint Reserved { get; set; } @@ -65,88 +77,240 @@ public static DataGroupBlock Create(Mdf mdf) Identifier = "DG" }; } - public static DataGroupBlock Read(Mdf mdf, ulong position) + public static DataGroupBlock Read(Mdf mdf, int position) { mdf.UpdatePosition(position); var block = new DataGroupBlock(mdf); - block.Read(); - block.nextBlock = null; block.Trigger = null; block.Reserved = 0; - if (mdf.IDBlock.Version >= 400) - { - ReadV4(mdf, block); - return block; - } + block.Read(); + block.Records = block.ReadRecords(); + + return block; + } + internal override void ReadV23() + { + base.ReadV23(); - block.ptrNextDataGroup = mdf.ReadU32(); - block.ptrFirstChannelGroupBlock = mdf.ReadU32(); - block.ptrTriggerBlock = mdf.ReadU32(); - block.ptrDataBlock = mdf.ReadU32(); - block.NumChannelGroups = mdf.ReadU16(); - block.NumRecordIds = mdf.ReadU16(); + listAddressesV23 = new List>(); - if (block.Size >= 24) - block.Reserved = mdf.ReadU32(); + ptrNextDataGroup = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), 4); + ptrFirstChannelGroupBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrNextDataGroup.offset + 4); + ptrTriggerBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrFirstChannelGroupBlock.offset + 4); + ptrDataBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrTriggerBlock.offset + 4); + NumChannelGroups = Mdf.ReadU16(); + NumRecordIds = Mdf.ReadU16(); + if (Size >= 24) + Reserved = Mdf.ReadU32(); - if (block.ptrTextBlock != 0) - block.FileComment = TextBlock.Read(mdf, block.ptrTextBlock); + if (ptrFirstChannelGroupBlock.address != 0) + { + var CGBlock = ChannelGroupBlock.Read(Mdf, (int)ptrFirstChannelGroupBlock.address); + //CGBlock.ChannelGroupRemove += CGBlock_ChannelGroupRemove; + ChannelGroups.Read(CGBlock, CGBlock_ChannelGroupRemove); + } - if (block.ptrFirstChannelGroupBlock != 0) - block.ChannelGroups.Read(ChannelGroupBlock.Read(mdf, block.ptrFirstChannelGroupBlock)); + listAddressesV23.AddRange(new PointerAddress[] + { + ptrNextDataGroup, + ptrFirstChannelGroupBlock, + ptrTriggerBlock, + ptrDataBlock, + }); /// TODO: Call Trigger Blocks //if (m_ptrTriggerBlock != 0) //{ // Mdf.Data.Position = m_ptrTriggerBlock; - // Trigger = new TriggerBlock(mdf); + // Trigger = new TriggerBlock(Mdf); //} /// TODO: Call ProgramsBlock ? //if (ptrProgramBlock != 0) //{ // Mdf.Data.Position = ptrProgramBlock; - // ProgramBlock = new ProgramBlock(mdf); + // ProgramBlock = new ProgramBlock(Mdf); //} - - block.Records = block.ReadRecords(); - - return block; } - - private static void ReadV4(Mdf mdf, DataGroupBlock block) + internal override void ReadV4() { - block.ptrNextDataGroup = mdf.ReadU64(); - block.ptrFirstChannelGroupBlock = mdf.ReadU64(); - block.ptrDataBlock = mdf.ReadU64(); - block.ptrTextBlock = mdf.ReadU64(); - block.NumRecordIds = mdf.ReadByte(); - block.Reserved1 = mdf.ReadByte(); + base.ReadV4(); - if (block.ptrTextBlock != 0) - block.FileComment = TextBlock.Read(mdf, block.ptrTextBlock); + listAddressesV4 = new List>(); - if (block.ptrFirstChannelGroupBlock != 0) - block.ChannelGroups.Read(ChannelGroupBlock.Read(mdf, block.ptrFirstChannelGroupBlock)); + ptrNextDataGroupV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), 24); + ptrFirstChannelGroupBlockV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrNextDataGroupV4.offset + 8); + ptrDataBlockV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf),ptrFirstChannelGroupBlockV4.offset + 8); + ptrTextBlockV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrDataBlockV4.offset + 8); + NumRecordIds = Mdf.ReadByte(); + Reserved1 = Mdf.ReadByte(); - if (block.ptrDataBlock != 0) + if (ptrTextBlockV4.address != 0) + FileComment = TextBlock.Read(Mdf, (int)ptrTextBlockV4.address); + + if (ptrFirstChannelGroupBlockV4.address != 0) { - var indentificator = mdf.GetNameBlock(block.ptrDataBlock); + var CGBlock = ChannelGroupBlock.Read(Mdf, (int)ptrFirstChannelGroupBlockV4.address); + //CGBlock.ChannelGroupRemove += CGBlock_ChannelGroupRemove; + ChannelGroups.Read(CGBlock, CGBlock_ChannelGroupRemove); + } + + if (ptrDataBlockV4.address != 0) + { + var indentificator = Mdf.GetNameBlock((int)ptrDataBlockV4.address); if (indentificator == "DZ") - block.DataZipped = DataZippedBlock.Read(mdf, block.ptrDataBlock); + DataZipped = DataZippedBlock.Read(Mdf, (int)ptrDataBlockV4.address); if (indentificator == "DL") { - block.DataListColl.Read(DataList.Read(mdf, block.ptrDataBlock)); + DataListColl.Read(DataList.Read(Mdf, (int)ptrDataBlockV4.address)); } } - block.Records = block.ReadRecords(); + listAddressesV4.AddRange(new PointerAddress[] + { + ptrNextDataGroupV4, + ptrDataBlockV4, + ptrFirstChannelGroupBlockV4, + ptrTextBlockV4, + }); + } + + private void CGBlock_ChannelGroupRemove(ChannelGroupBlock block, List bytes) + { + + if (Mdf.IDBlock.Version >= 400) + { + RemoveChannelGroupsV4(block, bytes); + RemoveDataRecordsV4(block, bytes); + } + else + { + RemoveChannelGroups(block, bytes); + RemoveDataRecords(block, bytes); + } + } + + private void RemoveChannelGroups(ChannelGroupBlock block, List bytes) + { + if (ptrFirstChannelGroupBlock.address == block.BlockAddress && NumChannelGroups == 1) + { + var ptrFirst = 0u; + var thisPointer = BlockAddress; + if (Previous != null) + thisPointer = (int)Previous.ptrNextDataGroup.address; + + var ptrOffset = thisPointer + ptrFirstChannelGroupBlock.offset; + var newbytes = BitConverter.GetBytes(ptrFirst); + for (int i = ptrOffset, j = 0; j < newbytes.Length; i++, j++) + bytes[i] = newbytes[j]; + } + else if (ptrFirstChannelGroupBlock.address == block.BlockAddress) + { + var ptrFirst = (uint)block.Next.BlockAddress; + + var ptrOffset = BlockAddress + ptrFirstChannelGroupBlock.offset; + var newbytes = BitConverter.GetBytes(ptrFirst); + for (int i = ptrOffset, j = 0; j < newbytes.Length; i++, j++) + bytes[i] = newbytes[j]; + + } + } + + private void RemoveDataRecords(ChannelGroupBlock block, List bytes) + { + var byteOffset = 0; + for (int i = 0; i < NumChannelGroups; i++) + { + var cg = ChannelGroups[i]; + if (cg == block) + break; + + byteOffset += (int)cg.NumRecords * cg.RecordSize; + } + + if (NumChannelGroups > 0) + { + NumChannelGroups -= 1; + + var numChannelGroupsOffset = BlockAddress + ptrDataBlock.offset + 4; + var value = BitConverter.GetBytes(NumChannelGroups); + for (int i = numChannelGroupsOffset, j = 0; j < value.Length; i++, j++) + bytes[i] = value[j]; + } + + var countRecord = (int)block.NumRecords * block.RecordSize; + + block.ClearDataType(bytes); + + var indexStarted = (int)ptrDataBlock.address + byteOffset; + + bytes.RemoveRange(indexStarted, countRecord); + + Mdf.UpdateAddresses(bytes, (ulong)countRecord, indexStarted); + } + private void RemoveChannelGroupsV4(ChannelGroupBlock block, List bytes) + { + if (ptrFirstChannelGroupBlockV4.address == (ulong)block.BlockAddress && NumChannelGroups == 1) + { + var ptrFirst = 0u; + var thisPointer = BlockAddress; + if (Previous != null) + thisPointer = (int)Previous.ptrNextDataGroup.address; + + var ptrOffset = thisPointer + ptrFirstChannelGroupBlockV4.offset; + var newbytes = BitConverter.GetBytes(ptrFirst); + for (int i = ptrOffset, j = 0; j < newbytes.Length; i++, j++) + bytes[i] = newbytes[j]; + } + else if (ptrFirstChannelGroupBlockV4.address == (ulong)block.BlockAddress) + { + var ptrFirst = (uint)block.Next.BlockAddress; + + var ptrOffset = BlockAddress + ptrFirstChannelGroupBlockV4.offset; + var newbytes = BitConverter.GetBytes(ptrFirst); + for (int i = ptrOffset, j = 0; j < newbytes.Length; i++, j++) + bytes[i] = newbytes[j]; + + } + } + + private void RemoveDataRecordsV4(ChannelGroupBlock block, List bytes) + { + var byteOffset = 0; + for (int i = 0; i < NumChannelGroups; i++) + { + var cg = ChannelGroups[i]; + if (cg == block) + break; + + byteOffset += (int)cg.NumRecords * cg.RecordSize; + } + + if (NumChannelGroups > 0) + { + NumChannelGroups -= 1; + + var numChannelGroupsOffset = BlockAddress + ptrDataBlockV4.offset + 4; + var value = BitConverter.GetBytes(NumChannelGroups); + for (int i = numChannelGroupsOffset, j = 0; j < value.Length; i++, j++) + bytes[i] = value[j]; + } + + var countRecord = (int)block.NumRecords * block.RecordSize; + + block.ClearDataType(bytes); + + var indexStarted = (int)ptrDataBlockV4.address + byteOffset; + + bytes.RemoveRange(indexStarted, countRecord); + + Mdf.UpdateAddresses(bytes, (ulong)countRecord, indexStarted); } internal DataRecord[] ReadRecords() @@ -154,7 +318,10 @@ internal DataRecord[] ReadRecords() var recordsList = new List(); var dataList = new List(); - var indentificator = Mdf.GetNameBlock(ptrDataBlock); + var indentificator = Mdf.GetNameBlock((int)ptrDataBlock.address); + + if (Mdf.IDBlock.Version >= 400) + indentificator = Mdf.GetNameBlock((int)ptrDataBlockV4.address); if (indentificator == "DL") { @@ -164,7 +331,10 @@ internal DataRecord[] ReadRecords() } } - Mdf.UpdatePosition(ptrDataBlock); + Mdf.UpdatePosition((int)ptrDataBlock.address); + + if (Mdf.IDBlock.Version >= 400) + Mdf.UpdatePosition((int)ptrDataBlockV4.address); if (Mdf.IDBlock.Version >= 400) { @@ -205,13 +375,17 @@ internal DataRecord[] ReadRecords() for (int i = 0; i < NumChannelGroups; i++) { var group = ChannelGroups[i]; + var countData = 0; for (int k = 0; k < group.NumRecords; k++) { var recordData = Mdf.ReadBytes(group.RecordSize); recordsList.Add(new DataRecord(group, recordData)); + + countData += group.RecordSize; } + countData = 0; } } @@ -222,6 +396,7 @@ internal override ushort GetSize() { return 28; } + internal override int GetSizeTotal() { var size = base.GetSizeTotal(); @@ -255,6 +430,44 @@ internal override void Write(byte[] array, ref int index) index += GetSize(); } + internal override void Write(List array) + { + var newList = new List(); + + var bytesNextGroupBlock = BitConverter.GetBytes(ptrNextDataGroup.address); + newList.AddRange(bytesNextGroupBlock); + + WriteFirstChannelGroupBlockLink(newList); + WriteTriggerBlockLink(newList); + WriteDataBlockLink(newList); + + var bytesNumChannelGroups = BitConverter.GetBytes(ChannelGroups.Count); + var bytesNumRecordsIds = BitConverter.GetBytes(NumRecordIds); + var bytesReserved = BitConverter.GetBytes(Reserved); + + newList.AddRange(bytesNumChannelGroups); + newList.AddRange(bytesNumRecordsIds); + newList.AddRange(bytesReserved); + + base.Write(newList); + + array.AddRange(newList); + } + + internal void WriteDataBlockLink(List newList) + { + var bytesDataBlockLink = BitConverter.GetBytes(ptrDataBlock.address); + + newList.AddRange(bytesDataBlockLink); + } + + internal void WriteTriggerBlockLink(List newList) + { + var bytesTriggerBlockLink = BitConverter.GetBytes(ptrTriggerBlock.address); + + newList.AddRange(bytesTriggerBlockLink); + } + internal void WriteChannelGroups(byte[] array, ref int index) { ChannelGroups.Write(array, ref index); @@ -265,6 +478,12 @@ internal void WriteFirstChannelGroupBlockLink(byte[] array, int index, int block Array.Copy(bytesFirst, 0, array, blockIndex + 8, bytesFirst.Length); } + internal void WriteFirstChannelGroupBlockLink(List array) + { + var bytesFirst = BitConverter.GetBytes(ptrFirstChannelGroupBlock.address); + + array.AddRange(bytesFirst); + } internal void WriteNextBlockLink(byte[] array, int index, int blockIndex) { var bytesNextBlockLink = BitConverter.GetBytes(index); @@ -291,5 +510,39 @@ internal void WriteRecords(byte[] array, ref int index, int blockIndex) index += r.Data.Length; } } + + internal void DataGroupUpdateAddress(int indexDeleted, List bytes, ulong countDeleted) + { + if (Mdf.IDBlock.Version >= 400) + DataGroupUpdateAddressV4(indexDeleted, bytes, countDeleted); + else + DataGroupUpdateAddressV23(indexDeleted, bytes, (uint)countDeleted); + } + + private void DataGroupUpdateAddressV23(int indexDeleted, List bytes, uint countDeleted) + { + foreach (var ptr in listAddressesV23) + { + if ((int)ptr.address >= indexDeleted) + { + ptr.address -= countDeleted; + + this.CopyAddress(ptr, bytes, indexDeleted, countDeleted); + } + } + } + + private void DataGroupUpdateAddressV4(int indexDeleted, List bytes, ulong countDeleted) + { + foreach (var ptr in listAddressesV4) + { + if ((int)ptr.address >= indexDeleted) + { + ptr.address -= countDeleted; + + this.CopyAddress(ptr, bytes, indexDeleted, countDeleted); + } + } + } } } diff --git a/ASAM.MDF/Libary/DataGroupCollection.cs b/ASAM.MDF/Libary/DataGroupCollection.cs index 5d9e298..07a6d8b 100644 --- a/ASAM.MDF/Libary/DataGroupCollection.cs +++ b/ASAM.MDF/Libary/DataGroupCollection.cs @@ -27,7 +27,7 @@ public bool IsReadOnly internal void Read(DataGroupBlock block) { - items = Common.BuildBlockList(null, block); + items = Common.BuildBlockList(null, block, Mdf); } internal void Write(byte[] array, ref int index) { @@ -69,6 +69,9 @@ internal void Write(byte[] array, ref int index) block.WriteRecords(array, ref index, blockIndexes[i]); } } + internal void Write(List array) + { + } // IList. public int IndexOf(DataGroupBlock item) diff --git a/ASAM.MDF/Libary/DataList.cs b/ASAM.MDF/Libary/DataList.cs index 53cc039..575585c 100644 --- a/ASAM.MDF/Libary/DataList.cs +++ b/ASAM.MDF/Libary/DataList.cs @@ -1,10 +1,13 @@ -namespace ASAM.MDF.Libary +using System; +using System.Collections.Generic; +using System.Security.Cryptography; + +namespace ASAM.MDF.Libary { - public class DataList : Block, INext + public class DataList : Block, INext, IPrevious, IParent { - private ulong ptrNextDL; - - public ulong DataBlockAddress { get; private set; } + internal PointerAddress ptrNextDLV4; + internal PointerAddress ptrDataBlockAddressV4; private ulong ptrDataBlockLen; private DataList nextBlock; @@ -14,43 +17,76 @@ public class DataList : Block, INext public uint BlockCount { get; private set; } public long BlockOffset { get; private set; } public DataList(Mdf mdf) : base(mdf) - { - } + { } + public DataList Next { get { - if (nextBlock == null && ptrNextDL != 0) - nextBlock = Read(Mdf, ptrNextDL); + if (nextBlock == null && ptrNextDLV4.address != 0) + nextBlock = Read(Mdf, (int)ptrNextDLV4.address); return nextBlock; } } + public DataList Previous { get; set; } public DataBlock DataBlock { get; private set; } + public DataGroupBlock Parent { get; set; } - public static DataList Read(Mdf mdf, ulong position) + public static DataList Read(Mdf mdf, int position) { mdf.UpdatePosition(position); var block = new DataList(mdf); block.Read(); - block.ptrNextDL = mdf.ReadU64(); - block.DataBlockAddress = mdf.ReadU64(); - block.Flags = (ListFlags)mdf.ReadU16(); - block.Reserved1 = mdf.ReadU16(); - block.BlockCount = mdf.ReadU32(); + return block; + } + internal override void ReadV4() + { + base.ReadV4(); - if (block.Flags == ListFlags.EqualLength) - block.ptrDataBlockLen = mdf.ReadU64(); - else - block.BlockOffset = mdf.Read64(); + listAddressesV4 = new List>(); - if (block.DataBlockAddress != 0) - block.DataBlock = DataBlock.Read(mdf, block.DataBlockAddress); + ptrNextDLV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), 24); + ptrDataBlockAddressV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf),ptrNextDLV4.offset + 8); + Flags = (ListFlags)Mdf.ReadU16(); + Reserved1 = Mdf.ReadU16(); + BlockCount = Mdf.ReadU32(); - return block; + if (Flags == ListFlags.EqualLength) + ptrDataBlockLen = Mdf.ReadU64(); + else + BlockOffset = Mdf.Read64(); + + listAddressesV4.AddRange(new PointerAddress[] + { + ptrNextDLV4, + ptrDataBlockAddressV4, + }); + + if (ptrDataBlockAddressV4.address != 0) + DataBlock = DataBlock.Read(Mdf, (int)ptrDataBlockAddressV4.address); + } + + internal void DataListUpdateAddress(int indexDeleted, List bytes, ulong countDeleted) + { + if (Mdf.IDBlock.Version >= 400) + DataListUpdateAddressV4(indexDeleted, bytes, countDeleted); + } + + private void DataListUpdateAddressV4(int indexDeleted, List bytes, ulong countDeleted) + { + foreach (var ptr in listAddressesV4) + { + if ((int)ptr.address >= indexDeleted) + { + ptr.address -= countDeleted; + + this.CopyAddress(ptr, bytes, indexDeleted, countDeleted); + } + } } } } diff --git a/ASAM.MDF/Libary/DataListCollection.cs b/ASAM.MDF/Libary/DataListCollection.cs index 5110cfc..7177162 100644 --- a/ASAM.MDF/Libary/DataListCollection.cs +++ b/ASAM.MDF/Libary/DataListCollection.cs @@ -8,17 +8,17 @@ namespace ASAM.MDF.Libary public class DataListCollection : IList { private List items = new List(); - public DataListCollection(Mdf mdf, DataGroupBlock dataGroupBlock) + public DataListCollection(Mdf mdf, DataGroupBlock parent) { if (mdf == null) throw new ArgumentNullException("mdf"); Mdf = mdf; - DataGroupBlock = dataGroupBlock; + Parent = parent; } public Mdf Mdf { get; private set; } - public DataGroupBlock DataGroupBlock { get; internal set; } + DataGroupBlock Parent { get; } public int Count { get { return items.Count; } @@ -36,7 +36,7 @@ public DataList this[int index] internal void Read(DataList datalistBlock) { - items = Common.BuildBlockList(null, datalistBlock); + items = Common.BuildBlockList(null, datalistBlock, Parent); } diff --git a/ASAM.MDF/Libary/DataRecord.cs b/ASAM.MDF/Libary/DataRecord.cs index 41f2651..71823b1 100644 --- a/ASAM.MDF/Libary/DataRecord.cs +++ b/ASAM.MDF/Libary/DataRecord.cs @@ -108,7 +108,8 @@ public object GetValue(ChannelBlock channel) break; case SignalTypeV3.String: - value = ChannelGroup.Mdf.IDBlock.Encoding.GetString(Data, byteOffset, channel.NumberOfBits / 8); + var valueString = ChannelGroup.Mdf.IDBlock.Encoding.GetString(Data, byteOffset, channel.NumberOfBits / 8).Replace("\0", ""); + value = string.IsNullOrEmpty(valueString) ? null : valueString; break; } diff --git a/ASAM.MDF/Libary/DataZippedBlock.cs b/ASAM.MDF/Libary/DataZippedBlock.cs index a79a975..20abfef 100644 --- a/ASAM.MDF/Libary/DataZippedBlock.cs +++ b/ASAM.MDF/Libary/DataZippedBlock.cs @@ -8,7 +8,7 @@ namespace ASAM.MDF.Libary { internal class DataZippedBlock : Block { - private ulong indexStart; // start compressed data index + private int indexStart; // start compressed data index public DataZippedBlock(Mdf mdf) : base(mdf) { } @@ -21,7 +21,7 @@ public DataZippedBlock(Mdf mdf) : base(mdf) public byte ZHeader { get; private set; } public ZCompressionInfo CompressionInfo { get; private set; } - public static DataZippedBlock Read(Mdf mdf, ulong position) + public static DataZippedBlock Read(Mdf mdf, int position) { mdf.UpdatePosition(position); diff --git a/ASAM.MDF/Libary/Extensions.cs b/ASAM.MDF/Libary/Extensions.cs new file mode 100644 index 0000000..82568c4 --- /dev/null +++ b/ASAM.MDF/Libary/Extensions.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace ASAM.MDF.Libary +{ + internal static class Extensions + { + /// + /// Copy address data to byte massive + /// + /// + /// Block coping + /// pointer to change + /// byte massive for copy + internal static void CopyAddress(this T block, PointerAddress pointerData, List bytes, int index, uint count) where T : Block + { + var newbytes = BitConverter.GetBytes(pointerData.address); + + for (int i = block.BlockAddress + pointerData.offset, j = 0; j < newbytes.Length; i++, j++) + bytes[i] = newbytes[j]; + //Array.Copy(newbytes, 0, bytes, block.BlockAddress + pointerData.offset, newbytes.Length); + + } + internal static void CopyAddress(this T block, PointerAddress pointerData, List bytes, int index, ulong count) where T : Block + { + var newBlockAddress = block.BlockAddress; + if (newBlockAddress > index) + newBlockAddress -= (int)count; + + var newbytes = BitConverter.GetBytes(pointerData.address); + + for (int i = newBlockAddress + pointerData.offset, j = 0; j < newbytes.Length; i++, j++) + bytes[i] = newbytes[j]; + //Array.Copy(newbytes, 0, bytes, block.BlockAddress + pointerData.offset, newbytes.Length); + } + } +} diff --git a/ASAM.MDF/Libary/HeaderBlock.cs b/ASAM.MDF/Libary/HeaderBlock.cs index 2e89369..87bbe1e 100644 --- a/ASAM.MDF/Libary/HeaderBlock.cs +++ b/ASAM.MDF/Libary/HeaderBlock.cs @@ -1,10 +1,15 @@ namespace ASAM.MDF.Libary { using System; + using System.Collections.Generic; + using System.Linq; using ASAM.MDF.Libary.Types; public class HeaderBlock : Block { + internal List> listAddressesV23; + internal List> listAddressesV4; + private string author; private string date; private string organization; @@ -12,9 +17,13 @@ public class HeaderBlock : Block private string subject; private string time; private string timerIdentification; - private ulong ptrFirstDataGroup; - private ulong ptrTextBlock; - private uint ptrProgramBlock; + internal PointerAddress ptrFirstDataGroup; + internal PointerAddress ptrTextBlock; + internal PointerAddress ptrProgramBlock; + + internal PointerAddress ptrFirstDataGroupV4; + internal PointerAddress ptrTextBlockV4; + internal PointerAddress ptrProgramBlockV4; /// /// Initializes a new instance of the class. @@ -22,8 +31,7 @@ public class HeaderBlock : Block /// The MDF. /// private HeaderBlock(Mdf mdf) : base(mdf) - { - } + { } [MdfVersion(400, 0)] public ulong StartTimeNs { get; set; } @@ -216,63 +224,71 @@ internal static HeaderBlock Create(Mdf mdf) internal static HeaderBlock Read(Mdf mdf) { var block = new HeaderBlock(mdf); + block.Read(); - if (mdf.IDBlock.Version >= 400) - { - ReadV4(mdf, block); - return block; - } + return block; + } - block.ptrFirstDataGroup = mdf.ReadU32(); - block.ptrTextBlock = mdf.ReadU32(); - block.ptrProgramBlock = mdf.ReadU32(); + internal override void ReadV23() + { + base.ReadV23(); + + listAddressesV23 = new List>(); + + ptrFirstDataGroup = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), 4); + ptrTextBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrFirstDataGroup.offset+ 4); + ptrProgramBlock = new PointerAddress(Mdf.ReadU32().ValidateAddress(Mdf), ptrTextBlock.offset + 4); + + DataGroupsCount = Mdf.ReadU16(); - block.DataGroupsCount = mdf.ReadU16(); + Date = Mdf.GetString(10); + Time = Mdf.GetString(8); + Author = Mdf.GetString(32); + Organization = Mdf.GetString(32); + Project = Mdf.GetString(32); + Subject = Mdf.GetString(32); - block.Date = mdf.GetString(10); - block.Time = mdf.GetString(8); - block.Author = mdf.GetString(32); - block.Organization = mdf.GetString(32); - block.Project = mdf.GetString(32); - block.Subject = mdf.GetString(32); + listAddressesV23.AddRange(new PointerAddress[] + { + ptrFirstDataGroup, + ptrTextBlock, + ptrProgramBlock, + }); - if (mdf.IDBlock.Version == 320) + if (Mdf.IDBlock.Version == 320) { - block.TimeStamp = mdf.ReadU64(); - block.UTCTimeOffset = mdf.Read16(); - block.TimeQuality = (TimeQuality)mdf.ReadU16(); - block.TimerIdentification = mdf.GetString(32); + TimeStamp = Mdf.ReadU64(); + UTCTimeOffset = Mdf.Read16(); + TimeQuality = (TimeQuality)Mdf.ReadU16(); + TimerIdentification = Mdf.GetString(32); } else { - block.TimeStamp = 0; - block.UTCTimeOffset = 0; - block.TimeQuality = 0; - block.TimerIdentification = ""; + TimeStamp = 0; + UTCTimeOffset = 0; + TimeQuality = 0; + TimerIdentification = ""; } // Check if ptrTextBlock is null - if (block.ptrTextBlock != 0) + if (ptrTextBlock.address != 0) { - block.FileComment = TextBlock.Read(mdf, block.ptrTextBlock); + FileComment = TextBlock.Read(Mdf, (int)ptrTextBlock.address); } // Check if ptrProgramBlock is null - if (block.ptrProgramBlock != 0) + if (ptrProgramBlock.address != 0) { - block.ProgramBlock = ProgramBlock.Read(mdf, block.ptrProgramBlock); + ProgramBlock = ProgramBlock.Read(Mdf, (int)ptrProgramBlock.address); } // Check if ptrFirstDataGroup is null - if (block.ptrFirstDataGroup != 0) + if (ptrFirstDataGroup.address != 0) { - mdf.DataGroups.Read(DataGroupBlock.Read(mdf, block.ptrFirstDataGroup)); + Mdf.DataGroups.Read(DataGroupBlock.Read(Mdf, (int)ptrFirstDataGroup.address)); } - - return block; } - internal override ushort GetSize() { if (Mdf.IDBlock.Version >= 320) @@ -301,6 +317,15 @@ internal override void Write(byte[] array, ref int index) var bytesTimeQuality = BitConverter.GetBytes((ushort)TimeQuality); var bytesTimeIdentification = Mdf.IDBlock.Encoding.GetBytes(TimerIdentification); + ValidateEncodingLength(ref bytesDate, 10); + ValidateEncodingLength(ref bytesTime, 8); + ValidateEncodingLength(ref bytesAuthor, 32); + ValidateEncodingLength(ref bytesOrganization, 32); + ValidateEncodingLength(ref bytesProject, 32); + ValidateEncodingLength(ref bytesSubject, 32); + ValidateEncodingLength(ref bytesTimeIdentification, 32); + + Array.Copy(bytesDataGroupsCount, 0, array, index + 16, bytesDataGroupsCount.Length); Array.Copy(bytesDate, 0, array, index + 18, bytesDate.Length); Array.Copy(bytesTime, 0, array, index + 28, bytesTime.Length); @@ -321,6 +346,44 @@ internal override void Write(byte[] array, ref int index) index += 44; } } + internal override void Write(List array) + { + var newList = new List(); + + var bytesFirstDataGroup = BitConverter.GetBytes(ptrFirstDataGroup.address); + newList.AddRange(bytesFirstDataGroup); + + WriteFileComment(newList); + WriteProgramBlock(newList); + + var bytesDataGroupsCount = BitConverter.GetBytes(Mdf.DataGroups.Count); + var bytesDate = Mdf.IDBlock.Encoding.GetBytes(Date); + var bytesTime = Mdf.IDBlock.Encoding.GetBytes(Time); + var bytesAuthor = Mdf.IDBlock.Encoding.GetBytes(Author); + var bytesOrganization = Mdf.IDBlock.Encoding.GetBytes(Organization); + var bytesProject = Mdf.IDBlock.Encoding.GetBytes(Project); + var bytesSubject = Mdf.IDBlock.Encoding.GetBytes(Subject); + var bytesTimestamp = BitConverter.GetBytes(TimeStamp); + var bytesUtcTimeOffset = BitConverter.GetBytes(UTCTimeOffset); + var bytesTimeQuality = BitConverter.GetBytes((ushort)TimeQuality); + var bytesTimeIdentification = Mdf.IDBlock.Encoding.GetBytes(TimerIdentification); + + newList.AddRange(bytesDataGroupsCount); + newList.AddRange(bytesDate); + newList.AddRange(bytesTime); + newList.AddRange(bytesAuthor); + newList.AddRange(bytesOrganization); + newList.AddRange(bytesProject); + newList.AddRange(bytesSubject); + newList.AddRange(bytesTimestamp); + newList.AddRange(bytesUtcTimeOffset); + newList.AddRange(bytesTimeQuality); + newList.AddRange(bytesTimeIdentification); + + base.Write(newList); + + array.AddRange(newList); + } internal void WriteFirstDataGroupLink(byte[] array, int index, int baseIndex) { var bytesFirstDataGroupLink = BitConverter.GetBytes(index); @@ -337,6 +400,16 @@ internal void WriteFileComment(byte[] array, ref int index, int baseIndex) FileComment.Write(array, ref index); } + internal void WriteFileComment(List array) + { + if (FileComment == null) return; + + var bytesFileCommentLink = BitConverter.GetBytes(FileComment.BlockAddress); + + array.AddRange(bytesFileCommentLink); + + FileComment.Write(array); + } internal void WriteProgramBlock(byte[] array, ref int index, int baseIndex) { if (ProgramBlock == null) return; @@ -347,37 +420,89 @@ internal void WriteProgramBlock(byte[] array, ref int index, int baseIndex) ProgramBlock.Write(array, ref index); } - private static void ReadV4(Mdf mdf, HeaderBlock block) + internal void WriteProgramBlock(List array) { + if (ProgramBlock == null) return; + + var bytesProgramLink = BitConverter.GetBytes(ProgramBlock.BlockAddress); + + array.AddRange(bytesProgramLink); + + ProgramBlock.Write(array); + } + internal override void ReadV4() + { + base.ReadV4(); - block.ptrFirstDataGroup = mdf.ReadU64(); //Adress DataGroup + listAddressesV4 = new List>(); + + ptrFirstDataGroupV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), 24); //Adress DataGroup //skiped: FileHistoryBlock (not used) +8 //skiped: Chanel... (not used) +8 //skiped: AttachmentBlock (not used) +8 //skiped: EventBlock (not used) +8 - ulong skippedCount = 8 * 4; - mdf.UpdatePosition(mdf.position + skippedCount); - - block.ptrTextBlock = mdf.ReadU64(); - block.StartTimeNs = mdf.ReadU64(); - block.TimeZoneOffsetMinutes = mdf.Read16(); - block.DstOffsetMinutes = mdf.Read16(); - block.TimeFlags = mdf.ReadByte(); - block.TimeClass = mdf.ReadByte(); - block.Flags = mdf.ReadByte(); - block.Reserved1 = mdf.ReadByte(); - block.StartAngle = mdf.ReadDouble(); - block.StartDistance = mdf.ReadDouble(); - - if (block.ptrTextBlock != 0) + var skippedCount = 8 * 4; + Mdf.UpdatePosition(Mdf.position + skippedCount); + + ptrTextBlockV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), skippedCount + ptrFirstDataGroupV4.offset); + StartTimeNs = Mdf.ReadU64(); + TimeZoneOffsetMinutes = Mdf.Read16(); + DstOffsetMinutes = Mdf.Read16(); + TimeFlags = Mdf.ReadByte(); + TimeClass = Mdf.ReadByte(); + Flags = Mdf.ReadByte(); + Reserved1 = Mdf.ReadByte(); + StartAngle = Mdf.ReadDouble(); + StartDistance = Mdf.ReadDouble(); + + listAddressesV4.AddRange(new PointerAddress[] { - block.FileComment = TextBlock.Read(mdf, block.ptrTextBlock); + ptrFirstDataGroupV4, + ptrTextBlockV4, + }); + + if (ptrTextBlockV4.address != 0) + { + FileComment = TextBlock.Read(Mdf, (int)ptrTextBlockV4.address); } // Check if ptrFirstDataGroup is null - if (block.ptrFirstDataGroup != 0) + if (ptrFirstDataGroupV4.address != 0) + { + Mdf.DataGroups.Read(DataGroupBlock.Read(Mdf, (int)ptrFirstDataGroupV4.address)); + } + } + internal void HeaderUpdateAddress(int indexDeleted, List bytes, ulong countDeleted) + { + if (Mdf.IDBlock.Version >= 400) + HeaderUpdateAddressV4(indexDeleted, bytes, countDeleted); + else + HeaderUpdateAddressV23(indexDeleted, bytes, (uint)countDeleted); + } + + private void HeaderUpdateAddressV23(int indexDeleted, List bytes, uint countDeleted) + { + foreach (var ptr in listAddressesV23) + { + if ((int)ptr.address >= indexDeleted) + { + ptr.address -= countDeleted; + + this.CopyAddress(ptr, bytes, indexDeleted, countDeleted); + } + } + } + + private void HeaderUpdateAddressV4(int indexDeleted, List bytes, ulong countDeleted) + { + foreach (var ptr in listAddressesV4) { - mdf.DataGroups.Read(DataGroupBlock.Read(mdf, block.ptrFirstDataGroup)); + if ((int)ptr.address >= indexDeleted) + { + ptr.address -= countDeleted; + + this.CopyAddress(ptr, bytes, indexDeleted, countDeleted); + } } } } diff --git a/ASAM.MDF/Libary/INext.cs b/ASAM.MDF/Libary/INext.cs index 644d6a6..c296330 100644 --- a/ASAM.MDF/Libary/INext.cs +++ b/ASAM.MDF/Libary/INext.cs @@ -1,13 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace ASAM.MDF.Libary +namespace ASAM.MDF.Libary { - public interface INext - where T : INext + internal interface INext where T : INext { T Next { get; } } -} + internal interface IPrevious where T : IPrevious + { + T Previous { get; set; } + } + internal interface IParent + { + T Parent { get; set; } + } +} \ No newline at end of file diff --git a/ASAM.MDF/Libary/IdentificationBlock.cs b/ASAM.MDF/Libary/IdentificationBlock.cs index b6e832d..0f73bba 100644 --- a/ASAM.MDF/Libary/IdentificationBlock.cs +++ b/ASAM.MDF/Libary/IdentificationBlock.cs @@ -1,7 +1,9 @@ namespace ASAM.MDF.Libary { using System; + using System.Collections.Generic; using System.IO; + using System.Linq; using System.Text; using ASAM.MDF.Libary.Types; @@ -22,12 +24,12 @@ public class IdentificationBlock private string reserved2; private IdentificationBlock() - { - } + { } public Mdf Mdf { get; private set; } public Encoding Encoding { get; private set; } + public int Size { get; set; } /// /// The file identifier always contains "MDF". ("MDF" followed by five spaces) @@ -117,6 +119,8 @@ public ushort CodePage set { codePage = value; + if (value == 1251) + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); Encoding = value == 0 ? Encoding.ASCII : Encoding.GetEncoding(value); } } @@ -165,6 +169,7 @@ public static IdentificationBlock Create(Mdf mdf) public static IdentificationBlock Read(Mdf mdf) { var block = new IdentificationBlock(); + var prevPos = mdf.position; block.Mdf = mdf; block.fileIdentifier = GetString(mdf, 8).Humanize(); @@ -176,6 +181,7 @@ public static IdentificationBlock Read(Mdf mdf) block.CodePage = mdf.ReadU16(); block.reserved1 = GetString(mdf, 2).Humanize(); block.reserved2 = GetString(mdf, 30).Humanize(); + block.Size = mdf.position - prevPos; return block; } @@ -196,6 +202,12 @@ internal void Write(byte[] array, ref int index) var bytesReserved1 = Encoding.UTF8.GetBytes(Reserved1); var bytesReserved2 = Encoding.UTF8.GetBytes(Reserved2); + ValidateEncodingLength(ref bytesFileIdentifier, 8); + ValidateEncodingLength(ref bytesFormatIdentifier, 8); + ValidateEncodingLength(ref bytesProgramIdentifier, 8); + ValidateEncodingLength(ref bytesReserved1, 2); + ValidateEncodingLength(ref bytesReserved2, 30); + Array.Copy(bytesFileIdentifier, 0, array, index, bytesFileIdentifier.Length); Array.Copy(bytesFormatIdentifier, 0, array, index + 8, bytesFormatIdentifier.Length); Array.Copy(bytesProgramIdentifier, 0, array, index + 16, bytesProgramIdentifier.Length); @@ -209,6 +221,29 @@ internal void Write(byte[] array, ref int index) index += GetSize(); } + internal void Write(List array) + { + var bytesFileIdentifier = Encoding.UTF8.GetBytes(FileIdentifier); + var bytesFormatIdentifier = Encoding.UTF8.GetBytes(FormatIdentifier); + var bytesProgramIdentifier = Encoding.UTF8.GetBytes(ProgramIdentifier); + var bytesByteOrder = BitConverter.GetBytes((ushort)ByteOrder); + var bytesFloatingPointFormat = BitConverter.GetBytes((ushort)FloatingPointFormat); + var bytesVersion = BitConverter.GetBytes(Version); + var bytesCodePage = BitConverter.GetBytes(CodePage); + var bytesReserved1 = Encoding.UTF8.GetBytes(Reserved1); + var bytesReserved2 = Encoding.UTF8.GetBytes(Reserved2); + + array.AddRange(bytesFileIdentifier); + array.AddRange(bytesFormatIdentifier); + array.AddRange(bytesProgramIdentifier); + array.AddRange(bytesByteOrder); + array.AddRange(bytesFloatingPointFormat); + array.AddRange(bytesVersion); + array.AddRange(bytesCodePage); + array.AddRange(bytesReserved1); + array.AddRange(bytesReserved2); + } + /// /// Sets the string value. /// @@ -232,9 +267,14 @@ private void SetStringValue(ref string target, string value, int maxLength) target = value; } } - private static string GetString(Mdf mdf, ulong count) + private static string GetString(Mdf mdf, int count) + { + return Encoding.UTF8.GetString(mdf.Data, mdf.AdvanceIndex(count), count); + } + public void ValidateEncodingLength(ref byte[] bytes, int countConstraints) { - return Encoding.UTF8.GetString(mdf.Data, mdf.AdvanceIndex(count), (int)count); + if (bytes.Length > countConstraints) + bytes = bytes.Take(countConstraints).ToArray(); } } } diff --git a/ASAM.MDF/Libary/Mdf.cs b/ASAM.MDF/Libary/Mdf.cs index f3bf259..e5c47b7 100644 --- a/ASAM.MDF/Libary/Mdf.cs +++ b/ASAM.MDF/Libary/Mdf.cs @@ -1,12 +1,13 @@ namespace ASAM.MDF.Libary { using System; - using System.IO; + using System.Collections.Generic; + using System.Linq; public class Mdf { - internal ulong position; - internal byte[] data; + internal int position; //like stream position + internal byte[] data;// data bytes /// /// Read MDF from stream. @@ -34,6 +35,47 @@ public Mdf() internal byte[] Data => data; + /// + /// Removing channels from mdf + /// + /// An array of channels to delete + /// New file byte array + public byte[] RemoveChannel(ChannelBlock[] channelBlocks) + { + var array = CheckChannelTimes(channelBlocks); + var bytes = new List(Data); + var copiedMDF = new Mdf(bytes.ToArray()); + var BlockAddresses = array.Select(x => x.BlockAddress); + + for (int i = 0; i < copiedMDF.DataGroups.Count; i++) + { + var dataGroup = copiedMDF.DataGroups[i]; + + for (int j = 0; j < dataGroup.ChannelGroups.Count; j++) + { + var channelGroup = dataGroup.ChannelGroups[j]; + + for (int k = 0; k < channelGroup.Channels.Count; k++) + { + var channel = channelGroup.Channels[k]; + + if (BlockAddresses.Contains(channel.BlockAddress)) + bytes = channel.Remove(bytes); + } + } + } + + return bytes.ToArray(); + } + + private ChannelBlock[] CheckChannelTimes(ChannelBlock[] channelBlocks) + { + return channelBlocks.Where(x => x.TypeV3 != Types.ChannelTypeV3.Time).ToArray(); + } + /// + /// Converting an array from mdf to a bytes file. Not supported version 4 + /// + /// byte array public byte[] GetBytes() { var array = new byte[GetSize()]; @@ -53,10 +95,65 @@ public byte[] GetBytes() // DGBLOCKs. DataGroups.Write(array, ref index); - + return array; } + + /// + /// Updating the data addresses of all blocks after deletion + /// + /// new data bytes + /// count deleted bytes + /// Index start deleted on prevDataBytes + internal void UpdateAddresses(List data, ulong countDeleted, int indexDeleted) + { + var bytes = data; + if (countDeleted == 0) + return; + + HDBlock.HeaderUpdateAddress(indexDeleted, bytes, countDeleted); + + for (int i = 0; i < DataGroups.Count; i++) + { + var dataGroup = DataGroups[i]; + + if (dataGroup.BlockAddress > indexDeleted) + dataGroup.BlockAddress -= (int)countDeleted; + + dataGroup.DataGroupUpdateAddress(indexDeleted, bytes, countDeleted); + for (int j = 0; j < dataGroup.ChannelGroups.Count; j++) + { + var channelGroup = dataGroup.ChannelGroups[j]; + + if (channelGroup.BlockAddress > indexDeleted) + channelGroup.BlockAddress -= (int)countDeleted; + + channelGroup.ChannelGroupUpdateAddress(indexDeleted, bytes, countDeleted); + for (int k = 0; k < channelGroup.Channels.Count; k++) + { + var channel = channelGroup.Channels[k]; + + if (channel.BlockAddress > indexDeleted) + channel.BlockAddress -= (int)countDeleted; + + channel.ChannelUpdateAddress(indexDeleted, bytes, countDeleted); + } + } + for (int j = 0; j < dataGroup.DataListColl.Count; j++) + { + var dataList = dataGroup.DataListColl[j]; + + if (dataList.BlockAddress > indexDeleted) + dataList.BlockAddress -= (int)countDeleted; + + dataList.DataListUpdateAddress(indexDeleted, bytes, countDeleted); + } + } + } + /// + /// We get the maximum possible file size + /// internal int GetSize() { var size = 0; @@ -74,9 +171,9 @@ internal byte[] ReadBytes(int recordSize) { var value = new byte[recordSize]; - Array.Copy(data, (int)position, value, 0, value.Length); + Array.Copy(data, position, value, 0, value.Length); - position += (ulong)value.Length; + position += value.Length; return value; } @@ -90,10 +187,10 @@ internal byte[] ReadBytes(byte[] data, int recordSize, ref int position) return value; } - internal string GetNameBlock(ulong position) + internal string GetNameBlock(int position) { var index = position + 2; - var name = IDBlock.Encoding.GetString(Data, (int)index, 2); + var name = IDBlock.Encoding.GetString(Data, index, 2); return name; } } diff --git a/ASAM.MDF/Libary/PointerAddress.cs b/ASAM.MDF/Libary/PointerAddress.cs new file mode 100644 index 0000000..13cf686 --- /dev/null +++ b/ASAM.MDF/Libary/PointerAddress.cs @@ -0,0 +1,14 @@ +namespace ASAM.MDF.Libary +{ + internal class PointerAddress where T : struct + { + internal readonly int offset; + internal T address; + + internal PointerAddress(T ptr, int offset) + { + address = ptr; + this.offset = offset; + } + } +} diff --git a/ASAM.MDF/Libary/ProgramBlock.cs b/ASAM.MDF/Libary/ProgramBlock.cs index ee1c6c8..9484ad2 100644 --- a/ASAM.MDF/Libary/ProgramBlock.cs +++ b/ASAM.MDF/Libary/ProgramBlock.cs @@ -2,14 +2,14 @@ { using System; using System.Linq; + using System.Security.Cryptography; public class ProgramBlock : Block { private byte[] data; private ProgramBlock(Mdf mdf) : base(mdf) - { - } + { } public byte[] Data { @@ -44,21 +44,25 @@ public override string ToString() return "{PRBLOCK: Data[" + Data.Length + "]}"; } - internal static ProgramBlock Read(Mdf mdf, ulong position) + internal static ProgramBlock Read(Mdf mdf, int position) { mdf.UpdatePosition(position); var block = new ProgramBlock(mdf); - block.Read(); - - Array.Copy(mdf.Data, (int)mdf.position, block.Data, 0, (int)block.Size); + block.Read(); return block; } + internal override void ReadV23() + { + base.ReadV23(); + + Array.Copy(Mdf.Data, Mdf.position, Data, 0, (int)Size); + } internal override ushort GetSize() { - return (ushort)(4 + Data.Length); + return (ushort)((int)Size + Data.Length); } internal override void Write(byte[] array, ref int index) { diff --git a/ASAM.MDF/Libary/SourceInformation.cs b/ASAM.MDF/Libary/SourceInformation.cs index 68a99f6..ec0dae3 100644 --- a/ASAM.MDF/Libary/SourceInformation.cs +++ b/ASAM.MDF/Libary/SourceInformation.cs @@ -1,16 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; namespace ASAM.MDF.Libary { public class SourceInformation : Block { - private ulong ptrTextName; - private ulong ptrTextPath; - private ulong ptrTextComment; + internal PointerAddress ptrTextNameV4; + internal PointerAddress ptrTextPathV4; + internal PointerAddress ptrTextCommentV4; public byte SourceType { get; private set; } public byte BusType { get; private set; } @@ -23,31 +19,62 @@ public class SourceInformation : Block public SourceInformation(Mdf mdf) : base(mdf) { } - public static SourceInformation Read(Mdf mdf, ulong position) + public static SourceInformation Read(Mdf mdf, int position) { mdf.UpdatePosition(position); var block = new SourceInformation(mdf); + block.Read(); + return block; + } + internal override void ReadV4() + { + base.ReadV4(); - block.ptrTextName = mdf.ReadU64(); - block.ptrTextPath = mdf.ReadU64(); - block.ptrTextComment = mdf.ReadU64(); - block.SourceType = mdf.ReadByte(); - block.BusType = mdf.ReadByte(); - block.SiFlags = mdf.ReadByte(); - block.Reserved1 = mdf.ReadByte(); + listAddressesV4 = new List>(); - if (block.ptrTextComment != 0) - block.FileComment = TextBlock.Read(mdf, block.ptrTextComment); + ptrTextNameV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), 24); + ptrTextPathV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrTextNameV4.offset + 8); + ptrTextCommentV4 = new PointerAddress(Mdf.ReadU64().ValidateAddress(Mdf), ptrTextPathV4.offset + 8); + SourceType = Mdf.ReadByte(); + BusType = Mdf.ReadByte(); + SiFlags = Mdf.ReadByte(); + Reserved1 = Mdf.ReadByte(); - if (block.ptrTextName != 0) - block.TextBlockName = TextBlock.Read(mdf, block.ptrTextName); + listAddressesV4.AddRange(new PointerAddress[] + { + ptrTextNameV4, + ptrTextPathV4, + ptrTextCommentV4, + }); - if (block.ptrTextPath != 0) - block.TextBlockPath = TextBlock.Read(mdf, block.ptrTextPath); + if (ptrTextCommentV4.address != 0) + FileComment = TextBlock.Read(Mdf, (int)ptrTextCommentV4.address); - return block; + if (ptrTextNameV4.address != 0) + TextBlockName = TextBlock.Read(Mdf, (int)ptrTextNameV4.address); + + if (ptrTextPathV4.address != 0) + TextBlockPath = TextBlock.Read(Mdf, (int)ptrTextPathV4.address); + } + internal void SourceInformationUpdateAddress(int indexDeleted, List bytes, ulong countDeleted) + { + if (Mdf.IDBlock.Version >= 400) + SourceInformationUpdateAddressV4(indexDeleted, bytes, countDeleted); + } + + private void SourceInformationUpdateAddressV4(int indexDeleted, List bytes, ulong countDeleted) + { + foreach (var ptr in listAddressesV4) + { + if ((int)ptr.address >= indexDeleted) + { + ptr.address -= countDeleted; + + this.CopyAddress(ptr, bytes, indexDeleted, countDeleted); + } + } } } } diff --git a/ASAM.MDF/Libary/StreamReadHelper.cs b/ASAM.MDF/Libary/StreamReadHelper.cs index 4f1770d..6600b77 100644 --- a/ASAM.MDF/Libary/StreamReadHelper.cs +++ b/ASAM.MDF/Libary/StreamReadHelper.cs @@ -16,7 +16,7 @@ public static byte ReadByte(this Mdf mdf) public static ushort ReadU16(this Mdf mdf) { - var value = BitConverter.ToUInt16(mdf.data, (int)mdf.position); + var value = BitConverter.ToUInt16(mdf.data, mdf.position); mdf.position += 2; @@ -24,7 +24,7 @@ public static ushort ReadU16(this Mdf mdf) } public static uint ReadU32(this Mdf mdf) { - var value = BitConverter.ToUInt32(mdf.data, (int)mdf.position); + var value = BitConverter.ToUInt32(mdf.data, mdf.position); mdf.position += 4; @@ -32,7 +32,7 @@ public static uint ReadU32(this Mdf mdf) } public static ulong ReadU64(this Mdf mdf) { - var value = BitConverter.ToUInt64(mdf.data, (int)mdf.position); + var value = BitConverter.ToUInt64(mdf.data, mdf.position); mdf.position += 8; @@ -40,7 +40,7 @@ public static ulong ReadU64(this Mdf mdf) } public static short Read16(this Mdf mdf) { - var value = BitConverter.ToInt16(mdf.data, (int)mdf.position); + var value = BitConverter.ToInt16(mdf.data, mdf.position); mdf.position += 2; @@ -48,7 +48,7 @@ public static short Read16(this Mdf mdf) } public static int Read32(this Mdf mdf) { - var value = BitConverter.ToInt32(mdf.data, (int)mdf.position); + var value = BitConverter.ToInt32(mdf.data, mdf.position); mdf.position += 4; @@ -56,7 +56,7 @@ public static int Read32(this Mdf mdf) } public static long Read64(this Mdf mdf) { - var value = BitConverter.ToInt64(mdf.data, (int)mdf.position); + var value = BitConverter.ToInt64(mdf.data, mdf.position); mdf.position += 8; @@ -64,7 +64,7 @@ public static long Read64(this Mdf mdf) } public static char ReadChar(this Mdf mdf) { - var value = mdf.IDBlock.Encoding.GetString(mdf.data, (int)mdf.position, 2); + var value = mdf.IDBlock.Encoding.GetString(mdf.data, mdf.position, 2); mdf.position += 2; @@ -74,15 +74,15 @@ public static char ReadChar(this Mdf mdf) public static bool ReadBoolean(this Mdf mdf) { - var value = BitConverter.ToUInt16(mdf.Data, (int)mdf.position); + var value = BitConverter.ToBoolean(mdf.Data, mdf.position); mdf.position += 2; - return value != 0; + return value; } public static double ReadDouble(this Mdf mdf) { - var value = BitConverter.ToDouble(mdf.Data, (int)mdf.position); + var value = BitConverter.ToDouble(mdf.Data, mdf.position); mdf.position += 8; @@ -90,27 +90,64 @@ public static double ReadDouble(this Mdf mdf) } - public static void UpdatePosition(this Mdf mdf, ulong address) + public static void UpdatePosition(this Mdf mdf, int address) { mdf.position = address; } - public static int AdvanceIndex(this Mdf mdf, ulong count) + public static int AdvanceIndex(this Mdf mdf, int count) { var index = mdf.position; mdf.position += count; - return (int)index; + return index; } - public static string GetString(this Mdf mdf, ulong count) + public static string GetString(this Mdf mdf, int count) { - var value = mdf.IDBlock.Encoding.GetString(mdf.Data, mdf.AdvanceIndex(count), (int)count); + var value = mdf.IDBlock.Encoding.GetString(mdf.Data, mdf.AdvanceIndex(count), count); var indexLastSymbol = value.IndexOf('\0'); if (indexLastSymbol != -1) value = value.Remove(indexLastSymbol); return value; } + + public static ushort ValidateAddress(this ushort value, Mdf mdf) + { + if (value >= mdf.Data.Length) + return 0; + return value; + } + public static uint ValidateAddress(this uint value, Mdf mdf) + { + if (value >= mdf.Data.Length) + return 0; + return value; + } + public static ulong ValidateAddress(this ulong value, Mdf mdf) + { + if (value >= (ulong)mdf.Data.Length) + return 0; + return value; + } + public static short ValidateAddress(this short value, Mdf mdf) + { + if (value >= mdf.Data.Length) + return 0; + return value; + } + public static int ValidateAddress(this int value, Mdf mdf) + { + if (value >= mdf.Data.Length) + return 0; + return value; + } + public static long ValidateAddress(this long value, Mdf mdf) + { + if (value >= mdf.Data.Length) + return 0; + return value; + } } } diff --git a/ASAM.MDF/Libary/TextBlock.cs b/ASAM.MDF/Libary/TextBlock.cs index 5baa71a..ce75a53 100644 --- a/ASAM.MDF/Libary/TextBlock.cs +++ b/ASAM.MDF/Libary/TextBlock.cs @@ -1,7 +1,10 @@ namespace ASAM.MDF.Libary { using System; + using System.Collections.Generic; + using System.Drawing; using System.IO; + using System.Linq; using System.Text; public class TextBlock : Block @@ -51,21 +54,27 @@ public override string ToString() return "{TXBLOCK: " + Text + "}"; } - internal static TextBlock Read(Mdf mdf, ulong position) + internal static TextBlock Read(Mdf mdf, int position) { + if (mdf.Data.Length < position) + return null; + mdf.UpdatePosition(position); var block = new TextBlock(mdf); block.Read(); - block.Text = mdf.GetString(block.Size - mdf.position + block.BlockAddress); + if (block.Size > (ulong)mdf.data.Length || block.Size <= 4) + return null; + + block.Text = mdf.GetString((int)block.Size - (mdf.position - block.BlockAddress)); return block; } internal override ushort GetSize() { - return (ushort)(4 + Text.Length); + return (ushort)(4 + Mdf.IDBlock.Encoding.GetBytes(Text).Length); } internal override void Write(byte[] array, ref int index) { @@ -77,5 +86,17 @@ internal override void Write(byte[] array, ref int index) index += GetSize(); } + internal override void Write(List array) + { + var newList = new List(); + + var bytesText = Mdf.IDBlock.Encoding.GetBytes(Text); + + newList.AddRange(bytesText); + + base.Write(newList); + + array.AddRange(newList); + } } } diff --git a/DebugOpenFileMdf/DebugOpenFileMdf.csproj b/DebugOpenFileMdf/DebugOpenFileMdf.csproj new file mode 100644 index 0000000..3d7adec --- /dev/null +++ b/DebugOpenFileMdf/DebugOpenFileMdf.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0-windows + enable + enable + + + + + + + diff --git a/DebugOpenFileMdf/Program.cs b/DebugOpenFileMdf/Program.cs new file mode 100644 index 0000000..78fc418 --- /dev/null +++ b/DebugOpenFileMdf/Program.cs @@ -0,0 +1,61 @@ +using ASAM.MDF.Libary; +using System.Runtime.InteropServices; +using System.Threading.Channels; + +var filename = ShowDialog(); +try +{ + var bytes = File.ReadAllBytes(filename); + var mdf = new Mdf(bytes); +} +catch (Exception e) +{ + Console.WriteLine(e.Message); + Console.WriteLine(); + throw; +} + +static string ShowDialog() +{ + var ofn = new OpenFileName(); + ofn.lStructSize = Marshal.SizeOf(ofn); + ofn.lpstrFile = new string(new char[256]); + ofn.nMaxFile = ofn.lpstrFile.Length; + ofn.lpstrFileTitle = new string(new char[64]); + ofn.nMaxFileTitle = ofn.lpstrFileTitle.Length; + ofn.lpstrTitle = "Open File Dialog..."; + if (GetOpenFileName(ref ofn)) + return ofn.lpstrFile; + return string.Empty; +} + +[DllImport("comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)] +static extern bool GetOpenFileName(ref OpenFileName ofn); + +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] +public struct OpenFileName +{ + public int lStructSize; + public IntPtr hwndOwner; + public IntPtr hInstance; + public string lpstrFilter; + public string lpstrCustomFilter; + public int nMaxCustFilter; + public int nFilterIndex; + public string lpstrFile; + public int nMaxFile; + public string lpstrFileTitle; + public int nMaxFileTitle; + public string lpstrInitialDir; + public string lpstrTitle; + public int Flags; + public short nFileOffset; + public short nFileExtension; + public string lpstrDefExt; + public IntPtr lCustData; + public IntPtr lpfnHook; + public string lpTemplateName; + public IntPtr pvReserved; + public int dwReserved; + public int flagsEx; +} \ No newline at end of file