/* 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.Diagnostics; using System.Text; namespace dnSpy.Contracts.Hex.Files.DotNet { /// /// Multi file resource element info /// public readonly struct MultiResourceInfo { /// /// Gets the resource name /// public string Name { get; } /// /// Gets the type code /// public ResourceTypeCode TypeCode { get; } /// /// Gets the user type name if is >= /// public string UserTypeName { get; } /// /// Constructor /// /// Name of resource /// Type code /// User type or null if it's not a public MultiResourceInfo(string name, ResourceTypeCode typeCode, string? userTypeName) { Name = name ?? throw new ArgumentNullException(nameof(name)); TypeCode = typeCode; UserTypeName = userTypeName ?? string.Empty; } } /// /// Multi-file resource data header base class /// public abstract class MultiResourceDataHeaderData : StructureData { const string NAME = "MultiResourceDataHeader"; /// /// Gets the owner instance /// public DotNetMultiFileResources ResourceProvider { get; } /// /// Gets resource info /// public MultiResourceInfo ResourceInfo { get; } /// Type code public abstract StructField TypeCode { get; } /// /// Constructor /// /// Owner /// Resource info /// Span protected MultiResourceDataHeaderData(DotNetMultiFileResources resourceProvider, MultiResourceInfo resourceInfo, HexBufferSpan span) : base(NAME, span) { ResourceProvider = resourceProvider ?? throw new ArgumentNullException(nameof(resourceProvider)); ResourceInfo = resourceInfo; } } /// /// Multi-file resource data header (everything that's not a string, byte array, stream) /// public class MultiResourceSimplDataHeaderData : MultiResourceDataHeaderData { /// Type code public override StructField TypeCode { get; } /// Content public StructField Content { get; } /// /// Gets the fields /// protected override BufferField[] Fields { get; } /// /// Constructor /// /// Owner /// Resource info /// Span /// Position of data which immediately follows the 7-bit encoded type code public MultiResourceSimplDataHeaderData(DotNetMultiFileResources resourceProvider, MultiResourceInfo resourceInfo, HexBufferSpan span, HexPosition dataPosition) : base(resourceProvider, resourceInfo, span) { // Don't use Contains() since data length could be 0 if (dataPosition < span.Start || dataPosition > span.End) throw new ArgumentOutOfRangeException(nameof(dataPosition)); var typeCodeSpan = new HexBufferSpan(span.Buffer, HexSpan.FromBounds(span.Start, dataPosition)); TypeCode = new StructField("TypeCode", ResourceTypeCodeData.Create(typeCodeSpan)); var pos = typeCodeSpan.Span.Start; var typeCode = (ResourceTypeCode)(Utils.Read7BitEncodedInt32(span.Buffer, ref pos) ?? -1); var dataSpan = new HexBufferSpan(span.Buffer, HexSpan.FromBounds(dataPosition, span.End)); switch (typeCode) { case ResourceTypeCode.String: Debug.Fail($"Use {nameof(MultiResourceStringDataHeaderData)}"); goto default; case ResourceTypeCode.ByteArray: case ResourceTypeCode.Stream: Debug.Fail($"Use {nameof(MultiResourceArrayDataHeaderData)}"); goto default; case ResourceTypeCode.Boolean: Content = new StructField("Content", new BooleanData(dataSpan)); break; case ResourceTypeCode.Char: Content = new StructField("Content", new CharData(dataSpan)); break; case ResourceTypeCode.Byte: Content = new StructField("Content", new ByteData(dataSpan)); break; case ResourceTypeCode.SByte: Content = new StructField("Content", new SByteData(dataSpan)); break; case ResourceTypeCode.Int16: Content = new StructField("Content", new Int16Data(dataSpan)); break; case ResourceTypeCode.UInt16: Content = new StructField("Content", new UInt16Data(dataSpan)); break; case ResourceTypeCode.Int32: Content = new StructField("Content", new Int32Data(dataSpan)); break; case ResourceTypeCode.UInt32: Content = new StructField("Content", new UInt32Data(dataSpan)); break; case ResourceTypeCode.Int64: Content = new StructField("Content", new Int64Data(dataSpan)); break; case ResourceTypeCode.UInt64: Content = new StructField("Content", new UInt64Data(dataSpan)); break; case ResourceTypeCode.Single: Content = new StructField("Content", new SingleData(dataSpan)); break; case ResourceTypeCode.Double: Content = new StructField("Content", new DoubleData(dataSpan)); break; case ResourceTypeCode.Decimal: Content = new StructField("Content", new DecimalData(dataSpan)); break; case ResourceTypeCode.DateTime: Content = new StructField("Content", new DateTimeData(dataSpan)); break; case ResourceTypeCode.TimeSpan: Content = new StructField("Content", new TimeSpanData(dataSpan)); break; case ResourceTypeCode.Null: default: Content = new StructField>("Content", ArrayData.CreateVirtualByteArray(dataSpan)); break; } Fields = new BufferField[] { TypeCode, Content, }; } } /// /// Multi-file resource data header (strings) /// public class MultiResourceStringDataHeaderData : MultiResourceDataHeaderData { /// Type code public override StructField TypeCode { get; } /// Content public StructField Content { get; } /// /// Gets the fields /// protected override BufferField[] Fields { get; } /// /// Constructor /// /// Owner /// Resource info /// Span /// Span of 7-bit encoded string length /// Span of string data (UTF-8) public MultiResourceStringDataHeaderData(DotNetMultiFileResources resourceProvider, MultiResourceInfo resourceInfo, HexBufferSpan span, HexSpan lengthSpan, HexSpan stringSpan) : base(resourceProvider, resourceInfo, span) { if (!span.Span.Contains(lengthSpan)) throw new ArgumentOutOfRangeException(nameof(lengthSpan)); if (!span.Span.Contains(stringSpan)) throw new ArgumentOutOfRangeException(nameof(stringSpan)); if (lengthSpan.End != stringSpan.Start) throw new ArgumentOutOfRangeException(nameof(stringSpan)); var typeCodeSpan = new HexBufferSpan(span.Buffer, HexSpan.FromBounds(span.Start, lengthSpan.Start)); TypeCode = new StructField("TypeCode", ResourceTypeCodeData.Create(typeCodeSpan)); Content = new StructField("Content", new Bit7EncodedStringData(span.Buffer, lengthSpan, stringSpan, Encoding.UTF8)); Fields = new BufferField[] { TypeCode, Content, }; } } /// /// Multi-file resource data header (byte arrays and streams) /// public class MultiResourceArrayDataHeaderData : MultiResourceDataHeaderData { /// Type code public override StructField TypeCode { get; } /// Content length public StructField ContentLength { get; } /// Content public StructField> Content { get; } /// /// Gets the fields /// protected override BufferField[] Fields { get; } /// /// Constructor /// /// Owner /// Resource info /// Span /// Position of 32-bit content length which immediately follows the 7-bit encoded type code public MultiResourceArrayDataHeaderData(DotNetMultiFileResources resourceProvider, MultiResourceInfo resourceInfo, HexBufferSpan span, HexPosition lengthPosition) : base(resourceProvider, resourceInfo, span) { var typeCodeSpan = new HexBufferSpan(span.Buffer, HexSpan.FromBounds(span.Start, lengthPosition)); TypeCode = new StructField("TypeCode", ResourceTypeCodeData.Create(typeCodeSpan)); ContentLength = new StructField("ContentLength", new UInt32Data(span.Buffer, lengthPosition)); var arraySpan = new HexBufferSpan(span.Buffer, HexSpan.FromBounds(lengthPosition + 4, span.End)); Content = new StructField>("Content", ArrayData.CreateVirtualByteArray(arraySpan)); Fields = new BufferField[] { TypeCode, ContentLength, Content, }; } } }