Skip to content

Commit

Permalink
Add support for deserializing images stored in newer formats
Browse files Browse the repository at this point in the history
  • Loading branch information
ElektroKill committed Sep 3, 2023
1 parent ebd6935 commit 4be7b3e
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ You should have received a copy of the GNU General Public License
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Text;
using dnlib.DotNet;
using dnlib.DotNet.Resources;

Expand All @@ -36,34 +37,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
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

0 comments on commit 4be7b3e

Please sign in to comment.