diff --git a/SabreTools.Serialization/Deserializers/PortableExecutable.cs b/SabreTools.Serialization/Deserializers/PortableExecutable.cs
index 44b24ced..2fea7d57 100644
--- a/SabreTools.Serialization/Deserializers/PortableExecutable.cs
+++ b/SabreTools.Serialization/Deserializers/PortableExecutable.cs
@@ -138,7 +138,7 @@ namespace SabreTools.Serialization.Deserializers
// Seek to the export table
data.Seek(offset, SeekOrigin.Begin);
- // Read the table data for parsing
+ // Read the table data
byte[] tableData = data.ReadBytes(tableSize);
// Set the export table
@@ -165,7 +165,7 @@ namespace SabreTools.Serialization.Deserializers
// Seek to the import table
data.Seek(offset, SeekOrigin.Begin);
- // Read the table data for parsing
+ // Read the table data
byte[] tableData = data.ReadBytes(tableSize);
// Set the import table
@@ -192,7 +192,7 @@ namespace SabreTools.Serialization.Deserializers
// Seek to the resource directory table
data.Seek(offset, SeekOrigin.Begin);
- // Read the table data for parsing
+ // Read the table data
byte[] tableData = data.ReadBytes(tableSize);
// Set the resource directory table
@@ -249,15 +249,11 @@ namespace SabreTools.Serialization.Deserializers
long paddingSize = optionalHeader.FileAlignment - ((offset + tableSize) % optionalHeader.FileAlignment);
tableSize += (int)paddingSize;
- // Seek to the attribute certificate table
- data.Seek(offset, SeekOrigin.Begin);
- long endOffset = offset + tableSize;
-
- // Read the table data for parsing
+ // Read the table data
byte[] tableData = data.ReadBytes(tableSize);
// Set the attribute certificate table
- pex.AttributeCertificateTable = ParseAttributeCertificateTable(tableData, data, endOffset);
+ pex.AttributeCertificateTable = ParseAttributeCertificateTable(tableData);
}
}
@@ -277,15 +273,11 @@ namespace SabreTools.Serialization.Deserializers
long paddingSize = optionalHeader.FileAlignment - ((offset + tableSize) % optionalHeader.FileAlignment);
tableSize += (int)paddingSize;
- // Seek to the base relocation table
- data.Seek(offset, SeekOrigin.Begin);
- long endOffset = offset + tableSize;
-
- // Read the table data for parsing
+ // Read the table data
byte[] tableData = data.ReadBytes(tableSize);
// Set the base relocation table
- pex.BaseRelocationTable = ParseBaseRelocationTable(tableData, data, endOffset);
+ pex.BaseRelocationTable = ParseBaseRelocationTable(tableData);
}
}
@@ -305,15 +297,11 @@ namespace SabreTools.Serialization.Deserializers
long paddingSize = optionalHeader.FileAlignment - ((offset + tableSize) % optionalHeader.FileAlignment);
tableSize += (int)paddingSize;
- // Seek to the debug table
- data.Seek(offset, SeekOrigin.Begin);
- long endOffset = offset + tableSize;
-
- // Read the table data for parsing
- byte[] tableData = data.ReadBytes(tableSize);
+ // Read the table data
+ byte[]? tableData = data.ReadFrom(offset, tableSize, retainPosition: true);
// Set the debug table
- pex.DebugTable = ParseDebugTable(tableData, data, endOffset);
+ pex.DebugTable = ParseDebugTable(tableData);
}
}
@@ -339,14 +327,11 @@ namespace SabreTools.Serialization.Deserializers
long paddingSize = optionalHeader.FileAlignment - ((offset + tableSize) % optionalHeader.FileAlignment);
tableSize += (int)paddingSize;
- // Seek to the delay-load directory table
- data.Seek(offset, SeekOrigin.Begin);
-
- // Read the table data for parsing
- byte[] tableData = data.ReadBytes(tableSize);
+ // Read the table data
+ byte[]? tableData = data.ReadFrom(offset, tableSize, retainPosition: true);
// Set the delay-load directory table
- pex.DelayLoadDirectoryTable = ParseDelayLoadDirectoryTable(tableData, data);
+ pex.DelayLoadDirectoryTable = ParseDelayLoadDirectoryTable(tableData);
}
}
@@ -365,118 +350,129 @@ namespace SabreTools.Serialization.Deserializers
}
///
- /// Parse a Stream into an attribute certificate table
+ /// Parse a byte array into an attribute certificate table
///
- /// Byte array representing the table data
- /// Stream to use for additional parsing
- /// First address not part of the attribute certificate table
+ /// Byte array to parse
/// Filled attribute certificate on success, null on error
- public static AttributeCertificateTableEntry[] ParseAttributeCertificateTable(byte[] tableData, Stream data, long endOffset)
+ public static AttributeCertificateTableEntry[]? ParseAttributeCertificateTable(byte[]? data)
{
+ if (data == null)
+ return null;
+
var obj = new List();
- while (data.Position < endOffset && data.Position < data.Length)
+ int offset = 0;
+ while (offset < data.Length)
{
- var entry = ParseAttributeCertificateTableEntry(data);
+ var entry = ParseAttributeCertificateTableEntry(data, ref offset);
obj.Add(entry);
- // Align to the 8-byte boundary
- data.AlignToBoundary(8);
+ // Align to the QWORD boundary if we're not at the end
+ while (offset < data.Length && offset % 8 != 0)
+ {
+ offset++;
+ }
}
return [.. obj];
}
///
- /// Parse a Stream into an AttributeCertificateTableEntry
+ /// Parse a byte array into an AttributeCertificateTableEntry
///
- /// Stream to parse
+ /// Byte array to parse
/// Filled AttributeCertificateTableEntry on success, null on error
- public static AttributeCertificateTableEntry ParseAttributeCertificateTableEntry(Stream data)
+ public static AttributeCertificateTableEntry ParseAttributeCertificateTableEntry(byte[] data, ref int offset)
{
var obj = new AttributeCertificateTableEntry();
- obj.Length = data.ReadUInt32LittleEndian();
- obj.Revision = (WindowsCertificateRevision)data.ReadUInt16LittleEndian();
- obj.CertificateType = (WindowsCertificateType)data.ReadUInt16LittleEndian();
+ obj.Length = data.ReadUInt32LittleEndian(ref offset);
+ obj.Revision = (WindowsCertificateRevision)data.ReadUInt16LittleEndian(ref offset);
+ obj.CertificateType = (WindowsCertificateType)data.ReadUInt16LittleEndian(ref offset);
int certificateDataLength = (int)obj.Length - 8;
- if (certificateDataLength > 0 && data.Position + certificateDataLength <= data.Length)
- obj.Certificate = data.ReadBytes(certificateDataLength);
+ if (certificateDataLength > 0 && offset + certificateDataLength <= data.Length)
+ obj.Certificate = data.ReadBytes(ref offset, certificateDataLength);
return obj;
}
///
- /// Parse a Stream into an BaseRelocationBlock
+ /// Parse a byte array into an BaseRelocationBlock
///
- /// Stream to parse
- /// First address not part of the base relocation table
+ /// Byte array to parse
+ /// Offset into the byte array
/// Filled BaseRelocationBlock on success, null on error
- public static BaseRelocationBlock? ParseBaseRelocationBlock(Stream data, long endOffset)
+ public static BaseRelocationBlock? ParseBaseRelocationBlock(byte[] data, ref int offset)
{
var obj = new BaseRelocationBlock();
- obj.PageRVA = data.ReadUInt32LittleEndian();
- obj.BlockSize = data.ReadUInt32LittleEndian();
+ obj.PageRVA = data.ReadUInt32LittleEndian(ref offset);
+ obj.BlockSize = data.ReadUInt32LittleEndian(ref offset);
if (obj.BlockSize <= 8)
return obj;
// Guard against invalid block sizes
if (obj.BlockSize % 2 != 0)
return obj;
- if (data.Position + obj.BlockSize > endOffset)
+ if (offset + obj.BlockSize > data.Length)
return obj;
int entryCount = ((int)obj.BlockSize - 8) / 2;
obj.TypeOffsetFieldEntries = new BaseRelocationTypeOffsetFieldEntry[entryCount];
for (int i = 0; i < obj.TypeOffsetFieldEntries.Length; i++)
{
- if (data.Position + 2 >= data.Length)
+ if (offset + 2 >= data.Length)
break;
- obj.TypeOffsetFieldEntries[i] = ParseBaseRelocationTypeOffsetFieldEntry(data);
+ obj.TypeOffsetFieldEntries[i] = ParseBaseRelocationTypeOffsetFieldEntry(data, ref offset);
}
return obj;
}
///
- /// Parse a Stream into a base relocation table
+ /// Parse a byte array into a base relocation table
///
- /// Byte array representing the table data
- /// Stream to use for additional parsing
- /// First address not part of the base relocation table
+ /// Byte array to parse
/// Filled base relocation table on success, null on error
- public static BaseRelocationBlock[] ParseBaseRelocationTable(byte[] tableData, Stream data, long endOffset)
+ public static BaseRelocationBlock[]? ParseBaseRelocationTable(byte[]? data)
{
+ if (data == null)
+ return null;
+
var obj = new List();
- while (data.Position < endOffset && data.Position < data.Length)
+ int offset = 0;
+ while (offset < data.Length)
{
- var block = ParseBaseRelocationBlock(data, endOffset);
+ var block = ParseBaseRelocationBlock(data, ref offset);
if (block == null)
break;
obj.Add(block);
// Align to the DWORD boundary if we're not at the end
- data.AlignToBoundary(4);
+ while (offset < data.Length && offset % 4 != 0)
+ {
+ offset++;
+ }
}
return [.. obj];
}
///
- /// Parse a Stream into a BaseRelocationTypeOffsetFieldEntry
+ /// Parse a byte array into a BaseRelocationTypeOffsetFieldEntry
///
- /// Stream to parse
+ /// Byte array to parse
+ /// Offset into the byte array
/// Filled BaseRelocationTypeOffsetFieldEntry on success, null on error
- public static BaseRelocationTypeOffsetFieldEntry ParseBaseRelocationTypeOffsetFieldEntry(Stream data)
+ public static BaseRelocationTypeOffsetFieldEntry ParseBaseRelocationTypeOffsetFieldEntry(byte[] data, ref int offset)
{
var obj = new BaseRelocationTypeOffsetFieldEntry();
- ushort typeAndOffsetField = data.ReadUInt16LittleEndian();
+ ushort typeAndOffsetField = data.ReadUInt16LittleEndian(ref offset);
obj.BaseRelocationType = (BaseRelocationTypes)(typeAndOffsetField >> 12);
obj.Offset = (ushort)(typeAndOffsetField & 0x0FFF);
@@ -677,46 +673,49 @@ namespace SabreTools.Serialization.Deserializers
}
///
- /// Parse a Stream into a DebugDirectoryEntry
+ /// Parse a byte array into a DebugDirectoryEntry
///
- /// Stream to parse
+ /// Byte array to parse
+ /// Offset into the byte array
/// Filled DebugDirectoryEntry on success, null on error
- public static DebugDirectoryEntry ParseDebugDirectoryEntry(Stream data)
+ public static DebugDirectoryEntry ParseDebugDirectoryEntry(byte[] data, ref int offset)
{
var obj = new DebugDirectoryEntry();
- obj.Characteristics = data.ReadUInt32LittleEndian();
- obj.TimeDateStamp = data.ReadUInt32LittleEndian();
- obj.MajorVersion = data.ReadUInt16LittleEndian();
- obj.MinorVersion = data.ReadUInt16LittleEndian();
- obj.DebugType = (DebugType)data.ReadUInt32LittleEndian();
- obj.SizeOfData = data.ReadUInt32LittleEndian();
- obj.AddressOfRawData = data.ReadUInt32LittleEndian();
- obj.PointerToRawData = data.ReadUInt32LittleEndian();
+ obj.Characteristics = data.ReadUInt32LittleEndian(ref offset);
+ obj.TimeDateStamp = data.ReadUInt32LittleEndian(ref offset);
+ obj.MajorVersion = data.ReadUInt16LittleEndian(ref offset);
+ obj.MinorVersion = data.ReadUInt16LittleEndian(ref offset);
+ obj.DebugType = (DebugType)data.ReadUInt32LittleEndian(ref offset);
+ obj.SizeOfData = data.ReadUInt32LittleEndian(ref offset);
+ obj.AddressOfRawData = data.ReadUInt32LittleEndian(ref offset);
+ obj.PointerToRawData = data.ReadUInt32LittleEndian(ref offset);
return obj;
}
///
- /// Parse a Stream into a DebugTable
+ /// Parse a byte array into a DebugTable
///
- /// Byte array representing the table data
- /// Stream to use for additional parsing
- /// First address not part of the debug table
+ /// Byte array to parse
/// Filled DebugTable on success, null on error
- public static DebugTable ParseDebugTable(byte[] tableData, Stream data, long endOffset)
+ public static DebugTable? ParseDebugTable(byte[]? data)
{
+ if (data == null)
+ return null;
+
var obj = new DebugTable();
- var debugDirectoryTable = new List();
+ var table = new List();
- while (data.Position < endOffset && data.Position < data.Length)
+ int offset = 0;
+ while (offset < data.Length)
{
- var debugDirectoryEntry = ParseDebugDirectoryEntry(data);
- debugDirectoryTable.Add(debugDirectoryEntry);
+ var entry = ParseDebugDirectoryEntry(data, ref offset);
+ table.Add(entry);
}
- obj.DebugDirectoryTable = [.. debugDirectoryTable];
+ obj.DebugDirectoryTable = [.. table];
// TODO: Should we read the debug data in? Most of it is unformatted or undocumented
// TODO: Implement .debug$F (Object Only) / IMAGE_DEBUG_TYPE_FPO
@@ -728,23 +727,26 @@ namespace SabreTools.Serialization.Deserializers
}
///
- /// Parse a Stream into a DelayLoadDirectoryTable
+ /// Parse a byte array into a DelayLoadDirectoryTable
///
- /// Byte array representing the table data
- /// Stream to use for additional parsing
+ /// Byte array to parse
/// Filled DelayLoadDirectoryTable on success, null on error
- public static DelayLoadDirectoryTable ParseDelayLoadDirectoryTable(byte[] tableData, Stream data)
+ public static DelayLoadDirectoryTable? ParseDelayLoadDirectoryTable(byte[]? data)
{
+ if (data == null)
+ return null;
+
var obj = new DelayLoadDirectoryTable();
- obj.Attributes = data.ReadUInt32LittleEndian();
- obj.Name = data.ReadUInt32LittleEndian();
- obj.ModuleHandle = data.ReadUInt32LittleEndian();
- obj.DelayImportAddressTable = data.ReadUInt32LittleEndian();
- obj.DelayImportNameTable = data.ReadUInt32LittleEndian();
- obj.BoundDelayImportTable = data.ReadUInt32LittleEndian();
- obj.UnloadDelayImportTable = data.ReadUInt32LittleEndian();
- obj.TimeStamp = data.ReadUInt32LittleEndian();
+ int offset = 0;
+ obj.Attributes = data.ReadUInt32LittleEndian(ref offset);
+ obj.Name = data.ReadUInt32LittleEndian(ref offset);
+ obj.ModuleHandle = data.ReadUInt32LittleEndian(ref offset);
+ obj.DelayImportAddressTable = data.ReadUInt32LittleEndian(ref offset);
+ obj.DelayImportNameTable = data.ReadUInt32LittleEndian(ref offset);
+ obj.BoundDelayImportTable = data.ReadUInt32LittleEndian(ref offset);
+ obj.UnloadDelayImportTable = data.ReadUInt32LittleEndian(ref offset);
+ obj.TimeStamp = data.ReadUInt32LittleEndian(ref offset);
return obj;
}