diff --git a/SabreTools.Serialization/Extensions/PortableExecutable.cs b/SabreTools.Serialization/Extensions/PortableExecutable.cs index 74fb1768..456885cb 100644 --- a/SabreTools.Serialization/Extensions/PortableExecutable.cs +++ b/SabreTools.Serialization/Extensions/PortableExecutable.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; using System.Xml.Serialization; using SabreTools.Data.Models.COFF; using SabreTools.Data.Models.PortableExecutable; @@ -243,84 +242,6 @@ namespace SabreTools.Data.Extensions return fontGroupHeader; } - /// - /// Read byte data as a string file info resource - /// - /// Data to parse into a string file info - /// Offset into the byte array - /// A filled string file info resource on success, null on error - public static StringFileInfo? AsStringFileInfo(byte[] data, ref int offset) - { - var stringFileInfo = new StringFileInfo(); - - // Cache the initial offset - int currentOffset = offset; - - stringFileInfo.Length = data.ReadUInt16LittleEndian(ref offset); - stringFileInfo.ValueLength = data.ReadUInt16LittleEndian(ref offset); - stringFileInfo.ResourceType = (VersionResourceType)data.ReadUInt16LittleEndian(ref offset); - stringFileInfo.Key = data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; - if (stringFileInfo.Key != "StringFileInfo") - { - offset -= 6 + (((stringFileInfo.Key?.Length ?? 0) + 1) * 2); - return null; - } - - // Align to the DWORD boundary if we're not at the end - data.AlignToBoundary(ref offset, 4); - - var stringFileInfoChildren = new List(); - while ((offset - currentOffset) < stringFileInfo.Length) - { - var stringTable = new Models.PortableExecutable.Resource.Entries.StringTable(); - - stringTable.Length = data.ReadUInt16LittleEndian(ref offset); - stringTable.ValueLength = data.ReadUInt16LittleEndian(ref offset); - stringTable.ResourceType = (VersionResourceType)data.ReadUInt16LittleEndian(ref offset); - stringTable.Key = data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; - - // Align to the DWORD boundary if we're not at the end - data.AlignToBoundary(ref offset, 4); - - var stringTableChildren = new List(); - while ((offset - currentOffset) < stringTable.Length) - { - var stringData = new StringData(); - - int dataStartOffset = offset; - stringData.Length = data.ReadUInt16LittleEndian(ref offset); - stringData.ValueLength = data.ReadUInt16LittleEndian(ref offset); - stringData.ResourceType = (VersionResourceType)data.ReadUInt16LittleEndian(ref offset); - stringData.Key = data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; - - // Align to the DWORD boundary if we're not at the end - data.AlignToBoundary(ref offset, 4); - - if (stringData.ValueLength > 0) - { - int bytesReadable = Math.Min(stringData.ValueLength * sizeof(ushort), stringData.Length - (offset - dataStartOffset)); - byte[] valueBytes = data.ReadBytes(ref offset, bytesReadable); - stringData.Value = Encoding.Unicode.GetString(valueBytes); - } - - // Align to the DWORD boundary if we're not at the end - data.AlignToBoundary(ref offset, 4); - - stringTableChildren.Add(stringData); - if (stringData.Length == 0 && stringData.ValueLength == 0) - break; - } - - stringTable.Children = [.. stringTableChildren]; - - stringFileInfoChildren.Add(stringTable); - } - - stringFileInfo.Children = [.. stringFileInfoChildren]; - - return stringFileInfo; - } - /// /// Read resource data as a string table resource /// @@ -353,67 +274,6 @@ namespace SabreTools.Data.Extensions return stringTable; } - /// - /// Read byte data as a var file info resource - /// - /// Data to parse into a var file info - /// Offset into the byte array - /// A filled var file info resource on success, null on error - public static VarFileInfo? AsVarFileInfo(byte[] data, ref int offset) - { - var varFileInfo = new VarFileInfo(); - - // Cache the initial offset - int initialOffset = offset; - - varFileInfo.Length = data.ReadUInt16LittleEndian(ref offset); - varFileInfo.ValueLength = data.ReadUInt16LittleEndian(ref offset); - varFileInfo.ResourceType = (VersionResourceType)data.ReadUInt16LittleEndian(ref offset); - varFileInfo.Key = data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; - if (varFileInfo.Key != "VarFileInfo") - return null; - - // Align to the DWORD boundary if we're not at the end - data.AlignToBoundary(ref offset, 4); - - var varFileInfoChildren = new List(); - while ((offset - initialOffset) < varFileInfo.Length) - { - var varData = new VarData(); - - varData.Length = data.ReadUInt16LittleEndian(ref offset); - varData.ValueLength = data.ReadUInt16LittleEndian(ref offset); - varData.ResourceType = (VersionResourceType)data.ReadUInt16LittleEndian(ref offset); - varData.Key = data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; - if (varData.Key != "Translation") - { - offset -= 6 + (((varData.Key?.Length ?? 0) + 1) * 2); - return null; - } - - // Align to the DWORD boundary if we're not at the end - data.AlignToBoundary(ref offset, 4); - - // Cache the current offset - int currentOffset = offset; - - var varDataValue = new List(); - while ((offset - currentOffset) < varData.ValueLength) - { - uint languageAndCodeIdentifierPair = data.ReadUInt32LittleEndian(ref offset); - varDataValue.Add(languageAndCodeIdentifierPair); - } - - varData.Value = [.. varDataValue]; - - varFileInfoChildren.Add(varData); - } - - varFileInfo.Children = [.. varFileInfoChildren]; - - return varFileInfo; - } - /// /// Read resource data as a version info resource /// @@ -425,67 +285,7 @@ namespace SabreTools.Data.Extensions if (entry?.Data is null) return null; - // Initialize the iterator - int offset = 0; - - // Create the output object - var versionInfo = new VersionInfo(); - - versionInfo.Length = entry.Data.ReadUInt16LittleEndian(ref offset); - versionInfo.ValueLength = entry.Data.ReadUInt16LittleEndian(ref offset); - versionInfo.ResourceType = (VersionResourceType)entry.Data.ReadUInt16LittleEndian(ref offset); - versionInfo.Key = entry.Data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; - if (versionInfo.Key != "VS_VERSION_INFO") - return null; - - while (offset < entry.Data.Length && (offset % 4) != 0) - versionInfo.Padding1 = entry.Data.ReadUInt16LittleEndian(ref offset); - - // Read fixed file info - if (versionInfo.ValueLength > 0 && offset + versionInfo.ValueLength <= entry.Data.Length) - { - var fixedFileInfo = ParseFixedFileInfo(entry.Data, ref offset); - if (fixedFileInfo?.Signature != 0xFEEF04BD) - return null; - - versionInfo.Value = fixedFileInfo; - } - - while (offset < entry.Data.Length && (offset % 4) != 0) - versionInfo.Padding2 = entry.Data.ReadUInt16LittleEndian(ref offset); - - // Determine if we have a StringFileInfo or VarFileInfo twice - ReadInfoSection(entry.Data, ref offset, versionInfo); - ReadInfoSection(entry.Data, ref offset, versionInfo); - - return versionInfo; - } - - /// - /// Parse a byte array into a FixedFileInfo - /// - /// Data to parse - /// Offset into the byte array - /// A filled FixedFileInfo on success, null on error - public static FixedFileInfo ParseFixedFileInfo(this byte[] data, ref int offset) - { - var obj = new FixedFileInfo(); - - obj.Signature = data.ReadUInt32LittleEndian(ref offset); - obj.StrucVersion = data.ReadUInt32LittleEndian(ref offset); - obj.FileVersionMS = data.ReadUInt32LittleEndian(ref offset); - obj.FileVersionLS = data.ReadUInt32LittleEndian(ref offset); - obj.ProductVersionMS = data.ReadUInt32LittleEndian(ref offset); - obj.ProductVersionLS = data.ReadUInt32LittleEndian(ref offset); - obj.FileFlagsMask = data.ReadUInt32LittleEndian(ref offset); - obj.FileFlags = (FixedFileInfoFlags)data.ReadUInt32LittleEndian(ref offset); - obj.FileOS = (FixedFileInfoOS)data.ReadUInt32LittleEndian(ref offset); - obj.FileType = (FixedFileInfoFileType)data.ReadUInt32LittleEndian(ref offset); - obj.FileSubtype = (FixedFileInfoFileSubtype)data.ReadUInt32LittleEndian(ref offset); - obj.FileDateMS = data.ReadUInt32LittleEndian(ref offset); - obj.FileDateLS = data.ReadUInt32LittleEndian(ref offset); - - return obj; + return Serialization.Readers.PortableExecutable.ParseVersionInfo(entry.Data); } /// @@ -512,38 +312,6 @@ namespace SabreTools.Data.Extensions return obj; } - /// - /// Read either a `StringFileInfo` or `VarFileInfo` based on the key - /// - /// - /// - /// - /// - private static void ReadInfoSection(byte[] data, ref int offset, VersionInfo versionInfo) - { - // If the offset is invalid, don't move the pointer - if (offset < 0 || offset >= versionInfo.Length) - return; - - // Cache the current offset for reading - int currentOffset = offset; - - offset += 6; - string? nextKey = data.ReadNullTerminatedUnicodeString(ref offset); - offset = currentOffset; - - if (nextKey == "StringFileInfo") - { - var stringFileInfo = AsStringFileInfo(data, ref offset); - versionInfo.StringFileInfo = stringFileInfo; - } - else if (nextKey == "VarFileInfo") - { - var varFileInfo = AsVarFileInfo(data, ref offset); - versionInfo.VarFileInfo = varFileInfo; - } - } - #endregion #region Helpers diff --git a/SabreTools.Serialization/Readers/PortableExecutable.cs b/SabreTools.Serialization/Readers/PortableExecutable.cs index 90f5abad..9db1f0f5 100644 --- a/SabreTools.Serialization/Readers/PortableExecutable.cs +++ b/SabreTools.Serialization/Readers/PortableExecutable.cs @@ -1450,6 +1450,33 @@ namespace SabreTools.Serialization.Readers return obj; } + /// + /// Parse a byte array into a FixedFileInfo + /// + /// Data to parse + /// Offset into the byte array + /// A filled FixedFileInfo on success, null on error + public static Data.Models.PortableExecutable.Resource.Entries.FixedFileInfo ParseFixedFileInfo(byte[] data, ref int offset) + { + var obj = new Data.Models.PortableExecutable.Resource.Entries.FixedFileInfo(); + + obj.Signature = data.ReadUInt32LittleEndian(ref offset); + obj.StrucVersion = data.ReadUInt32LittleEndian(ref offset); + obj.FileVersionMS = data.ReadUInt32LittleEndian(ref offset); + obj.FileVersionLS = data.ReadUInt32LittleEndian(ref offset); + obj.ProductVersionMS = data.ReadUInt32LittleEndian(ref offset); + obj.ProductVersionLS = data.ReadUInt32LittleEndian(ref offset); + obj.FileFlagsMask = data.ReadUInt32LittleEndian(ref offset); + obj.FileFlags = (FixedFileInfoFlags)data.ReadUInt32LittleEndian(ref offset); + obj.FileOS = (FixedFileInfoOS)data.ReadUInt32LittleEndian(ref offset); + obj.FileType = (FixedFileInfoFileType)data.ReadUInt32LittleEndian(ref offset); + obj.FileSubtype = (FixedFileInfoFileSubtype)data.ReadUInt32LittleEndian(ref offset); + obj.FileDateMS = data.ReadUInt32LittleEndian(ref offset); + obj.FileDateLS = data.ReadUInt32LittleEndian(ref offset); + + return obj; + } + /// /// Parse a Stream into a FunctionDefinition /// @@ -1811,6 +1838,37 @@ namespace SabreTools.Serialization.Readers return obj; } + /// + /// Read either a `StringFileInfo` or `VarFileInfo` based on the key + /// + /// Byte array to parse + /// Offset into the byte array + /// + public static void ReadInfoSection(byte[] data, ref int offset, Data.Models.PortableExecutable.Resource.Entries.VersionInfo versionInfo) + { + // If the offset is invalid, don't move the pointer + if (offset < 0 || offset >= versionInfo.Length) + return; + + // Cache the current offset for reading + int currentOffset = offset; + + offset += 6; + string? nextKey = data.ReadNullTerminatedUnicodeString(ref offset); + offset = currentOffset; + + if (nextKey == "StringFileInfo") + { + var stringFileInfo = ParseStringFileInfo(data, ref offset); + versionInfo.StringFileInfo = stringFileInfo; + } + else if (nextKey == "VarFileInfo") + { + var varFileInfo = ParseVarFileInfo(data, ref offset); + versionInfo.VarFileInfo = varFileInfo; + } + } + /// /// Parse a Stream into a LineNumber /// @@ -2542,6 +2600,76 @@ namespace SabreTools.Serialization.Readers return obj; } + /// + /// Read byte data as a string file info resource + /// + /// Byte array to parse + /// Offset into the byte array + /// A filled string file info resource on success, null on error + public static Data.Models.PortableExecutable.Resource.Entries.StringFileInfo? ParseStringFileInfo(byte[] data, ref int offset) + { + var obj = new Data.Models.PortableExecutable.Resource.Entries.StringFileInfo(); + + // Cache the initial offset + int currentOffset = offset; + + obj.Length = data.ReadUInt16LittleEndian(ref offset); + obj.ValueLength = data.ReadUInt16LittleEndian(ref offset); + obj.ResourceType = (VersionResourceType)data.ReadUInt16LittleEndian(ref offset); + obj.Key = data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; + if (obj.Key != "StringFileInfo") + { + offset -= 6 + (((obj.Key?.Length ?? 0) + 1) * 2); + return null; + } + + // Align to the DWORD boundary if we're not at the end + data.AlignToBoundary(ref offset, 4); + + var stringFileInfoChildren = new List(); + while ((offset - currentOffset) < obj.Length) + { + var stringTable = ParseStringTableResource(data, ref offset, currentOffset); + stringFileInfoChildren.Add(stringTable); + } + + obj.Children = [.. stringFileInfoChildren]; + + return obj; + } + + /// + /// Read byte data as a StringData + /// + /// Byte array to parse + /// Offset into the byte array + /// A filled StringData on success, null on error + public static Data.Models.PortableExecutable.Resource.Entries.StringData ParseStringData(byte[] data, ref int offset) + { + var obj = new Data.Models.PortableExecutable.Resource.Entries.StringData(); + + int dataStartOffset = offset; + obj.Length = data.ReadUInt16LittleEndian(ref offset); + obj.ValueLength = data.ReadUInt16LittleEndian(ref offset); + obj.ResourceType = (VersionResourceType)data.ReadUInt16LittleEndian(ref offset); + obj.Key = data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; + + // Align to the DWORD boundary if we're not at the end + data.AlignToBoundary(ref offset, 4); + + if (obj.ValueLength > 0) + { + int bytesReadable = Math.Min(obj.ValueLength * sizeof(ushort), obj.Length - (offset - dataStartOffset)); + byte[] valueBytes = data.ReadBytes(ref offset, bytesReadable); + obj.Value = Encoding.Unicode.GetString(valueBytes); + } + + // Align to the DWORD boundary if we're not at the end + data.AlignToBoundary(ref offset, 4); + + return obj; + } + /// /// Parse a Stream into a StringTable /// @@ -2571,6 +2699,38 @@ namespace SabreTools.Serialization.Readers return obj; } + /// + /// Read byte data as a StringTable resource + /// + /// Byte array to parse + /// Offset into the byte array + /// A filled StringTable resource on success, null on error + public static Data.Models.PortableExecutable.Resource.Entries.StringTable ParseStringTableResource(byte[] data, ref int offset, int initialOffset) + { + var obj = new Data.Models.PortableExecutable.Resource.Entries.StringTable(); + + obj.Length = data.ReadUInt16LittleEndian(ref offset); + obj.ValueLength = data.ReadUInt16LittleEndian(ref offset); + obj.ResourceType = (VersionResourceType)data.ReadUInt16LittleEndian(ref offset); + obj.Key = data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; + + // Align to the DWORD boundary if we're not at the end + data.AlignToBoundary(ref offset, 4); + + var stringTableChildren = new List(); + while ((offset - initialOffset) < obj.Length) + { + var stringData = ParseStringData(data, ref offset); + stringTableChildren.Add(stringData); + if (stringData.Length == 0 && stringData.ValueLength == 0) + break; + } + + obj.Children = [.. stringTableChildren]; + + return obj; + } + /// /// Parse a Stream into a symbol table /// @@ -2651,6 +2811,111 @@ namespace SabreTools.Serialization.Readers return obj; } + /// + /// Read byte data as a VarFileInfo + /// + /// Byte array to parse + /// Offset into the byte array + /// A filled VarFileInfo on success, null on error + public static Data.Models.PortableExecutable.Resource.Entries.VarFileInfo? ParseVarFileInfo(byte[] data, ref int offset) + { + var obj = new Data.Models.PortableExecutable.Resource.Entries.VarFileInfo(); + + // Cache the initial offset + int initialOffset = offset; + + obj.Length = data.ReadUInt16LittleEndian(ref offset); + obj.ValueLength = data.ReadUInt16LittleEndian(ref offset); + obj.ResourceType = (VersionResourceType)data.ReadUInt16LittleEndian(ref offset); + obj.Key = data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; + if (obj.Key != "VarFileInfo") + return null; + + // Align to the DWORD boundary if we're not at the end + data.AlignToBoundary(ref offset, 4); + + var varFileInfoChildren = new List(); + while ((offset - initialOffset) < obj.Length) + { + var varData = new Data.Models.PortableExecutable.Resource.Entries.VarData(); + + varData.Length = data.ReadUInt16LittleEndian(ref offset); + varData.ValueLength = data.ReadUInt16LittleEndian(ref offset); + varData.ResourceType = (VersionResourceType)data.ReadUInt16LittleEndian(ref offset); + varData.Key = data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; + if (varData.Key != "Translation") + { + offset -= 6 + (((varData.Key?.Length ?? 0) + 1) * 2); + return null; + } + + // Align to the DWORD boundary if we're not at the end + data.AlignToBoundary(ref offset, 4); + + // Cache the current offset + int currentOffset = offset; + + var varDataValue = new List(); + while ((offset - currentOffset) < varData.ValueLength) + { + uint languageAndCodeIdentifierPair = data.ReadUInt32LittleEndian(ref offset); + varDataValue.Add(languageAndCodeIdentifierPair); + } + + varData.Value = [.. varDataValue]; + + varFileInfoChildren.Add(varData); + } + + obj.Children = [.. varFileInfoChildren]; + + return obj; + } + + /// + /// Parse a byte array into a VersionInfo + /// + /// Byte array to parse + /// Offset into the byte array + /// Filled VersionInfo on success, null on error + public static Data.Models.PortableExecutable.Resource.Entries.VersionInfo? ParseVersionInfo(byte[] data) + { + // Initialize the iterator + int offset = 0; + + // Create the output object + var obj = new Data.Models.PortableExecutable.Resource.Entries.VersionInfo(); + + obj.Length = data.ReadUInt16LittleEndian(ref offset); + obj.ValueLength = data.ReadUInt16LittleEndian(ref offset); + obj.ResourceType = (VersionResourceType)data.ReadUInt16LittleEndian(ref offset); + obj.Key = data.ReadNullTerminatedUnicodeString(ref offset) ?? string.Empty; + if (obj.Key != "VS_VERSION_INFO") + return null; + + while (offset < data.Length && (offset % 4) != 0) + obj.Padding1 = data.ReadUInt16LittleEndian(ref offset); + + // Read fixed file info + if (obj.ValueLength > 0 && offset + obj.ValueLength <= data.Length) + { + var fixedFileInfo = ParseFixedFileInfo(data, ref offset); + if (fixedFileInfo?.Signature != 0xFEEF04BD) + return null; + + obj.Value = fixedFileInfo; + } + + while (offset < data.Length && (offset % 4) != 0) + obj.Padding2 = data.ReadUInt16LittleEndian(ref offset); + + // Determine if we have a StringFileInfo or VarFileInfo twice + ReadInfoSection(data, ref offset, obj); + ReadInfoSection(data, ref offset, obj); + + return obj; + } + /// /// Parse a Stream into a WeakExternal /// diff --git a/SabreTools.Serialization/Wrappers/PortableExecutable.Printing.cs b/SabreTools.Serialization/Wrappers/PortableExecutable.Printing.cs index 904f9d6c..6a1da020 100644 --- a/SabreTools.Serialization/Wrappers/PortableExecutable.Printing.cs +++ b/SabreTools.Serialization/Wrappers/PortableExecutable.Printing.cs @@ -1072,7 +1072,7 @@ namespace SabreTools.Serialization.Wrappers string padding = new(' ', (level + 1) * 2); MenuResource? menu = null; - try { menu = entry.AsMenu(); } catch { } + try { menu = Readers.PortableExecutable.ParseMenuResource(entry.Data); } catch { } if (menu is null) { @@ -1149,7 +1149,7 @@ namespace SabreTools.Serialization.Wrappers string padding = new(' ', (level + 1) * 2); DialogBoxResource? dialogBox = null; - try { dialogBox = entry.AsDialogBox(); } catch { } + try { dialogBox = Readers.PortableExecutable.ParseDialogBoxResource(entry.Data); } catch { } if (dialogBox is null) { @@ -1318,7 +1318,7 @@ namespace SabreTools.Serialization.Wrappers string padding = new(' ', (level + 1) * 2); AcceleratorTable? acceleratorTable = null; - try { acceleratorTable = entry.AsAcceleratorTableResource(); } catch { } + try { acceleratorTable = Readers.PortableExecutable.ParseAcceleratorTable(entry.Data); } catch { } if (acceleratorTable?.Entries is null) { @@ -1401,7 +1401,7 @@ namespace SabreTools.Serialization.Wrappers string padding = new(' ', (level + 1) * 2); MessageResourceData? messageTable = null; - try { messageTable = entry.AsMessageResourceData(); } catch { } + try { messageTable = Readers.PortableExecutable.ParseMessageResourceData(entry.Data); } catch { } if (messageTable is null) { @@ -1480,7 +1480,7 @@ namespace SabreTools.Serialization.Wrappers string padding = new(' ', (level + 1) * 2); VersionInfo? versionInfo = null; - try { versionInfo = entry.AsVersionInfo(); } catch { } + try { versionInfo = Readers.PortableExecutable.ParseVersionInfo(entry.Data); } catch { } if (versionInfo is null) { diff --git a/SabreTools.Serialization/Wrappers/PortableExecutable.cs b/SabreTools.Serialization/Wrappers/PortableExecutable.cs index 36252974..503b567f 100644 --- a/SabreTools.Serialization/Wrappers/PortableExecutable.cs +++ b/SabreTools.Serialization/Wrappers/PortableExecutable.cs @@ -1887,11 +1887,11 @@ namespace SabreTools.Serialization.Wrappers break; case ResourceType.RT_MENU: case ResourceType.RT_NEWMENU: - value = entry.AsMenu(); + value = Readers.PortableExecutable.ParseMenuResource(entry.Data); break; case ResourceType.RT_DIALOG: case ResourceType.RT_NEWDIALOG: - value = entry.AsDialogBox(); + value = Readers.PortableExecutable.ParseDialogBoxResource(entry.Data); break; case ResourceType.RT_STRING: value = entry.AsStringTable(); @@ -1903,13 +1903,13 @@ namespace SabreTools.Serialization.Wrappers value = entry.Data; break; case ResourceType.RT_ACCELERATOR: - value = entry.AsAcceleratorTableResource(); + value = Readers.PortableExecutable.ParseAcceleratorTable(entry.Data); break; case ResourceType.RT_RCDATA: value = entry.Data; break; case ResourceType.RT_MESSAGETABLE: - value = entry.AsMessageResourceData(); + value = Readers.PortableExecutable.ParseMessageResourceData(entry.Data); break; case ResourceType.RT_GROUP_CURSOR: value = entry.Data; @@ -1918,7 +1918,7 @@ namespace SabreTools.Serialization.Wrappers value = entry.Data; break; case ResourceType.RT_VERSION: - _versionInfo = entry.AsVersionInfo(); + _versionInfo = Readers.PortableExecutable.ParseVersionInfo(entry.Data); value = _versionInfo; break; case ResourceType.RT_DLGINCLUDE: