Do the same for version info

This commit is contained in:
Matt Nadareski
2026-02-12 14:08:12 -05:00
parent d089af71a4
commit ad3df18b24
4 changed files with 276 additions and 243 deletions

View File

@@ -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;
}
/// <summary>
/// Read byte data as a string file info resource
/// </summary>
/// <param name="data">Data to parse into a string file info</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled string file info resource on success, null on error</returns>
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<Models.PortableExecutable.Resource.Entries.StringTable>();
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<StringData>();
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;
}
/// <summary>
/// Read resource data as a string table resource
/// </summary>
@@ -353,67 +274,6 @@ namespace SabreTools.Data.Extensions
return stringTable;
}
/// <summary>
/// Read byte data as a var file info resource
/// </summary>
/// <param name="data">Data to parse into a var file info</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled var file info resource on success, null on error</returns>
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<VarData>();
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<uint>();
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;
}
/// <summary>
/// Read resource data as a version info resource
/// </summary>
@@ -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;
}
/// <summary>
/// Parse a byte array into a FixedFileInfo
/// </summary>
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled FixedFileInfo on success, null on error</returns>
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);
}
/// <summary>
@@ -512,38 +312,6 @@ namespace SabreTools.Data.Extensions
return obj;
}
/// <summary>
/// Read either a `StringFileInfo` or `VarFileInfo` based on the key
/// </summary>
/// <param name="entry"></param>
/// <param name="offset"></param>
/// <param name="versionInfo"></param>
/// <returns></returns>
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

View File

@@ -1450,6 +1450,33 @@ namespace SabreTools.Serialization.Readers
return obj;
}
/// <summary>
/// Parse a byte array into a FixedFileInfo
/// </summary>
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled FixedFileInfo on success, null on error</returns>
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;
}
/// <summary>
/// Parse a Stream into a FunctionDefinition
/// </summary>
@@ -1811,6 +1838,37 @@ namespace SabreTools.Serialization.Readers
return obj;
}
/// <summary>
/// Read either a `StringFileInfo` or `VarFileInfo` based on the key
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="versionInfo"></param>
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;
}
}
/// <summary>
/// Parse a Stream into a LineNumber
/// </summary>
@@ -2542,6 +2600,76 @@ namespace SabreTools.Serialization.Readers
return obj;
}
/// <summary>
/// Read byte data as a string file info resource
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled string file info resource on success, null on error</returns>
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<Data.Models.PortableExecutable.Resource.Entries.StringTable>();
while ((offset - currentOffset) < obj.Length)
{
var stringTable = ParseStringTableResource(data, ref offset, currentOffset);
stringFileInfoChildren.Add(stringTable);
}
obj.Children = [.. stringFileInfoChildren];
return obj;
}
/// <summary>
/// Read byte data as a StringData
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled StringData on success, null on error</returns>
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;
}
/// <summary>
/// Parse a Stream into a StringTable
/// </summary>
@@ -2571,6 +2699,38 @@ namespace SabreTools.Serialization.Readers
return obj;
}
/// <summary>
/// Read byte data as a StringTable resource
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled StringTable resource on success, null on error</returns>
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<Data.Models.PortableExecutable.Resource.Entries.StringData>();
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;
}
/// <summary>
/// Parse a Stream into a symbol table
/// </summary>
@@ -2651,6 +2811,111 @@ namespace SabreTools.Serialization.Readers
return obj;
}
/// <summary>
/// Read byte data as a VarFileInfo
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled VarFileInfo on success, null on error</returns>
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<Data.Models.PortableExecutable.Resource.Entries.VarData>();
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<uint>();
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;
}
/// <summary>
/// Parse a byte array into a VersionInfo
/// </summary>
/// <param name="tableData">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled VersionInfo on success, null on error</returns>
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;
}
/// <summary>
/// Parse a Stream into a WeakExternal
/// </summary>

View File

@@ -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)
{

View File

@@ -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: