/*
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,
};
}
}
}