/* Copyright (C) 2014-2019 de4dot@gmail.com This file is part of dnSpy dnSpy is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. dnSpy is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with dnSpy. If not, see . */ using System; using System.Collections.ObjectModel; using System.Text; namespace dnSpy.Contracts.Hex.Files.DotNet { /// /// #GUID heap record data /// public sealed class GuidHeapRecordData : GuidData { /// /// Gets the heap /// public GUIDHeap Heap { get; } /// /// Gets the GUID index (1-based) /// public uint Index { get; } /// /// Constructor /// /// Buffer /// Position /// Owner heap /// Guid index (1-based) public GuidHeapRecordData(HexBuffer buffer, HexPosition position, GUIDHeap heap, uint index) : base(buffer, position) { if (index == 0) throw new ArgumentOutOfRangeException(nameof(index)); Heap = heap ?? throw new ArgumentNullException(nameof(heap)); Index = index; } } /// /// #Strings heap record data /// public sealed class StringsHeapRecordData : StructureData { const string NAME = "StringZ"; /// /// Gets the owner heap /// public StringsHeap Heap { get; } /// /// Gets tokens of records referencing this string /// public ReadOnlyCollection Tokens { get; } /// /// Gets the string /// public StructField String { get; } /// /// Gets the terminator or null if there's none /// public StructField? Terminator { get; } /// /// Gets the fields /// protected override BufferField[] Fields { get; } /// /// Constructor /// /// Buffer /// Span of string, not including the terminating zero /// true if there's a terminating zero, false if there's no terminating zero /// or if the string is too long /// Owner heap /// Tokens of records referencing this string public StringsHeapRecordData(HexBuffer buffer, HexSpan stringSpan, bool hasTerminatingZero, StringsHeap heap, uint[] tokens) : base(NAME, new HexBufferSpan(buffer, HexSpan.FromBounds(stringSpan.Start, stringSpan.End + (hasTerminatingZero ? 1 : 0)))) { if (tokens is null) throw new ArgumentNullException(nameof(tokens)); Heap = heap ?? throw new ArgumentNullException(nameof(heap)); Tokens = new ReadOnlyCollection(tokens); String = new StructField("String", new StringData(new HexBufferSpan(buffer, stringSpan), Encoding.UTF8)); if (hasTerminatingZero) Terminator = new StructField("Terminator", new ByteData(buffer, stringSpan.End)); if (Terminator is not null) { Fields = new BufferField[] { String, Terminator, }; } else { Fields = new BufferField[] { String, }; } } } /// /// #US heap record data /// public sealed class USHeapRecordData : StructureData { const string NAME = "US"; /// /// Gets the owner heap /// public USHeap Heap { get; } /// /// Gets the length /// public StructField Length { get; } /// /// Gets the string data /// public StructField String { get; } /// /// Gets the terminal byte or null if none exists /// public StructField? TerminalByte { get; } /// /// Gets the fields /// protected override BufferField[] Fields { get; } /// /// Constructor /// /// Buffer /// Span of length /// Span of string data /// Span of terminal byte (0 or 1 byte) /// Owner heap public USHeapRecordData(HexBuffer buffer, HexSpan lengthSpan, HexSpan stringSpan, HexSpan terminalByteSpan, USHeap heap) : base(NAME, new HexBufferSpan(buffer, HexSpan.FromBounds(lengthSpan.Start, terminalByteSpan.End))) { if (lengthSpan.End != stringSpan.Start) throw new ArgumentOutOfRangeException(nameof(stringSpan)); if (stringSpan.End != terminalByteSpan.Start) throw new ArgumentOutOfRangeException(nameof(stringSpan)); if (!terminalByteSpan.IsEmpty && terminalByteSpan.Length != 1) throw new ArgumentOutOfRangeException(nameof(terminalByteSpan)); Heap = heap ?? throw new ArgumentNullException(nameof(heap)); Length = new StructField("Length", new BlobEncodedUInt32Data(new HexBufferSpan(buffer, lengthSpan))); String = new StructField("String", new StringData(new HexBufferSpan(buffer, stringSpan), Encoding.Unicode)); if (!terminalByteSpan.IsEmpty) TerminalByte = new StructField("TerminalByte", new ByteData(new HexBufferSpan(buffer, terminalByteSpan))); if (TerminalByte is not null) { Fields = new BufferField[] { Length, String, TerminalByte, }; } else { Fields = new BufferField[] { Length, String, }; } } } /// /// #Blob heap record data /// public sealed class BlobHeapRecordData : StructureData { const string NAME = "Blob"; /// /// Gets the owner heap /// public BlobHeap Heap { get; } /// /// Gets the tokens referencing the blob or an empty collection if none (eg. referenced from data in the #Blob) /// public ReadOnlyCollection Tokens { get; } /// /// Gets the length /// public StructField Length { get; } /// /// Gets the data /// public StructField Data { get; } /// /// Gets the fields /// protected override BufferField[] Fields { get; } /// /// Constructor /// /// Buffer /// Span /// Span of length /// Data /// Tokens referencing this blob or an empty collection /// Owner heap public BlobHeapRecordData(HexBuffer buffer, HexSpan span, HexSpan lengthSpan, BufferData data, ReadOnlyCollection tokens, BlobHeap heap) : base(NAME, new HexBufferSpan(buffer, span)) { if (lengthSpan.Start != span.Start) throw new ArgumentOutOfRangeException(nameof(lengthSpan)); if (data is null) throw new ArgumentNullException(nameof(data)); Heap = heap ?? throw new ArgumentNullException(nameof(heap)); Tokens = tokens ?? throw new ArgumentNullException(nameof(tokens)); Length = new StructField("Length", new BlobEncodedUInt32Data(new HexBufferSpan(buffer, lengthSpan))); Data = new StructField("Data", data); Fields = new BufferField[] { Length, Data, }; } } /// /// #Strings heap reference /// public abstract class StringsHeapData : SimpleData { /// /// Constructor /// /// Data span protected StringsHeapData(HexBufferSpan span) : base(span) { } /// /// Reads the rid /// /// protected abstract uint ReadOffset(); /// /// Returns the span the field value references or null. The span can be empty. /// /// File /// public override HexSpan? GetFieldReferenceSpan(HexBufferFile file) { var stringsStream = file.GetHeaders()?.StringsStream; if (stringsStream is null) return null; uint offset = ReadOffset(); if (offset >= stringsStream.Span.Length) return null; var pos = stringsStream.Span.Span.Start + offset; int len = GetStringLength(pos, stringsStream.Span.Span.End); return new HexSpan(pos, (ulong)len); } int GetStringLength(HexPosition position, HexPosition heapEnd) { var buffer = Span.Buffer; const int MAX_LEN = 0x400; var end = HexPosition.Min(heapEnd, position + MAX_LEN); var start = position; while (position < end) { if (buffer.ReadByte(position++) == 0) break; } return (int)(position - start).ToUInt64(); } } /// /// 16-bit #Strings heap reference /// public class StringsHeapData16 : StringsHeapData { /// /// Constructor /// /// Data span public StringsHeapData16(HexBufferSpan span) : base(span) { if (span.Length != 2) throw new ArgumentOutOfRangeException(nameof(span)); } /// /// Constructor /// /// Buffer /// Position public StringsHeapData16(HexBuffer buffer, HexPosition position) : this(new HexBufferSpan(buffer, new HexSpan(position, 2))) { } /// /// Writes the value /// /// Formatter public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt16((ushort)ReadOffset()); /// /// Reads the token value /// /// protected override uint ReadOffset() => Span.Buffer.ReadUInt16(Span.Start); } /// /// 32-bit #Strings heap reference /// public class StringsHeapData32 : StringsHeapData { /// /// Constructor /// /// Data span public StringsHeapData32(HexBufferSpan span) : base(span) { if (span.Length != 4) throw new ArgumentOutOfRangeException(nameof(span)); } /// /// Constructor /// /// Buffer /// Position public StringsHeapData32(HexBuffer buffer, HexPosition position) : this(new HexBufferSpan(buffer, new HexSpan(position, 4))) { } /// /// Writes the value /// /// Formatter public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt32(ReadOffset()); /// /// Reads the token value /// /// protected override uint ReadOffset() => Span.Buffer.ReadUInt32(Span.Start); } /// /// #Blob heap reference /// public abstract class BlobHeapData : SimpleData { /// /// Constructor /// /// Data span protected BlobHeapData(HexBufferSpan span) : base(span) { } /// /// Reads the rid /// /// protected abstract uint ReadOffset(); /// /// Returns the span the field value references or null. The span can be empty. /// /// File /// public override HexSpan? GetFieldReferenceSpan(HexBufferFile file) { var blobStream = file.GetHeaders()?.BlobStream; if (blobStream is null) return null; uint offset = ReadOffset(); if (offset >= blobStream.Span.Length) return null; var start = blobStream.Span.Span.Start + offset; var pos = start; int size = (int)Utils.ReadCompressedUInt32(Span.Buffer, ref pos); ulong blobSize = pos + size > blobStream.Span.Span.End ? 0 : (ulong)size + (pos - start).ToUInt64(); return new HexSpan(start, blobSize); } } /// /// 16-bit #Blob heap reference /// public class BlobHeapData16 : BlobHeapData { /// /// Constructor /// /// Data span public BlobHeapData16(HexBufferSpan span) : base(span) { if (span.Length != 2) throw new ArgumentOutOfRangeException(nameof(span)); } /// /// Constructor /// /// Buffer /// Position public BlobHeapData16(HexBuffer buffer, HexPosition position) : this(new HexBufferSpan(buffer, new HexSpan(position, 2))) { } /// /// Writes the value /// /// Formatter public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt16((ushort)ReadOffset()); /// /// Reads the token value /// /// protected override uint ReadOffset() => Span.Buffer.ReadUInt16(Span.Start); } /// /// 32-bit #Blob heap reference /// public class BlobHeapData32 : BlobHeapData { /// /// Constructor /// /// Data span public BlobHeapData32(HexBufferSpan span) : base(span) { if (span.Length != 4) throw new ArgumentOutOfRangeException(nameof(span)); } /// /// Constructor /// /// Buffer /// Position public BlobHeapData32(HexBuffer buffer, HexPosition position) : this(new HexBufferSpan(buffer, new HexSpan(position, 4))) { } /// /// Writes the value /// /// Formatter public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt32(ReadOffset()); /// /// Reads the token value /// /// protected override uint ReadOffset() => Span.Buffer.ReadUInt32(Span.Start); } /// /// #GUID heap reference /// public abstract class GUIDHeapData : SimpleData { /// /// Constructor /// /// Data span protected GUIDHeapData(HexBufferSpan span) : base(span) { } /// /// Reads the rid /// /// protected abstract uint ReadIndex(); /// /// Returns the span the field value references or null. The span can be empty. /// /// File /// public override HexSpan? GetFieldReferenceSpan(HexBufferFile file) { var guidStream = file.GetHeaders()?.GUIDStream; if (guidStream is null) return null; uint index = ReadIndex(); if (index == 0 || !guidStream.IsValidIndex(index)) return null; return new HexSpan(guidStream.Span.Span.Start + (index - 1) * 16, 16); } } /// /// 16-bit #GUID heap reference /// public class GUIDHeapData16 : GUIDHeapData { /// /// Constructor /// /// Data span public GUIDHeapData16(HexBufferSpan span) : base(span) { if (span.Length != 2) throw new ArgumentOutOfRangeException(nameof(span)); } /// /// Constructor /// /// Buffer /// Position public GUIDHeapData16(HexBuffer buffer, HexPosition position) : this(new HexBufferSpan(buffer, new HexSpan(position, 2))) { } /// /// Writes the value /// /// Formatter public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt16((ushort)ReadIndex()); /// /// Reads the token value /// /// protected override uint ReadIndex() => Span.Buffer.ReadUInt16(Span.Start); } /// /// 32-bit #GUID heap reference /// public class GUIDHeapData32 : GUIDHeapData { /// /// Constructor /// /// Data span public GUIDHeapData32(HexBufferSpan span) : base(span) { if (span.Length != 4) throw new ArgumentOutOfRangeException(nameof(span)); } /// /// Constructor /// /// Buffer /// Position public GUIDHeapData32(HexBuffer buffer, HexPosition position) : this(new HexBufferSpan(buffer, new HexSpan(position, 4))) { } /// /// Writes the value /// /// Formatter public override void WriteValue(HexFieldFormatter formatter) => formatter.WriteUInt32(ReadIndex()); /// /// Reads the token value /// /// protected override uint ReadIndex() => Span.Buffer.ReadUInt32(Span.Start); } }