diff --git a/DnSpyCommon.props b/DnSpyCommon.props
index 6b7a8c6dba..ddc935493e 100644
--- a/DnSpyCommon.props
+++ b/DnSpyCommon.props
@@ -43,7 +43,7 @@
1.7.0
- 3.6.0
+ 4.1.0
1.20.0
17.1.0
1.1.142101
diff --git a/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs b/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs
index 8dffff5754..430cd24bb6 100644
--- a/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs
+++ b/Extensions/dnSpy.AsmEditor/Resources/ResourceCommands.cs
@@ -766,7 +766,7 @@ static void Execute(Lazy 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);
@@ -2038,7 +2038,8 @@ static void Execute(Lazy 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) {
diff --git a/Extensions/dnSpy.AsmEditor/Resources/ResourceElementVM.cs b/Extensions/dnSpy.AsmEditor/Resources/ResourceElementVM.cs
index 1c916de69e..8f9fd38118 100644
--- a/Extensions/dnSpy.AsmEditor/Resources/ResourceElementVM.cs
+++ b/Extensions/dnSpy.AsmEditor/Resources/ResourceElementVM.cs
@@ -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());
case ResourceElementType.Stream: return new BuiltInResourceData((ResourceTypeCode)code, Data ?? Array.Empty());
- 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();
}
}
diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializationUtilities.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializationUtilities.cs
index a7e7269c51..e9dfbae570 100644
--- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializationUtilities.cs
+++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializationUtilities.cs
@@ -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;
diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageListStreamerUtilities.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageListStreamerUtilities.cs
index 020d38944e..d7641656d6 100644
--- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageListStreamerUtilities.cs
+++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageListStreamerUtilities.cs
@@ -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),
};
}
diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs
index f9bcf38ee9..a7ef3939c8 100644
--- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs
+++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedImageUtilities.cs
@@ -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;
@@ -36,34 +38,84 @@ public static class SerializedImageUtilities {
/// Serialized data
/// Updated with the image data
///
- 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);
+
+ ///
+ /// Gets the image data
+ ///
+ /// Module
+ /// Name of type
+ /// Serialized data
+ /// Format of serialized data
+ /// Updated with the image data
+ ///
+ 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);
@@ -99,7 +151,15 @@ public static bool CheckType(ModuleDef? module, string name, TypeRef expectedTyp
///
/// Resource element
///
- public static ResourceElement Serialize(ResourceElement resElem) {
+ public static ResourceElement Serialize(ResourceElement resElem) => Serialize(resElem, SerializationFormat.BinaryFormatter);
+
+ ///
+ /// Serializes the image
+ ///
+ /// Resource element
+ /// Serialization format to use
+ ///
+ public static ResourceElement Serialize(ResourceElement resElem, SerializationFormat format) {
var data = (byte[])((BuiltInResourceData)resElem.ResourceData).Data;
bool isIcon = BitConverter.ToUInt32(data, 0) == 0x00010000;
@@ -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),
};
}
}
diff --git a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedResourceElementNode.cs b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedResourceElementNode.cs
index 8560f692fb..50acb6a645 100644
--- a/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedResourceElementNode.cs
+++ b/dnSpy/dnSpy.Contracts.DnSpy/Documents/TreeView/Resources/SerializedResourceElementNode.cs
@@ -17,10 +17,13 @@ You should have received a copy of the GNU General Public License
along with dnSpy. If not, see .
*/
+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;
@@ -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;
diff --git a/dnSpy/dnSpy.Decompiler/MSBuild/ResXResourceFileWriter.cs b/dnSpy/dnSpy.Decompiler/MSBuild/ResXResourceFileWriter.cs
index 717cc87765..94fd9187e3 100644
--- a/dnSpy/dnSpy.Decompiler/MSBuild/ResXResourceFileWriter.cs
+++ b/dnSpy/dnSpy.Decompiler/MSBuild/ResXResourceFileWriter.cs
@@ -1,4 +1,4 @@
-/*
+/*
Copyright (C) 2023 ElektroKill
This file is part of dnSpy
@@ -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();
}
diff --git a/dnSpy/dnSpy/Documents/TreeView/Resources/ResourceElementSetNodeImpl.cs b/dnSpy/dnSpy/Documents/TreeView/Resources/ResourceElementSetNodeImpl.cs
index 0db9a99c2c..0fe368892a 100644
--- a/dnSpy/dnSpy/Documents/TreeView/Resources/ResourceElementSetNodeImpl.cs
+++ b/dnSpy/dnSpy/Documents/TreeView/Resources/ResourceElementSetNodeImpl.cs
@@ -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)
diff --git a/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs b/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs
index d17a7ef783..045ad294e8 100644
--- a/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs
+++ b/dnSpy/dnSpy/Documents/TreeView/Resources/SerializedImageResourceElementNode.cs
@@ -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;
@@ -94,7 +94,7 @@ protected override IEnumerable 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 {
@@ -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);
}
diff --git a/dnSpy/dnSpy/app.config b/dnSpy/dnSpy/app.config
index cdb732c52e..16f54a9c01 100644
--- a/dnSpy/dnSpy/app.config
+++ b/dnSpy/dnSpy/app.config
@@ -45,7 +45,7 @@
-
+