Skip to content

Commit

Permalink
Merge pull request #240 from dnSpyEx/feature/dnlib410
Browse files Browse the repository at this point in the history
  • Loading branch information
ElektroKill authored Sep 6, 2023
2 parents fdd8899 + 03b586d commit ea566e3
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 38 deletions.
2 changes: 1 addition & 1 deletion DnSpyCommon.props
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

<!-- Update app.config whenever some of these versions change (eg. dnlib version) -->
<DiaSymReaderVersion>1.7.0</DiaSymReaderVersion>
<DnlibVersion>3.6.0</DnlibVersion>
<DnlibVersion>4.1.0</DnlibVersion>
<IcedVersion>1.20.0</IcedVersion>
<MSBuildNuGetVersion>17.1.0</MSBuildNuGetVersion>
<MSDiagRuntimeVersion>1.1.142101</MSDiagRuntimeVersion>
Expand Down
5 changes: 3 additions & 2 deletions Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService ap
return;

var outStream = new MemoryStream();
ResourceWriter.Write(module, outStream, new ResourceElementSet());
ResourceWriter.Write(module, outStream, ResourceElementSet.CreateForResourceReader(module));
var er = new EmbeddedResource(data.Name, outStream.ToArray(), data.Attributes);
var treeView = appService.DocumentTreeView.TreeView;
var treeNodeGroup = appService.DocumentTreeView.DocumentTreeNodeGroups.GetGroup(DocumentTreeNodeGroupType.ResourceTreeNodeGroup);
Expand Down Expand Up @@ -2038,7 +2038,8 @@ static void Execute(Lazy<IUndoCommandService> undoCommandService, IAppService ap
var opts = data.CreateResourceElementOptions();
string? error;
try {
opts = new ResourceElementOptions(SerializedImageUtilities.Serialize(opts.Create()));
var format = ((BinaryResourceData)imgRsrcElNode.ResourceElement.ResourceData).Format;
opts = new ResourceElementOptions(SerializedImageUtilities.Serialize(opts.Create(), format));
error = imgRsrcElNode.CheckCanUpdateData(opts.Create());
}
catch (Exception ex) {
Expand Down
2 changes: 1 addition & 1 deletion Extensions/dnSpy.AsmEditor/Resources/ResourceElementVM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ IResourceData CreateResourceData() {
case ResourceElementType.TimeSpan: return new BuiltInResourceData((ResourceTypeCode)code, TimeSpanVM.Value);
case ResourceElementType.ByteArray: return new BuiltInResourceData((ResourceTypeCode)code, Data ?? Array.Empty<byte>());
case ResourceElementType.Stream: return new BuiltInResourceData((ResourceTypeCode)code, Data ?? Array.Empty<byte>());
case ResourceElementType.SerializedType: return new BinaryResourceData(new UserResourceType(UserTypeVM.TypeFullName, ResourceTypeCode.UserTypes), UserTypeVM.GetSerializedData());
case ResourceElementType.SerializedType: return new BinaryResourceData(new UserResourceType(UserTypeVM.TypeFullName, ResourceTypeCode.UserTypes), UserTypeVM.GetSerializedData(), SerializationFormat.BinaryFormatter);
default: throw new InvalidOperationException();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ static ResourceElement CreateSerializedImage(Stream stream, string filename) {
var userType = new UserResourceType(typeName, ResourceTypeCode.UserTypes);
var rsrcElem = new ResourceElement {
Name = Path.GetFileName(filename),
ResourceData = new BinaryResourceData(userType, serializedData),
ResourceData = new BinaryResourceData(userType, serializedData, SerializationFormat.BinaryFormatter),
};

return rsrcElem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public static ResourceElement Serialize(ImageListOptions opts) {
var typeName = SystemWindowsFormsImageListStreamer.AssemblyQualifiedName;
return new ResourceElement {
Name = opts.Name,
ResourceData = new BinaryResourceData(new UserResourceType(typeName, ResourceTypeCode.UserTypes), SerializationUtilities.Serialize(obj)),
ResourceData = new BinaryResourceData(new UserResourceType(typeName, ResourceTypeCode.UserTypes), SerializationUtilities.Serialize(obj), SerializationFormat.BinaryFormatter),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ You should have received a copy of the GNU General Public License
*/

using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using dnlib.DotNet;
using dnlib.DotNet.Resources;

Expand All @@ -36,34 +38,84 @@ public static class SerializedImageUtilities {
/// <param name="serializedData">Serialized data</param>
/// <param name="imageData">Updated with the image data</param>
/// <returns></returns>
public static bool GetImageData(ModuleDef? module, string typeName, byte[] serializedData, [NotNullWhen(true)] out byte[]? imageData) {
public static bool GetImageData(ModuleDef? module, string typeName, byte[] serializedData, [NotNullWhen(true)] out byte[]? imageData) =>
GetImageData(module, typeName, serializedData, SerializationFormat.BinaryFormatter, out imageData);

/// <summary>
/// Gets the image data
/// </summary>
/// <param name="module">Module</param>
/// <param name="typeName">Name of type</param>
/// <param name="serializedData">Serialized data</param>
/// <param name="format">Format of serialized data</param>
/// <param name="imageData">Updated with the image data</param>
/// <returns></returns>
public static bool GetImageData(ModuleDef? module, string typeName, byte[] serializedData, SerializationFormat format, [NotNullWhen(true)] out byte[]? imageData) {
imageData = null;

if (CouldBeBitmap(module, typeName)) {
var dict = Deserializer.Deserialize(SystemDrawingBitmap.DefinitionAssembly.FullName, SystemDrawingBitmap.ReflectionFullName, serializedData);
// Bitmap loops over every item looking for "Data" (case insensitive)
foreach (var v in dict.Values) {
var d = v.Value as byte[];
if (d is null)
continue;
if ("Data".Equals(v.Name, StringComparison.OrdinalIgnoreCase)) {
imageData = d;
return true;
if (format == SerializationFormat.BinaryFormatter) {
var dict = Deserializer.Deserialize(SystemDrawingBitmap.DefinitionAssembly.FullName, SystemDrawingBitmap.ReflectionFullName, serializedData);
// Bitmap loops over every item looking for "Data" (case insensitive)
foreach (var v in dict.Values) {
var d = v.Value as byte[];
if (d is null)
continue;
if ("Data".Equals(v.Name, StringComparison.OrdinalIgnoreCase)) {
imageData = d;
return true;
}
}
return false;
}
if (format == SerializationFormat.ActivatorStream) {
imageData = serializedData;
return true;
}
if (format == SerializationFormat.TypeConverterByteArray) {
imageData = GetBitmapData(serializedData) ?? serializedData;
return true;
}
return false;
}

if (CouldBeIcon(module, typeName)) {
var dict = Deserializer.Deserialize(SystemDrawingIcon.DefinitionAssembly.FullName, SystemDrawingIcon.ReflectionFullName, serializedData);
if (!dict.TryGetValue("IconData", out var info))
return false;
imageData = info.Value as byte[];
return imageData is not null;
if (format == SerializationFormat.BinaryFormatter) {
var dict = Deserializer.Deserialize(SystemDrawingIcon.DefinitionAssembly.FullName, SystemDrawingIcon.ReflectionFullName, serializedData);
if (!dict.TryGetValue("IconData", out var info))
return false;
imageData = info.Value as byte[];
return imageData is not null;
}
if (format == SerializationFormat.ActivatorStream || format == SerializationFormat.TypeConverterByteArray) {
imageData = serializedData;
return true;
}
}

return false;
}

static byte[]? GetBitmapData(byte[] rawData) {
// Based on ImageConverter.GetBitmapStream
// See https://github.com/dotnet/winforms/blob/main/src/System.Drawing.Common/src/System/Drawing/ImageConverter.cs
if (rawData.Length <= 18)
return null;

short sig = (short)(rawData[0] | rawData[1] << 8);
if (sig != 0x1C15)
return null;

short headerSize = (short)(rawData[2] | rawData[3] << 8);
if (rawData.Length <= headerSize + 18)
return null;
if (Encoding.ASCII.GetString(rawData, headerSize + 12, 6) != "PBrush")
return null;

var newData = new byte[rawData.Length - 78];
Buffer.BlockCopy(rawData, 78, newData, 0, newData.Length);
return newData;
}

static bool CouldBeBitmap(ModuleDef? module, string name) => CheckType(module, name, SystemDrawingBitmap);
static bool CouldBeIcon(ModuleDef? module, string name) => CheckType(module, name, SystemDrawingIcon);

Expand Down Expand Up @@ -99,7 +151,15 @@ public static bool CheckType(ModuleDef? module, string name, TypeRef expectedTyp
/// </summary>
/// <param name="resElem">Resource element</param>
/// <returns></returns>
public static ResourceElement Serialize(ResourceElement resElem) {
public static ResourceElement Serialize(ResourceElement resElem) => Serialize(resElem, SerializationFormat.BinaryFormatter);

/// <summary>
/// Serializes the image
/// </summary>
/// <param name="resElem">Resource element</param>
/// <param name="format">Serialization format to use</param>
/// <returns></returns>
public static ResourceElement Serialize(ResourceElement resElem, SerializationFormat format) {
var data = (byte[])((BuiltInResourceData)resElem.ResourceData).Data;
bool isIcon = BitConverter.ToUInt32(data, 0) == 0x00010000;

Expand All @@ -114,9 +174,32 @@ public static ResourceElement Serialize(ResourceElement resElem) {
typeName = SystemDrawingBitmap.AssemblyQualifiedName;
}

byte[] serializedData;
if (format == SerializationFormat.BinaryFormatter) {
serializedData = SerializationUtilities.Serialize(obj);
}
else if (format == SerializationFormat.TypeConverterByteArray) {
var converter = TypeDescriptor.GetConverter(obj.GetType());
var byteArr = converter.ConvertTo(obj, typeof(byte[]));
if (byteArr is not byte[] d)
throw new InvalidOperationException("Failed to serialize image");
serializedData = d;
}
else if (format == SerializationFormat.ActivatorStream) {
using (var stream = new MemoryStream()) {
if (obj is System.Drawing.Bitmap bitmap)
bitmap.Save(stream, bitmap.RawFormat);
else
((System.Drawing.Icon)obj).Save(stream);
serializedData = stream.ToArray();
}
}
else
throw new ArgumentOutOfRangeException(nameof(format));

return new ResourceElement {
Name = resElem.Name,
ResourceData = new BinaryResourceData(new UserResourceType(typeName, ResourceTypeCode.UserTypes), SerializationUtilities.Serialize(obj)),
ResourceData = new BinaryResourceData(new UserResourceType(typeName, ResourceTypeCode.UserTypes), serializedData, format),
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ You should have received a copy of the GNU General Public License
along with dnSpy. If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading;
using dnlib.DotNet.Resources;
using dnSpy.Contracts.Images;
Expand Down Expand Up @@ -91,16 +94,59 @@ public void Deserialize() {
if (!CanDeserialize)
return;

var serializedData = ((BinaryResourceData)ResourceElement.ResourceData).Data;
var formatter = new BinaryFormatter();
try {
var binaryResourceData = ((BinaryResourceData)ResourceElement.ResourceData);
var serializedData = binaryResourceData.Data;
if (binaryResourceData.Format == SerializationFormat.BinaryFormatter) {
var formatter = new BinaryFormatter();
try {
#pragma warning disable SYSLIB0011
deserializedData = formatter.Deserialize(new MemoryStream(serializedData));
deserializedData = formatter.Deserialize(new MemoryStream(serializedData));
#pragma warning restore SYSLIB0011
}
catch {
return;
}
}
catch {
return;
else if (binaryResourceData.Format == SerializationFormat.TypeConverterByteArray) {
try {
var type = Type.GetType(binaryResourceData.TypeName);
if (type is null)
return;
var converter = TypeDescriptor.GetConverter(type);
if (converter is null)
return;
deserializedData = converter.ConvertFrom(serializedData);
}
catch {
return;
}
}
else if (binaryResourceData.Format == SerializationFormat.TypeConverterString) {
try {
var type = Type.GetType(binaryResourceData.TypeName);
if (type is null)
return;
var converter = TypeDescriptor.GetConverter(type);
if (converter is null)
return;
deserializedData = converter.ConvertFromInvariantString(Encoding.UTF8.GetString(serializedData));
}
catch {
return;
}
}
else if (binaryResourceData.Format == SerializationFormat.ActivatorStream) {
try {
var type = Type.GetType(binaryResourceData.TypeName);
if (type is null)
return;
deserializedData = Activator.CreateInstance(type, new MemoryStream(serializedData));
}
catch {
return;
}
}

if (deserializedData is null)
return;

Expand Down
17 changes: 14 additions & 3 deletions dnSpy/dnSpy.Decompiler/MSBuild/ResXResourceFileWriter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
Copyright (C) 2023 ElektroKill
This file is part of dnSpy
Expand Down Expand Up @@ -182,8 +182,19 @@ ResXResourceInfo GetNodeInfo(IResourceData resourceData) {
throw new ArgumentOutOfRangeException();
}
}
if (resourceData is BinaryResourceData binaryResourceData)
return new ResXResourceInfo(ToBase64WrappedString(binaryResourceData.Data), binaryResourceData.TypeName, ResXResourceWriter.BinSerializedObjectMimeType);
if (resourceData is BinaryResourceData binaryResourceData) {
switch (binaryResourceData.Format) {
case SerializationFormat.BinaryFormatter:
return new ResXResourceInfo(ToBase64WrappedString(binaryResourceData.Data), binaryResourceData.TypeName, ResXResourceWriter.BinSerializedObjectMimeType);
case SerializationFormat.TypeConverterByteArray:
case SerializationFormat.ActivatorStream:
// RESX does not have a way to represent creation of an object using Activator.CreateInstance,
// so we fallback to the same representation as data passed into TypeConverter.
return new ResXResourceInfo(ToBase64WrappedString(binaryResourceData.Data), binaryResourceData.TypeName, ResXResourceWriter.ByteArraySerializedObjectMimeType);
case SerializationFormat.TypeConverterString:
return new ResXResourceInfo(Encoding.UTF8.GetString(binaryResourceData.Data), binaryResourceData.TypeName);
}
}

throw new ArgumentOutOfRangeException();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public override void RegenerateEmbeddedResource() {
void RegenerateEmbeddedResource(ModuleDef module) {
TreeNode.EnsureChildrenLoaded();
var outStream = new MemoryStream();
var resources = new ResourceElementSet();
var resources = resourceElementSet.Clone();
foreach (DocumentTreeNodeData child in TreeNode.DataChildren) {
var resourceElement = ResourceElementNode.GetResourceElement(child);
if (resourceElement is null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ sealed class SerializedImageResourceElementNodeProvider : IResourceNodeProvider
if (serializedData is null)
return null;

if (SerializedImageUtilities.GetImageData(module, serializedData.TypeName, serializedData.Data, out var imageData))
if (SerializedImageUtilities.GetImageData(module, serializedData.TypeName, serializedData.Data, serializedData.Format, out var imageData))
return new SerializedImageResourceElementNodeImpl(treeNodeGroup, resourceElement, imageData);

return null;
Expand Down Expand Up @@ -94,7 +94,7 @@ protected override IEnumerable<ResourceData> GetDeserializedData() {
return res;

var binData = (BinaryResourceData)newResElem.ResourceData;
if (!SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, out var imageData))
if (!SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, binData.Format, out var imageData))
return dnSpy_Resources.NewDataIsNotAnImage;

try {
Expand All @@ -111,7 +111,7 @@ public override void UpdateData(ResourceElement newResElem) {
base.UpdateData(newResElem);

var binData = (BinaryResourceData)newResElem.ResourceData;
SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, out var imageData);
SerializedImageUtilities.GetImageData(this.GetModule(), binData.TypeName, binData.Data, binData.Format, out var imageData);
Debug2.Assert(imageData is not null);
InitializeImageData(imageData);
}
Expand Down
2 changes: 1 addition & 1 deletion dnSpy/dnSpy/app.config
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@

<dependentAssembly>
<assemblyIdentity name="dnlib" publicKeyToken="50e96378b6e77999" culture="neutral"/>
<bindingRedirect oldVersion="3.0.0.0-3.6.0.0" newVersion="3.6.0.0"/>
<bindingRedirect oldVersion="3.0.0.0-4.1.0.0" newVersion="4.1.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="iced" publicKeyToken="5baba79f4264913b" culture="neutral"/>
Expand Down

0 comments on commit ea566e3

Please sign in to comment.