diff --git a/BurnOutSharp.Builder/BFPK.cs b/BurnOutSharp.Builder/BFPK.cs
index 8f6aa46b..06f70f57 100644
--- a/BurnOutSharp.Builder/BFPK.cs
+++ b/BurnOutSharp.Builder/BFPK.cs
@@ -5,7 +5,6 @@ using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builder
{
- // TODO: Make Stream Data rely on Byte Data
public class BFPK
{
#region Byte Data
@@ -26,95 +25,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
- // Cache the current offset
- int initialOffset = offset;
-
- // Create a new archive to fill
- var archive = new Archive();
-
- #region Header
-
- // Try to parse the header
- var header = ParseHeader(data, ref offset);
- if (header == null)
- return null;
-
- // Set the archive header
- archive.Header = header;
-
- #endregion
-
- #region Files
-
- // If we have any files
- if (header.Files > 0)
- {
- var files = new FileEntry[header.Files];
-
- // Read all entries in turn
- for (int i = 0; i < header.Files; i++)
- {
- var file = ParseFileEntry(data, ref offset);
- if (file == null)
- return null;
-
- files[i] = file;
- }
-
- // Set the files
- archive.Files = files;
- }
-
- #endregion
-
- return archive;
- }
-
- ///
- /// Parse a byte array into a header
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled header on success, null on error
- private static Header ParseHeader(byte[] data, ref int offset)
- {
- // TODO: Use marshalling here instead of building
- Header header = new Header();
-
- header.Magic = data.ReadUInt32(ref offset);
- if (header.Magic != 0x4b504642)
- return null;
-
- header.Version = data.ReadInt32(ref offset);
- header.Files = data.ReadInt32(ref offset);
-
- return header;
- }
-
- ///
- /// Parse a byte array into a file entry
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled file entry on success, null on error
- private static FileEntry ParseFileEntry(byte[] data, ref int offset)
- {
- // TODO: Use marshalling here instead of building
- FileEntry fileEntry = new FileEntry();
-
- fileEntry.NameSize = data.ReadInt32(ref offset);
- if (fileEntry.NameSize > 0)
- fileEntry.Name = new string(data.ReadBytes(ref offset, fileEntry.NameSize).Select(b => (char)b).ToArray());
-
- fileEntry.UncompressedSize = data.ReadInt32(ref offset);
- fileEntry.Offset = data.ReadInt32(ref offset);
- if (fileEntry.Offset > 0)
- {
- int entryOffset = fileEntry.Offset;
- fileEntry.CompressedSize = data.ReadInt32(ref entryOffset);
- }
-
- return fileEntry;
+ // Create a memory stream and parse that
+ MemoryStream dataStream = new MemoryStream(data);
+ dataStream.Position = offset;
+ return ParseArchive(dataStream);
}
#endregion
diff --git a/BurnOutSharp.Builder/LinearExecutable.cs b/BurnOutSharp.Builder/LinearExecutable.cs
index 26613774..35ade8e2 100644
--- a/BurnOutSharp.Builder/LinearExecutable.cs
+++ b/BurnOutSharp.Builder/LinearExecutable.cs
@@ -3,7 +3,6 @@ using BurnOutSharp.Models.LinearExecutable;
namespace BurnOutSharp.Builder
{
- // TODO: Make Stream Data rely on Byte Data
public static class LinearExecutable
{
#region Byte Data
@@ -24,34 +23,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
- // Cache the current offset
- int initialOffset = offset;
-
- // Create a new executable to fill
- var executable = new Executable();
-
- // Parse the MS-DOS stub
- var stub = MSDOS.ParseExecutable(data, offset);
- if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
- return null;
-
- // Set the MS-DOS stub
- executable.Stub = stub;
-
- // TODO: Implement LE/LX parsing
- return null;
- }
-
- ///
- /// Parse a byte array into a Linear Executable information block
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled information block on success, null on error
- private static InformationBlock ParseInformationBlock(byte[] data, int offset)
- {
- // TODO: Implement LE/LX information block parsing
- return null;
+ // Create a memory stream and parse that
+ MemoryStream dataStream = new MemoryStream(data);
+ dataStream.Position = offset;
+ return ParseExecutable(dataStream);
}
#endregion
diff --git a/BurnOutSharp.Builder/MSDOS.cs b/BurnOutSharp.Builder/MSDOS.cs
index 8c7c46ed..9bdc064a 100644
--- a/BurnOutSharp.Builder/MSDOS.cs
+++ b/BurnOutSharp.Builder/MSDOS.cs
@@ -4,7 +4,6 @@ using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builder
{
- // TODO: Make Stream Data rely on Byte Data
public static class MSDOS
{
#region Byte Data
@@ -25,128 +24,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
- // Cache the current offset
- int initialOffset = offset;
-
- // Create a new executable to fill
- var executable = new Executable();
-
- #region Executable Header
-
- // Try to parse the executable header
- var executableHeader = ParseExecutableHeader(data, offset);
- if (executableHeader == null)
- return null;
-
- // Set the executable header
- executable.Header = executableHeader;
-
- #endregion
-
- #region Relocation Table
-
- // If the offset for the relocation table doesn't exist
- int tableAddress = initialOffset + executableHeader.RelocationTableAddr;
- if (tableAddress >= data.Length)
- return executable;
-
- // Try to parse the relocation table
- var relocationTable = ParseRelocationTable(data, tableAddress, executableHeader.RelocationItems);
- if (relocationTable == null)
- return null;
-
- // Set the relocation table
- executable.RelocationTable = relocationTable;
-
- #endregion
-
- // Return the executable
- return executable;
- }
-
- ///
- /// Parse a byte array into an MS-DOS executable header
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled executable header on success, null on error
- private static ExecutableHeader ParseExecutableHeader(byte[] data, int offset)
- {
- // TODO: Use marshalling here instead of building
- var header = new ExecutableHeader();
-
- #region Standard Fields
-
- header.Magic = new byte[2];
- for (int i = 0; i < header.Magic.Length; i++)
- {
- header.Magic[i] = data.ReadByte(ref offset);
- }
- if (header.Magic[0] != 'M' || header.Magic[1] != 'Z')
- return null;
-
- header.LastPageBytes = data.ReadUInt16(ref offset);
- header.Pages = data.ReadUInt16(ref offset);
- header.RelocationItems = data.ReadUInt16(ref offset);
- header.HeaderParagraphSize = data.ReadUInt16(ref offset);
- header.MinimumExtraParagraphs = data.ReadUInt16(ref offset);
- header.MaximumExtraParagraphs = data.ReadUInt16(ref offset);
- header.InitialSSValue = data.ReadUInt16(ref offset);
- header.InitialSPValue = data.ReadUInt16(ref offset);
- header.Checksum = data.ReadUInt16(ref offset);
- header.InitialIPValue = data.ReadUInt16(ref offset);
- header.InitialCSValue = data.ReadUInt16(ref offset);
- header.RelocationTableAddr = data.ReadUInt16(ref offset);
- header.OverlayNumber = data.ReadUInt16(ref offset);
-
- #endregion
-
- // If we don't have enough data for PE extensions
- if (offset >= data.Length || data.Length - offset < 36)
- return header;
-
- #region PE Extensions
-
- header.Reserved1 = new ushort[4];
- for (int i = 0; i < header.Reserved1.Length; i++)
- {
- header.Reserved1[i] = data.ReadUInt16(ref offset);
- }
- header.OEMIdentifier = data.ReadUInt16(ref offset);
- header.OEMInformation = data.ReadUInt16(ref offset);
- header.Reserved2 = new ushort[10];
- for (int i = 0; i < header.Reserved2.Length; i++)
- {
- header.Reserved2[i] = data.ReadUInt16(ref offset);
- }
- header.NewExeHeaderAddr = data.ReadUInt32(ref offset);
-
- #endregion
-
- return header;
- }
-
- ///
- /// Parse a byte array into a relocation table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Number of relocation table entries to read
- /// Filled relocation table on success, null on error
- private static RelocationEntry[] ParseRelocationTable(byte[] data, int offset, int count)
- {
- // TODO: Use marshalling here instead of building
- var relocationTable = new RelocationEntry[count];
-
- for (int i = 0; i < count; i++)
- {
- var entry = new RelocationEntry();
- entry.Offset = data.ReadUInt16(ref offset);
- entry.Segment = data.ReadUInt16(ref offset);
- relocationTable[i] = entry;
- }
-
- return relocationTable;
+ // Create a memory stream and parse that
+ MemoryStream dataStream = new MemoryStream(data);
+ dataStream.Position = offset;
+ return ParseExecutable(dataStream);
}
#endregion
diff --git a/BurnOutSharp.Builder/MicrosoftCabinet.cs b/BurnOutSharp.Builder/MicrosoftCabinet.cs
index 26f3459e..43541d71 100644
--- a/BurnOutSharp.Builder/MicrosoftCabinet.cs
+++ b/BurnOutSharp.Builder/MicrosoftCabinet.cs
@@ -7,7 +7,6 @@ using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builder
{
// TODO: Add multi-cabinet reading
- // TODO: Make Stream Data rely on Byte Data
public class MicrosoftCabinet
{
#region Constants
@@ -72,234 +71,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
- // Cache the current offset
- int initialOffset = offset;
-
- // Create a new cabinet to fill
- var cabinet = new Cabinet();
-
- #region Cabinet Header
-
- // Try to parse the cabinet header
- var cabinetHeader = ParseCabinetHeader(data, ref offset);
- if (cabinetHeader == null)
- return null;
-
- // Set the cabinet header
- cabinet.Header = cabinetHeader;
-
- #endregion
-
- #region Folders
-
- // Set the folder array
- cabinet.Folders = new CFFOLDER[cabinetHeader.FolderCount];
-
- // Try to parse each folder, if we have any
- for (int i = 0; i < cabinetHeader.FolderCount; i++)
- {
- var folder = ParseFolder(data, ref offset, cabinetHeader, initialOffset);
- if (folder == null)
- return null;
-
- // Set the folder
- cabinet.Folders[i] = folder;
- }
-
- #endregion
-
- #region Files
-
- // Get the files offset
- int filesOffset = (int)cabinetHeader.FilesOffset + initialOffset;
- if (filesOffset > data.Length)
- return null;
-
- // Set the file array
- cabinet.Files = new CFFILE[cabinetHeader.FileCount];
-
- // Try to parse each file, if we have any
- for (int i = 0; i < cabinetHeader.FileCount; i++)
- {
- var file = ParseFile(data, ref filesOffset);
- if (file == null)
- return null;
-
- // Set the file
- cabinet.Files[i] = file;
- }
-
- #endregion
-
- return cabinet;
- }
-
- ///
- /// Parse a byte array into a cabinet header
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled cabinet header on success, null on error
- private static CFHEADER ParseCabinetHeader(byte[] data, ref int offset)
- {
- // TODO: Use marshalling here instead of building
- CFHEADER header = new CFHEADER();
-
- header.Signature = data.ReadUInt32(ref offset);
- if (header.Signature != SignatureValue)
- return null;
-
- header.Reserved1 = data.ReadUInt32(ref offset);
- if (header.Reserved1 != 0x00000000)
- return null;
-
- header.CabinetSize = data.ReadUInt32(ref offset);
- if (header.CabinetSize > MaximumCabSize)
- return null;
-
- header.Reserved2 = data.ReadUInt32(ref offset);
- if (header.Reserved2 != 0x00000000)
- return null;
-
- header.FilesOffset = data.ReadUInt32(ref offset);
-
- header.Reserved3 = data.ReadUInt32(ref offset);
- if (header.Reserved3 != 0x00000000)
- return null;
-
- header.VersionMinor = data.ReadByte(ref offset);
- header.VersionMajor = data.ReadByte(ref offset);
- if (header.VersionMajor != 0x00000001 || header.VersionMinor != 0x00000003)
- return null;
-
- header.FolderCount = data.ReadUInt16(ref offset);
- if (header.FolderCount > MaximumFolderCount)
- return null;
-
- header.FileCount = data.ReadUInt16(ref offset);
- if (header.FileCount > MaximumFileCount)
- return null;
-
- header.Flags = (HeaderFlags)data.ReadUInt16(ref offset);
- header.SetID = data.ReadUInt16(ref offset);
- header.CabinetIndex = data.ReadUInt16(ref offset);
-
- if (header.Flags.HasFlag(HeaderFlags.RESERVE_PRESENT))
- {
- header.HeaderReservedSize = data.ReadUInt16(ref offset);
- if (header.HeaderReservedSize > 60_000)
- return null;
-
- header.FolderReservedSize = data.ReadByte(ref offset);
- header.DataReservedSize = data.ReadByte(ref offset);
-
- if (header.HeaderReservedSize > 0)
- header.ReservedData = data.ReadBytes(ref offset, header.HeaderReservedSize);
- }
-
- if (header.Flags.HasFlag(HeaderFlags.PREV_CABINET))
- {
- header.CabinetPrev = data.ReadString(ref offset, Encoding.ASCII);
- header.DiskPrev = data.ReadString(ref offset, Encoding.ASCII);
- }
-
- if (header.Flags.HasFlag(HeaderFlags.NEXT_CABINET))
- {
- header.CabinetNext = data.ReadString(ref offset, Encoding.ASCII);
- header.DiskNext = data.ReadString(ref offset, Encoding.ASCII);
- }
-
- return header;
- }
-
- ///
- /// Parse a byte array into a folder
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Cabinet header to get flags and sizes from
- /// Initial offset for calculations
- /// Filled folder on success, null on error
- private static CFFOLDER ParseFolder(byte[] data, ref int offset, CFHEADER header, int initialOffset)
- {
- // TODO: Use marshalling here instead of building
- CFFOLDER folder = new CFFOLDER();
-
- folder.CabStartOffset = data.ReadUInt32(ref offset);
- folder.DataCount = data.ReadUInt16(ref offset);
- folder.CompressionType = (CompressionType)data.ReadUInt16(ref offset);
-
- if (header.FolderReservedSize > 0)
- folder.ReservedData = data.ReadBytes(ref offset, header.FolderReservedSize);
-
- if (folder.CabStartOffset > 0)
- {
- int blockPtr = initialOffset + (int)folder.CabStartOffset;
-
- folder.DataBlocks = new Dictionary();
- for (int i = 0; i < folder.DataCount; i++)
- {
- int dataStart = blockPtr;
- CFDATA dataBlock = ParseDataBlock(data, ref blockPtr, header.DataReservedSize);
- folder.DataBlocks[dataStart] = dataBlock;
- }
- }
-
- return folder;
- }
-
- ///
- /// Parse a byte array into a data block
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Reserved byte size for data blocks
- /// Filled folder on success, null on error
- private static CFDATA ParseDataBlock(byte[] data, ref int offset, byte dataReservedSize)
- {
- // TODO: Use marshalling here instead of building
- CFDATA dataBlock = new CFDATA();
-
- dataBlock.Checksum = data.ReadUInt32(ref offset);
- dataBlock.CompressedSize = data.ReadUInt16(ref offset);
- dataBlock.UncompressedSize = data.ReadUInt16(ref offset);
-
- if (dataBlock.UncompressedSize != 0 && dataBlock.CompressedSize > dataBlock.UncompressedSize)
- return null;
-
- if (dataReservedSize > 0)
- dataBlock.ReservedData = data.ReadBytes(ref offset, dataReservedSize);
-
- if (dataBlock.CompressedSize > 0)
- dataBlock.CompressedData = data.ReadBytes(ref offset, dataBlock.CompressedSize);
-
- return dataBlock;
- }
-
- ///
- /// Parse a byte array into a file
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled file on success, null on error
- private static CFFILE ParseFile(byte[] data, ref int offset)
- {
- // TODO: Use marshalling here instead of building
- CFFILE file = new CFFILE();
-
- file.FileSize = data.ReadUInt32(ref offset);
- file.FolderStartOffset = data.ReadUInt32(ref offset);
- file.FolderIndex = (FolderIndex)data.ReadUInt16(ref offset);
- file.Date = data.ReadUInt16(ref offset);
- file.Time = data.ReadUInt16(ref offset);
- file.Attributes = (Models.MicrosoftCabinet.FileAttributes)data.ReadUInt16(ref offset);
-
- if (file.Attributes.HasFlag(Models.MicrosoftCabinet.FileAttributes.NAME_IS_UTF))
- file.Name = data.ReadString(ref offset, Encoding.Unicode);
- else
- file.Name = data.ReadString(ref offset, Encoding.ASCII);
-
- return file;
+ // Create a memory stream and parse that
+ MemoryStream dataStream = new MemoryStream(data);
+ dataStream.Position = offset;
+ return ParseCabinet(dataStream);
}
#endregion
diff --git a/BurnOutSharp.Builder/MoPaQ.cs b/BurnOutSharp.Builder/MoPaQ.cs
index bcdb2645..7b9a6ae9 100644
--- a/BurnOutSharp.Builder/MoPaQ.cs
+++ b/BurnOutSharp.Builder/MoPaQ.cs
@@ -6,7 +6,6 @@ using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builder
{
- // TODO: Make Stream Data rely on Byte Data
public class MoPaQ
{
#region Constants
@@ -225,519 +224,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
- // Cache the current offset
- int initialOffset = offset;
-
- // Create a new archive to fill
- var archive = new Archive();
-
- #region User Data
-
- // Check for User Data
- uint possibleSignature = BitConverter.ToUInt32(data, offset);
- if (possibleSignature == UserDataSignatureValue)
- {
- // Save the current position for offset correction
- int basePtr = offset;
-
- // Deserialize the user data, returning null if invalid
- var userData = ParseUserData(data, ref offset);
- if (userData == null)
- return null;
-
- // Set the user data
- archive.UserData = userData;
-
- // Set the starting position according to the header offset
- offset = basePtr + (int)archive.UserData.HeaderOffset;
- }
-
- #endregion
-
- #region Archive Header
-
- // Check for the Header
- possibleSignature = BitConverter.ToUInt32(data, offset);
- if (possibleSignature == ArchiveHeaderSignatureValue)
- {
- // Try to parse the archive header
- var archiveHeader = ParseArchiveHeader(data, ref offset);
- if (archiveHeader == null)
- return null;
-
- // Set the archive header
- archive.ArchiveHeader = archiveHeader;
- }
-
- #endregion
-
- #region Hash Table
-
- // Version 1
- if (archive.ArchiveHeader.FormatVersion == 0)
- {
- // If we have a hash table
- long hashTableOffset = archive.ArchiveHeader.HashTablePosition;
- if (hashTableOffset != 0)
- {
- // Find the ending offset based on size
- long hashTableEnd = hashTableOffset + archive.ArchiveHeader.HashTableSize;
-
- // Read in the hash table
- var hashTable = new List();
-
- while (hashTableOffset < hashTableEnd)
- {
- var hashEntry = ParseHashEntry(data, ref hashTableOffset);
- if (hashEntry == null)
- return null;
-
- hashTable.Add(hashEntry);
- }
-
- archive.HashTable = hashTable.ToArray();
- }
- }
-
- // Version 2 and 3
- else if (archive.ArchiveHeader.FormatVersion == 1 || archive.ArchiveHeader.FormatVersion == 2)
- {
- // If we have a hash table
- long hashTableOffset = ((uint)archive.ArchiveHeader.HashTablePositionHi << 23) | archive.ArchiveHeader.HashTablePosition;
- if (hashTableOffset != 0)
- {
- // Find the ending offset based on size
- long hashTableEnd = hashTableOffset + archive.ArchiveHeader.HashTableSize;
-
- // Read in the hash table
- var hashTable = new List();
-
- while (hashTableOffset < hashTableEnd)
- {
- var hashEntry = ParseHashEntry(data, ref hashTableOffset);
- if (hashEntry == null)
- return null;
-
- hashTable.Add(hashEntry);
- }
-
- archive.HashTable = hashTable.ToArray();
- }
- }
-
- // Version 4
- else if (archive.ArchiveHeader.FormatVersion == 3)
- {
- // If we have a hash table
- long hashTableOffset = ((uint)archive.ArchiveHeader.HashTablePositionHi << 23) | archive.ArchiveHeader.HashTablePosition;
- if (hashTableOffset != 0)
- {
- // Find the ending offset based on size
- long hashTableEnd = hashTableOffset + (long)archive.ArchiveHeader.HashTableSizeLong;
-
- // Read in the hash table
- var hashTable = new List();
-
- while (hashTableOffset < hashTableEnd)
- {
- var hashEntry = ParseHashEntry(data, ref hashTableOffset);
- if (hashEntry == null)
- return null;
-
- hashTable.Add(hashEntry);
- }
-
- archive.HashTable = hashTable.ToArray();
- }
- }
-
- #endregion
-
- #region Block Table
-
- // Version 1
- if (archive.ArchiveHeader.FormatVersion == 0)
- {
- // If we have a block table
- long blockTableOffset = archive.ArchiveHeader.BlockTablePosition;
- if (blockTableOffset != 0)
- {
- // Find the ending offset based on size
- long blockTableEnd = blockTableOffset + archive.ArchiveHeader.BlockTableSize;
-
- // Read in the block table
- var blockTable = new List();
-
- while (blockTableOffset < blockTableEnd)
- {
- var blockEntry = ParseBlockEntry(data, ref blockTableOffset);
- if (blockEntry == null)
- return null;
-
- blockTable.Add(blockEntry);
- }
-
- archive.BlockTable = blockTable.ToArray();
- }
- }
-
- // Version 2 and 3
- else if (archive.ArchiveHeader.FormatVersion == 1 || archive.ArchiveHeader.FormatVersion == 2)
- {
- // If we have a block table
- long blockTableOffset = ((uint)archive.ArchiveHeader.BlockTablePositionHi << 23) | archive.ArchiveHeader.BlockTablePosition;
- if (blockTableOffset != 0)
- {
- // Find the ending offset based on size
- long blockTableEnd = blockTableOffset + archive.ArchiveHeader.BlockTableSize;
-
- // Read in the block table
- var blockTable = new List();
-
- while (blockTableOffset < blockTableEnd)
- {
- var blockEntry = ParseBlockEntry(data, ref blockTableOffset);
- if (blockEntry == null)
- return null;
-
- blockTable.Add(blockEntry);
- }
-
- archive.BlockTable = blockTable.ToArray();
- }
- }
-
- // Version 4
- else if (archive.ArchiveHeader.FormatVersion == 3)
- {
- // If we have a block table
- long blockTableOffset = ((uint)archive.ArchiveHeader.BlockTablePositionHi << 23) | archive.ArchiveHeader.BlockTablePosition;
- if (blockTableOffset != 0)
- {
- // Find the ending offset based on size
- long blockTableEnd = blockTableOffset + (long)archive.ArchiveHeader.BlockTableSizeLong;
-
- // Read in the block table
- var blockTable = new List();
-
- while (blockTableOffset < blockTableEnd)
- {
- var blockEntry = ParseBlockEntry(data, ref blockTableOffset);
- if (blockEntry == null)
- return null;
-
- blockTable.Add(blockEntry);
- }
-
- archive.BlockTable = blockTable.ToArray();
- }
- }
-
- #endregion
-
- #region Hi-Block Table
-
- // Version 2, 3, and 4
- if (archive.ArchiveHeader.FormatVersion == 1
- || archive.ArchiveHeader.FormatVersion == 2
- || archive.ArchiveHeader.FormatVersion == 3)
- {
- // If we have a hi-block table
- int hiBlockTableOffset = (int)archive.ArchiveHeader.HiBlockTablePosition;
- if (hiBlockTableOffset != 0)
- {
- // Read in the hi-block table
- var hiBlockTable = new List();
-
- for (int i = 0; i < archive.BlockTable.Length; i++)
- {
- short hiBlockEntry = data.ReadInt16(ref hiBlockTableOffset);
- hiBlockTable.Add(hiBlockEntry);
- }
-
- archive.HiBlockTable = hiBlockTable.ToArray();
- }
- }
-
- #endregion
-
- #region BET Table
-
- // Version 3 and 4
- if (archive.ArchiveHeader.FormatVersion == 2 || archive.ArchiveHeader.FormatVersion == 3)
- {
- // If we have a BET table
- int betTableOffset = (int)archive.ArchiveHeader.BetTablePosition;
- if (betTableOffset != 0)
- {
- // Read in the BET table
- var betTable = ParseBetTable(data, ref betTableOffset);
- if (betTable == null)
- return null;
-
- archive.BetTable = betTable;
- }
- }
-
- #endregion
-
- #region HET Table
-
- // Version 3 and 4
- if (archive.ArchiveHeader.FormatVersion == 2 || archive.ArchiveHeader.FormatVersion == 3)
- {
- // If we have a HET table
- int hetTableOffset = (int)archive.ArchiveHeader.HetTablePosition;
- if (hetTableOffset != 0)
- {
- // Read in the HET table
- var hetTable = ParseHetTable(data, ref hetTableOffset);
- if (hetTable == null)
- return null;
-
- archive.HetTable = hetTable;
- }
- }
-
- #endregion
-
- return archive;
- }
-
- ///
- /// Parse a byte array into a archive header
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled archive header on success, null on error
- private static ArchiveHeader ParseArchiveHeader(byte[] data, ref int offset)
- {
- // TODO: Use marshalling here instead of building
- ArchiveHeader archiveHeader = new ArchiveHeader();
-
- // V1 - Common
- archiveHeader.Signature = data.ReadUInt32(ref offset);
- if (archiveHeader.Signature != ArchiveHeaderSignatureValue)
- return null;
-
- archiveHeader.HeaderSize = data.ReadUInt32(ref offset);
- archiveHeader.ArchiveSize = data.ReadUInt32(ref offset);
- archiveHeader.FormatVersion = data.ReadUInt16(ref offset);
- archiveHeader.BlockSize = data.ReadUInt16(ref offset);
- archiveHeader.HashTablePosition = data.ReadUInt32(ref offset);
- archiveHeader.BlockTablePosition = data.ReadUInt32(ref offset);
- archiveHeader.HashTableSize = data.ReadUInt32(ref offset);
- archiveHeader.BlockTableSize = data.ReadUInt32(ref offset);
-
- // V2
- if (archiveHeader.FormatVersion >= 2 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion2Size)
- {
- archiveHeader.HiBlockTablePosition = data.ReadUInt64(ref offset);
- archiveHeader.HashTablePositionHi = data.ReadUInt16(ref offset);
- archiveHeader.BlockTablePositionHi = data.ReadUInt16(ref offset);
- }
-
- // V3
- if (archiveHeader.FormatVersion >= 3 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion3Size)
- {
- archiveHeader.ArchiveSizeLong = data.ReadUInt64(ref offset);
- archiveHeader.BetTablePosition = data.ReadUInt64(ref offset);
- archiveHeader.HetTablePosition = data.ReadUInt64(ref offset);
- }
-
- // V4
- if (archiveHeader.FormatVersion >= 4 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion4Size)
- {
- archiveHeader.HashTableSizeLong = data.ReadUInt64(ref offset);
- archiveHeader.BlockTableSizeLong = data.ReadUInt64(ref offset);
- archiveHeader.HiBlockTableSize = data.ReadUInt64(ref offset);
- archiveHeader.HetTableSize = data.ReadUInt64(ref offset);
- archiveHeader.BetTablesize = data.ReadUInt64(ref offset);
- archiveHeader.RawChunkSize = data.ReadUInt32(ref offset);
-
- archiveHeader.BlockTableMD5 = data.ReadBytes(ref offset, 0x10);
- archiveHeader.HashTableMD5 = data.ReadBytes(ref offset, 0x10);
- archiveHeader.HiBlockTableMD5 = data.ReadBytes(ref offset, 0x10);
- archiveHeader.BetTableMD5 = data.ReadBytes(ref offset, 0x10);
- archiveHeader.HetTableMD5 = data.ReadBytes(ref offset, 0x10);
- archiveHeader.HetTableMD5 = data.ReadBytes(ref offset, 0x10);
- }
-
- return archiveHeader;
- }
-
- ///
- /// Parse a byte array into a user data object
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled user data on success, null on error
- private static UserData ParseUserData(byte[] data, ref int offset)
- {
- // TODO: Use marshalling here instead of building
- UserData userData = new UserData();
-
- userData.Signature = data.ReadUInt32(ref offset);
- if (userData.Signature != UserDataSignatureValue)
- return null;
-
- userData.UserDataSize = data.ReadUInt32(ref offset);
- userData.HeaderOffset = data.ReadUInt32(ref offset);
- userData.UserDataHeaderSize = data.ReadUInt32(ref offset);
-
- return userData;
- }
-
- ///
- /// Parse a byte array into a HET table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled HET table on success, null on error
- private static HetTable ParseHetTable(byte[] data, ref int offset)
- {
- // TODO: Use marshalling here instead of building
- HetTable hetTable = new HetTable();
-
- // Common Headers
- hetTable.Signature = data.ReadUInt32(ref offset);
- if (hetTable.Signature != HetTableSignatureValue)
- return null;
-
- hetTable.Version = data.ReadUInt32(ref offset);
- hetTable.DataSize = data.ReadUInt32(ref offset);
-
- // HET-Specific
- hetTable.TableSize = data.ReadUInt32(ref offset);
- hetTable.MaxFileCount = data.ReadUInt32(ref offset);
- hetTable.HashTableSize = data.ReadUInt32(ref offset);
- hetTable.TotalIndexSize = data.ReadUInt32(ref offset);
- hetTable.IndexSizeExtra = data.ReadUInt32(ref offset);
- hetTable.IndexSize = data.ReadUInt32(ref offset);
- hetTable.BlockTableSize = data.ReadUInt32(ref offset);
- hetTable.HashTable = data.ReadBytes(ref offset, (int)hetTable.HashTableSize);
-
- // TODO: Populate the file indexes array
- hetTable.FileIndexes = new byte[(int)hetTable.HashTableSize][];
-
- return hetTable;
- }
-
- ///
- /// Parse a byte array into a BET table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled BET table on success, null on error
- private static BetTable ParseBetTable(byte[] data, ref int offset)
- {
- // TODO: Use marshalling here instead of building
- BetTable betTable = new BetTable();
-
- // Common Headers
- betTable.Signature = data.ReadUInt32(ref offset);
- if (betTable.Signature != BetTableSignatureValue)
- return null;
-
- betTable.Version = data.ReadUInt32(ref offset);
- betTable.DataSize = data.ReadUInt32(ref offset);
-
- // BET-Specific
- betTable.TableSize = data.ReadUInt32(ref offset);
- betTable.FileCount = data.ReadUInt32(ref offset);
- betTable.Unknown = data.ReadUInt32(ref offset);
- betTable.TableEntrySize = data.ReadUInt32(ref offset);
-
- betTable.FilePositionBitIndex = data.ReadUInt32(ref offset);
- betTable.FileSizeBitIndex = data.ReadUInt32(ref offset);
- betTable.CompressedSizeBitIndex = data.ReadUInt32(ref offset);
- betTable.FlagIndexBitIndex = data.ReadUInt32(ref offset);
- betTable.UnknownBitIndex = data.ReadUInt32(ref offset);
-
- betTable.FilePositionBitCount = data.ReadUInt32(ref offset);
- betTable.FileSizeBitCount = data.ReadUInt32(ref offset);
- betTable.CompressedSizeBitCount = data.ReadUInt32(ref offset);
- betTable.FlagIndexBitCount = data.ReadUInt32(ref offset);
- betTable.UnknownBitCount = data.ReadUInt32(ref offset);
-
- betTable.TotalBetHashSize = data.ReadUInt32(ref offset);
- betTable.BetHashSizeExtra = data.ReadUInt32(ref offset);
- betTable.BetHashSize = data.ReadUInt32(ref offset);
- betTable.BetHashArraySize = data.ReadUInt32(ref offset);
- betTable.FlagCount = data.ReadUInt32(ref offset);
-
- betTable.FlagsArray = new uint[betTable.FlagCount];
- Buffer.BlockCopy(data, offset, betTable.FlagsArray, 0, (int)betTable.FlagCount * 4);
- offset += (int)betTable.FlagCount * 4;
-
- // TODO: Populate the file table
- // TODO: Populate the hash table
-
- return betTable;
- }
-
- ///
- /// Parse a byte array into a hash entry
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled hash entry on success, null on error
- private static HashEntry ParseHashEntry(byte[] data, ref long offset)
- {
- // TODO: Use marshalling here instead of building
- HashEntry hashEntry = new HashEntry();
- int intOffset = (int)offset;
-
- hashEntry.NameHashPartA = data.ReadUInt32(ref intOffset);
- hashEntry.NameHashPartB = data.ReadUInt32(ref intOffset);
- hashEntry.Locale = (Locale)data.ReadUInt16(ref intOffset);
- hashEntry.Platform = data.ReadUInt16(ref intOffset);
- hashEntry.BlockIndex = data.ReadUInt32(ref intOffset);
-
- offset = intOffset;
- return hashEntry;
- }
-
- ///
- /// Parse a byte array into a block entry
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled block entry on success, null on error
- private static BlockEntry ParseBlockEntry(byte[] data, ref long offset)
- {
- // TODO: Use marshalling here instead of building
- BlockEntry blockEntry = new BlockEntry();
- int intOffset = (int)offset;
-
- blockEntry.FilePosition = data.ReadUInt32(ref intOffset);
- blockEntry.CompressedSize = data.ReadUInt32(ref intOffset);
- blockEntry.UncompressedSize = data.ReadUInt32(ref intOffset);
- blockEntry.Flags = (FileFlags)data.ReadUInt32(ref intOffset);
-
- offset = intOffset;
- return blockEntry;
- }
-
- ///
- /// Parse a byte array into a patch info
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled patch info on success, null on error
- private static PatchInfo ParsePatchInfo(byte[] data, ref int offset)
- {
- // TODO: Use marshalling here instead of building
- PatchInfo patchInfo = new PatchInfo();
-
- patchInfo.Length = data.ReadUInt32(ref offset);
- patchInfo.Flags = data.ReadUInt32(ref offset);
- patchInfo.DataSize = data.ReadUInt32(ref offset);
- patchInfo.MD5 = data.ReadBytes(ref offset, 0x10);
-
- // TODO: Fill the sector offset table
-
- return patchInfo;
+ // Create a memory stream and parse that
+ MemoryStream dataStream = new MemoryStream(data);
+ dataStream.Position = offset;
+ return ParseArchive(dataStream);
}
#endregion
diff --git a/BurnOutSharp.Builder/NewExecutable.cs b/BurnOutSharp.Builder/NewExecutable.cs
index ae1a6ea2..a9d05648 100644
--- a/BurnOutSharp.Builder/NewExecutable.cs
+++ b/BurnOutSharp.Builder/NewExecutable.cs
@@ -6,7 +6,6 @@ using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builder
{
- // TODO: Make Stream Data rely on Byte Data
public static class NewExecutable
{
#region Byte Data
@@ -27,458 +26,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
- // Cache the current offset
- int initialOffset = offset;
-
- // Create a new executable to fill
- var executable = new Executable();
-
- #region MS-DOS Stub
-
- // Parse the MS-DOS stub
- var stub = MSDOS.ParseExecutable(data, offset);
- if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
- return null;
-
- // Set the MS-DOS stub
- executable.Stub = stub;
-
- #endregion
-
- #region Executable Header
-
- // Try to parse the executable header
- offset = (int)(initialOffset + stub.Header.NewExeHeaderAddr);
- var executableHeader = ParseExecutableHeader(data, offset);
- if (executableHeader == null)
- return null;
-
- // Set the executable header
- executable.Header = executableHeader;
-
- #endregion
-
- #region Segment Table
-
- // If the offset for the segment table doesn't exist
- int tableAddress = initialOffset
- + (int)stub.Header.NewExeHeaderAddr
- + executableHeader.SegmentTableOffset;
- if (tableAddress >= data.Length)
- return executable;
-
- // Try to parse the segment table
- var segmentTable = ParseSegmentTable(data, tableAddress, executableHeader.FileSegmentCount);
- if (segmentTable == null)
- return null;
-
- // Set the segment table
- executable.SegmentTable = segmentTable;
-
- #endregion
-
- #region Resource Table
-
- // If the offset for the segment table doesn't exist
- tableAddress = initialOffset
- + (int)stub.Header.NewExeHeaderAddr
- + executableHeader.SegmentTableOffset;
- if (tableAddress >= data.Length)
- return executable;
-
- // Try to parse the resource table
- var resourceTable = ParseResourceTable(data, tableAddress, executableHeader.ResourceEntriesCount);
- if (resourceTable == null)
- return null;
-
- // Set the resource table
- executable.ResourceTable = resourceTable;
-
- #endregion
-
- #region Resident-Name Table
-
- // If the offset for the resident-name table doesn't exist
- tableAddress = initialOffset
- + (int)stub.Header.NewExeHeaderAddr
- + executableHeader.ResidentNameTableOffset;
- int endOffset = initialOffset
- + (int)stub.Header.NewExeHeaderAddr
- + executableHeader.ModuleReferenceTableOffset;
- if (tableAddress >= data.Length)
- return executable;
-
- // Try to parse the resident-name table
- var residentNameTable = ParseResidentNameTable(data, tableAddress, endOffset);
- if (residentNameTable == null)
- return null;
-
- // Set the resident-name table
- executable.ResidentNameTable = residentNameTable;
-
- #endregion
-
- #region Module-Reference Table
-
- // If the offset for the module-reference table doesn't exist
- tableAddress = initialOffset
- + (int)stub.Header.NewExeHeaderAddr
- + executableHeader.ModuleReferenceTableOffset;
- if (tableAddress >= data.Length)
- return executable;
-
- // Try to parse the module-reference table
- var moduleReferenceTable = ParseModuleReferenceTable(data, tableAddress, executableHeader.ModuleReferenceTableSize);
- if (moduleReferenceTable == null)
- return null;
-
- // Set the module-reference table
- executable.ModuleReferenceTable = moduleReferenceTable;
-
- #endregion
-
- #region Imported-Name Table
-
- // If the offset for the imported-name table doesn't exist
- tableAddress = initialOffset
- + (int)stub.Header.NewExeHeaderAddr
- + executableHeader.ImportedNamesTableOffset;
- endOffset = initialOffset
- + (int)stub.Header.NewExeHeaderAddr
- + executableHeader.EntryTableOffset;
- if (tableAddress >= data.Length)
- return executable;
-
- // Try to parse the imported-name table
- var importedNameTable = ParseImportedNameTable(data, tableAddress, endOffset);
- if (importedNameTable == null)
- return null;
-
- // Set the imported-name table
- executable.ImportedNameTable = importedNameTable;
-
- #endregion
-
- #region Entry Table
-
- // If the offset for the entry table doesn't exist
- tableAddress = initialOffset
- + (int)stub.Header.NewExeHeaderAddr
- + executableHeader.EntryTableOffset;
- endOffset = initialOffset
- + (int)stub.Header.NewExeHeaderAddr
- + executableHeader.EntryTableOffset
- + executableHeader.EntryTableSize;
- if (tableAddress >= data.Length)
- return executable;
-
- // Try to parse the entry table
- var entryTable = ParseEntryTable(data, tableAddress, endOffset);
- if (entryTable == null)
- return null;
-
- // Set the entry table
- executable.EntryTable = entryTable;
-
- #endregion
-
- #region Nonresident-Name Table
-
- // If the offset for the nonresident-name table doesn't exist
- tableAddress = initialOffset
- + (int)executableHeader.NonResidentNamesTableOffset;
- endOffset = initialOffset
- + (int)executableHeader.NonResidentNamesTableOffset
- + executableHeader.NonResidentNameTableSize;
- if (tableAddress >= data.Length)
- return executable;
-
- // Try to parse the nonresident-name table
- var nonResidentNameTable = ParseNonResidentNameTable(data, tableAddress, endOffset);
- if (nonResidentNameTable == null)
- return null;
-
- // Set the nonresident-name table
- executable.NonResidentNameTable = nonResidentNameTable;
-
- #endregion
-
- return executable;
- }
-
- ///
- /// Parse a byte array into a New Executable header
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled executable header on success, null on error
- private static ExecutableHeader ParseExecutableHeader(byte[] data, int offset)
- {
- // TODO: Use marshalling here instead of building
- var header = new ExecutableHeader();
-
- header.Magic = new byte[2];
- for (int i = 0; i < header.Magic.Length; i++)
- {
- header.Magic[i] = data.ReadByte(ref offset);
- }
- if (header.Magic[0] != 'N' || header.Magic[1] != 'E')
- return null;
-
- header.LinkerVersion = data.ReadByte(ref offset);
- header.LinkerRevision = data.ReadByte(ref offset);
- header.EntryTableOffset = data.ReadUInt16(ref offset);
- header.EntryTableSize = data.ReadUInt16(ref offset);
- header.CrcChecksum = data.ReadUInt32(ref offset);
- header.FlagWord = (HeaderFlag)data.ReadUInt16(ref offset);
- header.AutomaticDataSegmentNumber = data.ReadUInt16(ref offset);
- header.InitialHeapAlloc = data.ReadUInt16(ref offset);
- header.InitialStackAlloc = data.ReadUInt16(ref offset);
- header.InitialCSIPSetting = data.ReadUInt32(ref offset);
- header.InitialSSSPSetting = data.ReadUInt32(ref offset);
- header.FileSegmentCount = data.ReadUInt16(ref offset);
- header.ModuleReferenceTableSize = data.ReadUInt16(ref offset);
- header.NonResidentNameTableSize = data.ReadUInt16(ref offset);
- header.SegmentTableOffset = data.ReadUInt16(ref offset);
- header.ResourceTableOffset = data.ReadUInt16(ref offset);
- header.ResidentNameTableOffset = data.ReadUInt16(ref offset);
- header.ModuleReferenceTableOffset = data.ReadUInt16(ref offset);
- header.ImportedNamesTableOffset = data.ReadUInt16(ref offset);
- header.NonResidentNamesTableOffset = data.ReadUInt32(ref offset);
- header.MovableEntriesCount = data.ReadUInt16(ref offset);
- header.SegmentAlignmentShiftCount = data.ReadUInt16(ref offset);
- header.ResourceEntriesCount = data.ReadUInt16(ref offset);
- header.TargetOperatingSystem = (OperatingSystem)data.ReadByte(ref offset);
- header.AdditionalFlags = (OS2Flag)data.ReadByte(ref offset);
- header.ReturnThunkOffset = data.ReadUInt16(ref offset);
- header.SegmentReferenceThunkOffset = data.ReadUInt16(ref offset);
- header.MinCodeSwapAreaSize = data.ReadUInt16(ref offset);
- header.WindowsSDKRevision = data.ReadByte(ref offset);
- header.WindowsSDKVersion = data.ReadByte(ref offset);
-
- return header;
- }
-
- ///
- /// Parse a byte array into a segment table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Number of segment table entries to read
- /// Filled segment table on success, null on error
- private static SegmentTableEntry[] ParseSegmentTable(byte[] data, int offset, int count)
- {
- // TODO: Use marshalling here instead of building
- var segmentTable = new SegmentTableEntry[count];
-
- for (int i = 0; i < count; i++)
- {
- var entry = new SegmentTableEntry();
- entry.Offset = data.ReadUInt16(ref offset);
- entry.Length = data.ReadUInt16(ref offset);
- entry.FlagWord = (SegmentTableEntryFlag)data.ReadUInt16(ref offset);
- entry.MinimumAllocationSize = data.ReadUInt16(ref offset);
- segmentTable[i] = entry;
- }
-
- return segmentTable;
- }
-
- ///
- /// Parse a byte array into a resource table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Number of resource table entries to read
- /// Filled resource table on success, null on error
- private static ResourceTable ParseResourceTable(byte[] data, int offset, int count)
- {
- int initialOffset = offset;
-
- // TODO: Use marshalling here instead of building
- var resourceTable = new ResourceTable();
-
- resourceTable.AlignmentShiftCount = data.ReadUInt16(ref offset);
- resourceTable.ResourceTypes = new ResourceTypeInformationEntry[count];
- for (int i = 0; i < resourceTable.ResourceTypes.Length; i++)
- {
- var entry = new ResourceTypeInformationEntry();
- entry.TypeID = data.ReadUInt16(ref offset);
- entry.ResourceCount = data.ReadUInt16(ref offset);
- entry.Reserved = data.ReadUInt32(ref offset);
- entry.Resources = new ResourceTypeResourceEntry[entry.ResourceCount];
- for (int j = 0; j < entry.ResourceCount; j++)
- {
- // TODO: Should we read and store the resource data?
- var resource = new ResourceTypeResourceEntry();
- resource.Offset = data.ReadUInt16(ref offset);
- resource.Length = data.ReadUInt16(ref offset);
- resource.FlagWord = (ResourceTypeResourceFlag)data.ReadUInt16(ref offset);
- resource.ResourceID = data.ReadUInt16(ref offset);
- resource.Reserved = data.ReadUInt32(ref offset);
- entry.Resources[j] = resource;
- }
- resourceTable.ResourceTypes[i] = entry;
- }
-
- // Get the full list of unique string offsets
- var stringOffsets = resourceTable.ResourceTypes
- .Where(rt => rt.IsIntegerType() == false)
- .Select(rt => rt.TypeID)
- .Union(resourceTable.ResourceTypes
- .SelectMany(rt => rt.Resources)
- .Where(r => r.IsIntegerType() == false)
- .Select(r => r.ResourceID))
- .Distinct()
- .OrderBy(o => o)
- .ToList();
-
- // Populate the type and name string dictionary
- resourceTable.TypeAndNameStrings = new Dictionary();
- for (int i = 0; i < stringOffsets.Count; i++)
- {
- int stringOffset = stringOffsets[i] + initialOffset;
- var str = new ResourceTypeAndNameString();
- str.Length = data.ReadByte(ref stringOffset);
- str.Text = data.ReadBytes(ref stringOffset, str.Length);
- resourceTable.TypeAndNameStrings[stringOffsets[i]] = str;
- }
-
- return resourceTable;
- }
-
- ///
- /// Parse a byte array into a resident-name table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// First address not part of the resident-name table
- /// Filled resident-name table on success, null on error
- private static ResidentNameTableEntry[] ParseResidentNameTable(byte[] data, int offset, int endOffset)
- {
- // TODO: Use marshalling here instead of building
- var residentNameTable = new List();
-
- while (offset < endOffset)
- {
- var entry = new ResidentNameTableEntry();
- entry.Length = data.ReadByte(ref offset);
- entry.NameString = data.ReadBytes(ref offset, entry.Length);
- entry.OrdinalNumber = data.ReadUInt16(ref offset);
- residentNameTable.Add(entry);
- }
-
- return residentNameTable.ToArray();
- }
-
- ///
- /// Parse a byte array into a module-reference table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Number of module-reference table entries to read
- /// Filled module-reference table on success, null on error
- private static ModuleReferenceTableEntry[] ParseModuleReferenceTable(byte[] data, int offset, int count)
- {
- // TODO: Use marshalling here instead of building
- var moduleReferenceTable = new ModuleReferenceTableEntry[count];
-
- for (int i = 0; i < count; i++)
- {
- var entry = new ModuleReferenceTableEntry();
- entry.Offset = data.ReadUInt16(ref offset);
- moduleReferenceTable[i] = entry;
- }
-
- return moduleReferenceTable;
- }
-
- ///
- /// Parse a byte array into an imported-name table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// First address not part of the imported-name table
- /// Filled imported-name table on success, null on error
- private static Dictionary ParseImportedNameTable(byte[] data, int offset, int endOffset)
- {
- // TODO: Use marshalling here instead of building
- var importedNameTable = new Dictionary();
-
- while (offset < endOffset)
- {
- ushort currentOffset = (ushort)offset;
- var entry = new ImportedNameTableEntry();
- entry.Length = data.ReadByte(ref offset);
- entry.NameString = data.ReadBytes(ref offset, entry.Length);
- importedNameTable[currentOffset] = entry;
- }
-
- return importedNameTable;
- }
-
- ///
- /// Parse a byte array into an entry table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// First address not part of the entry table
- /// Filled entry table on success, null on error
- private static EntryTableBundle[] ParseEntryTable(byte[] data, int offset, int endOffset)
- {
- // TODO: Use marshalling here instead of building
- var entryTable = new List();
-
- while (offset < endOffset)
- {
- var entry = new EntryTableBundle();
- entry.EntryCount = data.ReadByte(ref offset);
- entry.SegmentIndicator = data.ReadByte(ref offset);
- switch (entry.GetEntryType())
- {
- case SegmentEntryType.Unused:
- break;
-
- case SegmentEntryType.FixedSegment:
- entry.FixedFlagWord = (FixedSegmentEntryFlag)data.ReadByte(ref offset);
- entry.FixedOffset = data.ReadUInt16(ref offset);
- break;
-
- case SegmentEntryType.MoveableSegment:
- entry.MoveableFlagWord = (MoveableSegmentEntryFlag)data.ReadByte(ref offset);
- entry.MoveableReserved = data.ReadUInt16(ref offset);
- entry.MoveableSegmentNumber = data.ReadByte(ref offset);
- entry.MoveableOffset = data.ReadUInt16(ref offset);
- break;
- }
- entryTable.Add(entry);
- }
-
- return entryTable.ToArray();
- }
-
- ///
- /// Parse a byte array into a nonresident-name table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// First address not part of the nonresident-name table
- /// Filled nonresident-name table on success, null on error
- private static NonResidentNameTableEntry[] ParseNonResidentNameTable(byte[] data, int offset, int endOffset)
- {
- // TODO: Use marshalling here instead of building
- var residentNameTable = new List();
-
- while (offset < endOffset)
- {
- var entry = new NonResidentNameTableEntry();
- entry.Length = data.ReadByte(ref offset);
- entry.NameString = data.ReadBytes(ref offset, entry.Length);
- entry.OrdinalNumber = data.ReadUInt16(ref offset);
- residentNameTable.Add(entry);
- }
-
- return residentNameTable.ToArray();
+ // Create a memory stream and parse that
+ MemoryStream dataStream = new MemoryStream(data);
+ dataStream.Position = offset;
+ return ParseExecutable(dataStream);
}
#endregion
diff --git a/BurnOutSharp.Builder/PortableExecutable.cs b/BurnOutSharp.Builder/PortableExecutable.cs
index 6a82309c..24bfcbdd 100644
--- a/BurnOutSharp.Builder/PortableExecutable.cs
+++ b/BurnOutSharp.Builder/PortableExecutable.cs
@@ -8,7 +8,6 @@ using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builder
{
- // TODO: Make Stream Data rely on Byte Data
public static class PortableExecutable
{
#region Byte Data
@@ -29,1227 +28,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
- // Cache the current offset
- int initialOffset = offset;
-
- // Create a new executable to fill
- var executable = new Executable();
-
- #region MS-DOS Stub
-
- // Parse the MS-DOS stub
- var stub = MSDOS.ParseExecutable(data, offset);
- if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
- return null;
-
- // Set the MS-DOS stub
- executable.Stub = stub;
-
- #endregion
-
- #region Signature
-
- offset = (int)(initialOffset + stub.Header.NewExeHeaderAddr);
- executable.Signature = new byte[4];
- for (int i = 0; i < executable.Signature.Length; i++)
- {
- executable.Signature[i] = data.ReadByte(ref offset);
- }
- if (executable.Signature[0] != 'P' || executable.Signature[1] != 'E' || executable.Signature[2] != '\0' || executable.Signature[3] != '\0')
- return null;
-
- #endregion
-
- #region COFF File Header
-
- // Try to parse the COFF file header
- var coffFileHeader = ParseCOFFFileHeader(data, ref offset);
- if (coffFileHeader == null)
- return null;
-
- // Set the COFF file header
- executable.COFFFileHeader = coffFileHeader;
-
- #endregion
-
- #region Optional Header
-
- // Try to parse the optional header
- var optionalHeader = ParseOptionalHeader(data, ref offset, coffFileHeader.SizeOfOptionalHeader);
- if (optionalHeader == null)
- return null;
-
- // Set the optional header
- executable.OptionalHeader = optionalHeader;
-
- #endregion
-
- #region Section Table
-
- // Try to parse the section table
- var sectionTable = ParseSectionTable(data, offset, coffFileHeader.NumberOfSections);
- if (sectionTable == null)
- return null;
-
- // Set the section table
- executable.SectionTable = sectionTable;
-
- #endregion
-
- #region COFF Symbol Table and COFF String Table
-
- // TODO: Validate that this is correct with an "old" PE
- if (coffFileHeader.PointerToSymbolTable.ConvertVirtualAddress(executable.SectionTable) != 0)
- {
- // If the offset for the COFF symbol table doesn't exist
- int coffSymbolTableAddress = initialOffset
- + (int)coffFileHeader.PointerToSymbolTable.ConvertVirtualAddress(executable.SectionTable);
- if (coffSymbolTableAddress >= data.Length)
- return executable;
-
- // Try to parse the COFF symbol table
- var coffSymbolTable = ParseCOFFSymbolTable(data, coffSymbolTableAddress, coffFileHeader.NumberOfSymbols);
- if (coffSymbolTable == null)
- return null;
-
- // If the offset for the COFF string table doesn't exist
- coffSymbolTableAddress = initialOffset
- + (int)coffFileHeader.PointerToSymbolTable.ConvertVirtualAddress(executable.SectionTable)
- + (coffSymbolTable.Length * 18 /* sizeof(COFFSymbolTableEntry) */);
- if (coffSymbolTableAddress >= data.Length)
- return executable;
-
- // Set the COFF symbol table
- executable.COFFSymbolTable = coffSymbolTable;
-
- // Try to parse the COFF string table
- var coffStringTable = ParseCOFFStringTable(data, coffSymbolTableAddress);
- if (coffStringTable == null)
- return null;
-
- // Set the COFF string table
- executable.COFFStringTable = coffStringTable;
- }
-
- #endregion
-
- #region Attribute Certificate Table
-
- if (optionalHeader.CertificateTable != null && optionalHeader.CertificateTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
- {
- // If the offset for the COFF symbol table doesn't exist
- int certificateTableAddress = initialOffset
- + (int)optionalHeader.CertificateTable.VirtualAddress;
- if (certificateTableAddress >= data.Length)
- return executable;
-
- // Try to parse the attribute certificate table
- int endOffset = (int)(certificateTableAddress + optionalHeader.CertificateTable.Size);
- var attributeCertificateTable = ParseAttributeCertificateTable(data, certificateTableAddress, endOffset);
- if (attributeCertificateTable == null)
- return null;
-
- // Set the attribute certificate table
- executable.AttributeCertificateTable = attributeCertificateTable;
- }
-
- #endregion
-
- #region Delay-Load Directory Table
-
- if (optionalHeader.DelayImportDescriptor != null && optionalHeader.DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
- {
- // If the offset for the delay-load directory table doesn't exist
- int delayLoadDirectoryTableAddress = initialOffset
- + (int)optionalHeader.DelayImportDescriptor.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
- if (delayLoadDirectoryTableAddress >= data.Length)
- return executable;
-
- // Try to parse the delay-load directory table
- var delayLoadDirectoryTable = ParseDelayLoadDirectoryTable(data, delayLoadDirectoryTableAddress);
- if (delayLoadDirectoryTable == null)
- return null;
-
- // Set the delay-load directory table
- executable.DelayLoadDirectoryTable = delayLoadDirectoryTable;
- }
-
- #endregion
-
- #region Base Relocation Table
-
- // Should also be in a '.reloc' section
- if (optionalHeader.BaseRelocationTable != null && optionalHeader.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
- {
- // If the offset for the base relocation table doesn't exist
- int baseRelocationTableAddress = initialOffset
- + (int)optionalHeader.BaseRelocationTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
- if (baseRelocationTableAddress >= data.Length)
- return executable;
-
- // Try to parse the base relocation table
- int endOffset = (int)(baseRelocationTableAddress + optionalHeader.BaseRelocationTable.Size);
- var baseRelocationTable = ParseBaseRelocationTable(data, baseRelocationTableAddress, endOffset, executable.SectionTable);
- if (baseRelocationTable == null)
- return null;
-
- // Set the base relocation table
- executable.BaseRelocationTable = baseRelocationTable;
- }
-
- #endregion
-
- #region Debug Table
-
- // Should also be in a '.debug' section
- if (optionalHeader.Debug != null && optionalHeader.Debug.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
- {
- // If the offset for the debug table doesn't exist
- int debugTableAddress = initialOffset
- + (int)optionalHeader.Debug.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
- if (debugTableAddress >= data.Length)
- return executable;
-
- // Try to parse the debug table
- int endOffset = (int)(debugTableAddress + optionalHeader.Debug.Size);
- var debugTable = ParseDebugTable(data, debugTableAddress, endOffset, executable.SectionTable);
- if (debugTable == null)
- return null;
-
- // Set the debug table
- executable.DebugTable = debugTable;
- }
-
- #endregion
-
- #region Export Table
-
- // Should also be in a '.edata' section
- if (optionalHeader.ExportTable != null && optionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
- {
- // If the offset for the export table doesn't exist
- int exportTableAddress = initialOffset
- + (int)optionalHeader.ExportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
- if (exportTableAddress >= data.Length)
- return executable;
-
- // Try to parse the export table
- var exportTable = ParseExportTable(data, exportTableAddress, executable.SectionTable);
- if (exportTable == null)
- return null;
-
- // Set the export table
- executable.ExportTable = exportTable;
- }
-
- #endregion
-
- #region Import Table
-
- // Should also be in a '.idata' section
- if (optionalHeader.ImportTable != null && optionalHeader.ImportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
- {
- // If the offset for the import table doesn't exist
- int importTableAddress = initialOffset
- + (int)optionalHeader.ImportTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
- if (importTableAddress >= data.Length)
- return executable;
-
- // Try to parse the import table
- var importTable = ParseImportTable(data, importTableAddress, optionalHeader.Magic, executable.SectionTable);
- if (importTable == null)
- return null;
-
- // Set the import table
- executable.ImportTable = importTable;
- }
-
- #endregion
-
- #region Resource Directory Table
-
- // Should also be in a '.rsrc' section
- if (optionalHeader.ResourceTable != null && optionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable) != 0)
- {
- // If the offset for the resource directory table doesn't exist
- int resourceTableAddress = initialOffset
- + (int)optionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(executable.SectionTable);
- if (resourceTableAddress >= data.Length)
- return executable;
-
- // Try to parse the resource directory table
- var resourceDirectoryTable = ParseResourceDirectoryTable(data, resourceTableAddress, resourceTableAddress, executable.SectionTable);
- if (resourceDirectoryTable == null)
- return null;
-
- // Set the resource directory table
- executable.ResourceDirectoryTable = resourceDirectoryTable;
- }
-
- #endregion
-
- // TODO: Finish implementing PE parsing
- return executable;
- }
-
- ///
- /// Parse a byte array into a Portable Executable COFF file header
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled COFF file header on success, null on error
- private static COFFFileHeader ParseCOFFFileHeader(byte[] data, ref int offset)
- {
- // TODO: Use marshalling here instead of building
- var fileHeader = new COFFFileHeader();
-
- fileHeader.Machine = (MachineType)data.ReadUInt16(ref offset);
- fileHeader.NumberOfSections = data.ReadUInt16(ref offset);
- fileHeader.TimeDateStamp = data.ReadUInt32(ref offset);
- fileHeader.PointerToSymbolTable = data.ReadUInt32(ref offset);
- fileHeader.NumberOfSymbols = data.ReadUInt32(ref offset);
- fileHeader.SizeOfOptionalHeader = data.ReadUInt16(ref offset);
- fileHeader.Characteristics = (Characteristics)data.ReadUInt16(ref offset);
-
- return fileHeader;
- }
-
- ///
- /// Parse a byte array into an optional header
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Size of the optional header
- /// Filled optional header on success, null on error
- private static OptionalHeader ParseOptionalHeader(byte[] data, ref int offset, int optionalSize)
- {
- int initialOffset = offset;
-
- // TODO: Use marshalling here instead of building
- var optionalHeader = new OptionalHeader();
-
- #region Standard Fields
-
- optionalHeader.Magic = (OptionalHeaderMagicNumber)data.ReadUInt16(ref offset);
- optionalHeader.MajorLinkerVersion = data.ReadByte(ref offset);
- optionalHeader.MinorLinkerVersion = data.ReadByte(ref offset);
- optionalHeader.SizeOfCode = data.ReadUInt32(ref offset);
- optionalHeader.SizeOfInitializedData = data.ReadUInt32(ref offset);
- optionalHeader.SizeOfUninitializedData = data.ReadUInt32(ref offset);
- optionalHeader.AddressOfEntryPoint = data.ReadUInt32(ref offset);
- optionalHeader.BaseOfCode = data.ReadUInt32(ref offset);
-
- if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
- optionalHeader.BaseOfData = data.ReadUInt32(ref offset);
-
- #endregion
-
- #region Windows-Specific Fields
-
- if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
- optionalHeader.ImageBase_PE32 = data.ReadUInt32(ref offset);
- else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus)
- optionalHeader.ImageBase_PE32Plus = data.ReadUInt64(ref offset);
- optionalHeader.SectionAlignment = data.ReadUInt32(ref offset);
- optionalHeader.FileAlignment = data.ReadUInt32(ref offset);
- optionalHeader.MajorOperatingSystemVersion = data.ReadUInt16(ref offset);
- optionalHeader.MinorOperatingSystemVersion = data.ReadUInt16(ref offset);
- optionalHeader.MajorImageVersion = data.ReadUInt16(ref offset);
- optionalHeader.MinorImageVersion = data.ReadUInt16(ref offset);
- optionalHeader.MajorSubsystemVersion = data.ReadUInt16(ref offset);
- optionalHeader.MinorSubsystemVersion = data.ReadUInt16(ref offset);
- optionalHeader.Win32VersionValue = data.ReadUInt32(ref offset);
- optionalHeader.SizeOfImage = data.ReadUInt32(ref offset);
- optionalHeader.SizeOfHeaders = data.ReadUInt32(ref offset);
- optionalHeader.CheckSum = data.ReadUInt32(ref offset);
- optionalHeader.Subsystem = (WindowsSubsystem)data.ReadUInt16(ref offset);
- optionalHeader.DllCharacteristics = (DllCharacteristics)data.ReadUInt16(ref offset);
- if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
- optionalHeader.SizeOfStackReserve_PE32 = data.ReadUInt32(ref offset);
- else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus)
- optionalHeader.SizeOfStackReserve_PE32Plus = data.ReadUInt64(ref offset);
- if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
- optionalHeader.SizeOfStackCommit_PE32 = data.ReadUInt32(ref offset);
- else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus)
- optionalHeader.SizeOfStackCommit_PE32Plus = data.ReadUInt64(ref offset);
- if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
- optionalHeader.SizeOfHeapReserve_PE32 = data.ReadUInt32(ref offset);
- else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus)
- optionalHeader.SizeOfHeapReserve_PE32Plus = data.ReadUInt64(ref offset);
- if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32)
- optionalHeader.SizeOfHeapCommit_PE32 = data.ReadUInt32(ref offset);
- else if (optionalHeader.Magic == OptionalHeaderMagicNumber.PE32Plus)
- optionalHeader.SizeOfHeapCommit_PE32Plus = data.ReadUInt64(ref offset);
- optionalHeader.LoaderFlags = data.ReadUInt32(ref offset);
- optionalHeader.NumberOfRvaAndSizes = data.ReadUInt32(ref offset);
-
- #endregion
-
- #region Data Directories
-
- if (optionalHeader.NumberOfRvaAndSizes >= 1 && offset - initialOffset < optionalSize)
- {
- optionalHeader.ExportTable = new DataDirectory();
- optionalHeader.ExportTable.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.ExportTable.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 2 && offset - initialOffset < optionalSize)
- {
- optionalHeader.ImportTable = new DataDirectory();
- optionalHeader.ImportTable.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.ImportTable.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 3 && offset - initialOffset < optionalSize)
- {
- optionalHeader.ResourceTable = new DataDirectory();
- optionalHeader.ResourceTable.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.ResourceTable.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 4 && offset - initialOffset < optionalSize)
- {
- optionalHeader.ExceptionTable = new DataDirectory();
- optionalHeader.ExceptionTable.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.ExceptionTable.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 5 && offset - initialOffset < optionalSize)
- {
- optionalHeader.CertificateTable = new DataDirectory();
- optionalHeader.CertificateTable.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.CertificateTable.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 6 && offset - initialOffset < optionalSize)
- {
- optionalHeader.BaseRelocationTable = new DataDirectory();
- optionalHeader.BaseRelocationTable.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.BaseRelocationTable.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 7 && offset - initialOffset < optionalSize)
- {
- optionalHeader.Debug = new DataDirectory();
- optionalHeader.Debug.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.Debug.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 8 && offset - initialOffset < optionalSize)
- {
- optionalHeader.Architecture = data.ReadUInt64(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 9 && offset - initialOffset < optionalSize)
- {
- optionalHeader.GlobalPtr = new DataDirectory();
- optionalHeader.GlobalPtr.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.GlobalPtr.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 10 && offset - initialOffset < optionalSize)
- {
- optionalHeader.ThreadLocalStorageTable = new DataDirectory();
- optionalHeader.ThreadLocalStorageTable.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.ThreadLocalStorageTable.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 11 && offset - initialOffset < optionalSize)
- {
- optionalHeader.LoadConfigTable = new DataDirectory();
- optionalHeader.LoadConfigTable.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.LoadConfigTable.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 12 && offset - initialOffset < optionalSize)
- {
- optionalHeader.BoundImport = new DataDirectory();
- optionalHeader.BoundImport.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.BoundImport.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 13 && offset - initialOffset < optionalSize)
- {
- optionalHeader.ImportAddressTable = new DataDirectory();
- optionalHeader.ImportAddressTable.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.ImportAddressTable.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 14 && offset - initialOffset < optionalSize)
- {
- optionalHeader.DelayImportDescriptor = new DataDirectory();
- optionalHeader.DelayImportDescriptor.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.DelayImportDescriptor.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 15 && offset - initialOffset < optionalSize)
- {
- optionalHeader.CLRRuntimeHeader = new DataDirectory();
- optionalHeader.CLRRuntimeHeader.VirtualAddress = data.ReadUInt32(ref offset);
- optionalHeader.CLRRuntimeHeader.Size = data.ReadUInt32(ref offset);
- }
- if (optionalHeader.NumberOfRvaAndSizes >= 16 && offset - initialOffset < optionalSize)
- {
- optionalHeader.Reserved = data.ReadUInt64(ref offset);
- }
-
- #endregion
-
- return optionalHeader;
- }
-
- ///
- /// Parse a byte array into a section table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Number of section table entries to read
- /// Filled section table on success, null on error
- private static SectionHeader[] ParseSectionTable(byte[] data, int offset, int count)
- {
- // TODO: Use marshalling here instead of building
- var sectionTable = new SectionHeader[count];
-
- for (int i = 0; i < count; i++)
- {
- var entry = new SectionHeader();
- entry.Name = data.ReadBytes(ref offset, 8);
- entry.VirtualSize = data.ReadUInt32(ref offset);
- entry.VirtualAddress = data.ReadUInt32(ref offset);
- entry.SizeOfRawData = data.ReadUInt32(ref offset);
- entry.PointerToRawData = data.ReadUInt32(ref offset);
- entry.PointerToRelocations = data.ReadUInt32(ref offset);
- entry.PointerToLinenumbers = data.ReadUInt32(ref offset);
- entry.NumberOfRelocations = data.ReadUInt16(ref offset);
- entry.NumberOfLinenumbers = data.ReadUInt16(ref offset);
- entry.Characteristics = (SectionFlags)data.ReadUInt32(ref offset);
- entry.COFFRelocations = new COFFRelocation[entry.NumberOfRelocations];
- for (int j = 0; i < entry.NumberOfRelocations; j++)
- {
- // TODO: Seek to correct location and read data
- }
- entry.COFFLineNumbers = new COFFLineNumber[entry.NumberOfRelocations];
- for (int j = 0; i < entry.NumberOfRelocations; j++)
- {
- // TODO: Seek to correct location and read data
- }
- sectionTable[i] = entry;
- }
-
- return sectionTable;
- }
-
- ///
- /// Parse a byte array into a COFF symbol table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Number of COFF symbol table entries to read
- /// Filled COFF symbol table on success, null on error
- private static COFFSymbolTableEntry[] ParseCOFFSymbolTable(byte[] data, int offset, uint count)
- {
- // TODO: Use marshalling here instead of building
- var coffSymbolTable = new COFFSymbolTableEntry[count];
-
- int auxSymbolsRemaining = 0;
- int currentSymbolType = 0;
-
- for (int i = 0; i < count; i++)
- {
- // Standard COFF Symbol Table Entry
- if (currentSymbolType == 0)
- {
- var entry = new COFFSymbolTableEntry();
- entry.ShortName = data.ReadBytes(ref offset, 8);
- entry.Zeroes = BitConverter.ToUInt32(entry.ShortName, 0);
- if (entry.Zeroes == 0)
- {
- entry.Offset = BitConverter.ToUInt32(entry.ShortName, 4);
- entry.ShortName = null;
- }
- entry.Value = data.ReadUInt32(ref offset);
- entry.SectionNumber = data.ReadUInt16(ref offset);
- entry.SymbolType = (SymbolType)data.ReadUInt16(ref offset);
- entry.StorageClass = (StorageClass)data.ReadByte(ref offset);
- entry.NumberOfAuxSymbols = data.ReadByte(ref offset);
- coffSymbolTable[i] = entry;
-
- auxSymbolsRemaining = entry.NumberOfAuxSymbols;
- if (auxSymbolsRemaining == 0)
- continue;
-
- if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL
- && entry.SymbolType == SymbolType.IMAGE_SYM_TYPE_FUNC
- && entry.SectionNumber > 0)
- {
- currentSymbolType = 1;
- }
- else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FUNCTION
- && entry.ShortName != null
- && ((entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x62 && entry.ShortName[2] == 0x66) // .bf
- || (entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x65 && entry.ShortName[2] == 0x66))) // .ef
- {
- currentSymbolType = 2;
- }
- else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL
- && entry.SectionNumber == (ushort)SectionNumber.IMAGE_SYM_UNDEFINED
- && entry.Value == 0)
- {
- currentSymbolType = 3;
- }
- else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FILE)
- {
- // TODO: Symbol name should be ".file"
- currentSymbolType = 4;
- }
- else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_STATIC)
- {
- // TODO: Should have the name of a section (like ".text")
- currentSymbolType = 5;
- }
- else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_CLR_TOKEN)
- {
- currentSymbolType = 6;
- }
- }
-
- // Auxiliary Format 1: Function Definitions
- else if (currentSymbolType == 1)
- {
- var entry = new COFFSymbolTableEntry();
- entry.AuxFormat1TagIndex = data.ReadUInt32(ref offset);
- entry.AuxFormat1TotalSize = data.ReadUInt32(ref offset);
- entry.AuxFormat1PointerToLinenumber = data.ReadUInt32(ref offset);
- entry.AuxFormat1PointerToNextFunction = data.ReadUInt32(ref offset);
- entry.AuxFormat1Unused = data.ReadUInt16(ref offset);
- coffSymbolTable[i] = entry;
- auxSymbolsRemaining--;
- }
-
- // Auxiliary Format 2: .bf and .ef Symbols
- else if (currentSymbolType == 2)
- {
- var entry = new COFFSymbolTableEntry();
- entry.AuxFormat2Unused1 = data.ReadUInt32(ref offset);
- entry.AuxFormat2Linenumber = data.ReadUInt16(ref offset);
- entry.AuxFormat2Unused2 = data.ReadBytes(ref offset, 6);
- entry.AuxFormat2PointerToNextFunction = data.ReadUInt32(ref offset);
- entry.AuxFormat2Unused3 = data.ReadUInt16(ref offset);
- coffSymbolTable[i] = entry;
- auxSymbolsRemaining--;
- }
-
- // Auxiliary Format 3: Weak Externals
- else if (currentSymbolType == 3)
- {
- var entry = new COFFSymbolTableEntry();
- entry.AuxFormat3TagIndex = data.ReadUInt32(ref offset);
- entry.AuxFormat3Characteristics = data.ReadUInt32(ref offset);
- entry.AuxFormat3Unused = data.ReadBytes(ref offset, 10);
- coffSymbolTable[i] = entry;
- auxSymbolsRemaining--;
- }
-
- // Auxiliary Format 4: Files
- else if (currentSymbolType == 4)
- {
- var entry = new COFFSymbolTableEntry();
- entry.AuxFormat4FileName = data.ReadBytes(ref offset, 18);
- coffSymbolTable[i] = entry;
- auxSymbolsRemaining--;
- }
-
- // Auxiliary Format 5: Section Definitions
- else if (currentSymbolType == 5)
- {
- var entry = new COFFSymbolTableEntry();
- entry.AuxFormat5Length = data.ReadUInt32(ref offset);
- entry.AuxFormat5NumberOfRelocations = data.ReadUInt16(ref offset);
- entry.AuxFormat5NumberOfLinenumbers = data.ReadUInt16(ref offset);
- entry.AuxFormat5CheckSum = data.ReadUInt32(ref offset);
- entry.AuxFormat5Number = data.ReadUInt16(ref offset);
- entry.AuxFormat5Selection = data.ReadByte(ref offset);
- entry.AuxFormat5Unused = data.ReadBytes(ref offset, 3);
- coffSymbolTable[i] = entry;
- auxSymbolsRemaining--;
- }
-
- // Auxiliary Format 6: CLR Token Definition
- else if (currentSymbolType == 6)
- {
- var entry = new COFFSymbolTableEntry();
- entry.AuxFormat6AuxType = data.ReadByte(ref offset);
- entry.AuxFormat6Reserved1 = data.ReadByte(ref offset);
- entry.AuxFormat6SymbolTableIndex = data.ReadUInt32(ref offset);
- entry.AuxFormat6Reserved2 = data.ReadBytes(ref offset, 12);
- coffSymbolTable[i] = entry;
- auxSymbolsRemaining--;
- }
-
- // If we hit the last aux symbol, go back to normal format
- if (auxSymbolsRemaining == 0)
- currentSymbolType = 0;
- }
-
- return coffSymbolTable;
- }
-
- ///
- /// Parse a Stream into a COFF string table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled COFF string table on success, null on error
- private static COFFStringTable ParseCOFFStringTable(byte[] data, int offset)
- {
- // TODO: Use marshalling here instead of building
- var coffStringTable = new COFFStringTable();
-
- coffStringTable.TotalSize = data.ReadUInt32(ref offset);
-
- if (coffStringTable.TotalSize <= 4)
- return coffStringTable;
-
- var strings = new List();
-
- uint totalSize = coffStringTable.TotalSize;
- while (totalSize > 0 && offset < data.Length)
- {
- int initialPosition = offset;
- string str = data.ReadString(ref offset);
- strings.Add(str);
- totalSize -= (uint)(offset - initialPosition);
- }
-
- coffStringTable.Strings = strings.ToArray();
-
- return coffStringTable;
- }
-
- ///
- /// Parse a byte array into an attribute certificate table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// First address not part of the attribute certificate table
- /// Filled attribute certificate on success, null on error
- private static AttributeCertificateTableEntry[] ParseAttributeCertificateTable(byte[] data, int offset, int endOffset)
- {
- var attributeCertificateTable = new List();
-
- while (offset < endOffset && offset != data.Length)
- {
- var entry = new AttributeCertificateTableEntry();
-
- entry.Length = data.ReadUInt32(ref offset);
- entry.Revision = (WindowsCertificateRevision)data.ReadUInt16(ref offset);
- entry.CertificateType = (WindowsCertificateType)data.ReadUInt16(ref offset);
-
- int certificateDataLength = (int)(entry.Length - 8);
- if (certificateDataLength > 0)
- entry.Certificate = data.ReadBytes(ref offset, certificateDataLength);
-
- attributeCertificateTable.Add(entry);
-
- // Align to the 8-byte boundary
- while ((offset % 8) != 0 && offset < endOffset - 1 && offset != data.Length)
- _ = data.ReadByte(ref offset);
- }
-
- return attributeCertificateTable.ToArray();
- }
-
- ///
- /// Parse a byte array into a delay-load directory table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Filled delay-load directory table on success, null on error
- private static DelayLoadDirectoryTable ParseDelayLoadDirectoryTable(byte[] data, int offset)
- {
- // TODO: Use marshalling here instead of building
- var delayLoadDirectoryTable = new DelayLoadDirectoryTable();
-
- delayLoadDirectoryTable.Attributes = data.ReadUInt32(ref offset);
- delayLoadDirectoryTable.Name = data.ReadUInt32(ref offset);
- delayLoadDirectoryTable.ModuleHandle = data.ReadUInt32(ref offset);
- delayLoadDirectoryTable.DelayImportAddressTable = data.ReadUInt32(ref offset);
- delayLoadDirectoryTable.DelayImportNameTable = data.ReadUInt32(ref offset);
- delayLoadDirectoryTable.BoundDelayImportTable = data.ReadUInt32(ref offset);
- delayLoadDirectoryTable.UnloadDelayImportTable = data.ReadUInt32(ref offset);
- delayLoadDirectoryTable.TimeStamp = data.ReadUInt32(ref offset);
-
- return delayLoadDirectoryTable;
- }
-
- ///
- /// Parse a byte array into a base relocation table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// First address not part of the base relocation table
- /// Section table to use for virtual address translation
- /// Filled base relocation table on success, null on error
- private static BaseRelocationBlock[] ParseBaseRelocationTable(byte[] data, int offset, int endOffset, SectionHeader[] sections)
- {
- // TODO: Use marshalling here instead of building
- var baseRelocationTable = new List();
-
- while (offset < endOffset)
- {
- var baseRelocationBlock = new BaseRelocationBlock();
-
- baseRelocationBlock.PageRVA = data.ReadUInt32(ref offset);
- baseRelocationBlock.BlockSize = data.ReadUInt32(ref offset);
-
- var typeOffsetFieldEntries = new List();
- int totalSize = 8;
- while (totalSize < baseRelocationBlock.BlockSize && offset < data.Length)
- {
- var baseRelocationTypeOffsetFieldEntry = new BaseRelocationTypeOffsetFieldEntry();
-
- ushort typeAndOffsetField = data.ReadUInt16(ref offset);
- baseRelocationTypeOffsetFieldEntry.BaseRelocationType = (BaseRelocationTypes)(typeAndOffsetField >> 12);
- baseRelocationTypeOffsetFieldEntry.Offset = (ushort)(typeAndOffsetField & 0x0FFF);
-
- typeOffsetFieldEntries.Add(baseRelocationTypeOffsetFieldEntry);
- totalSize += 2;
- }
-
- baseRelocationBlock.TypeOffsetFieldEntries = typeOffsetFieldEntries.ToArray();
-
- baseRelocationTable.Add(baseRelocationBlock);
- }
-
- return baseRelocationTable.ToArray();
- }
-
- ///
- /// Parse a byte array into a debug table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// First address not part of the debug table
- /// Section table to use for virtual address translation
- /// Filled debug table on success, null on error
- private static DebugTable ParseDebugTable(byte[] data, int offset, int endOffset, SectionHeader[] sections)
- {
- // TODO: Use marshalling here instead of building
- var debugTable = new DebugTable();
-
- var debugDirectoryTable = new List();
-
- while (offset < endOffset)
- {
- var debugDirectoryEntry = new DebugDirectoryEntry();
-
- debugDirectoryEntry.Characteristics = data.ReadUInt32(ref offset);
- debugDirectoryEntry.TimeDateStamp = data.ReadUInt32(ref offset);
- debugDirectoryEntry.MajorVersion = data.ReadUInt16(ref offset);
- debugDirectoryEntry.MinorVersion = data.ReadUInt16(ref offset);
- debugDirectoryEntry.DebugType = (DebugType)data.ReadUInt32(ref offset);
- debugDirectoryEntry.SizeOfData = data.ReadUInt32(ref offset);
- debugDirectoryEntry.AddressOfRawData = data.ReadUInt32(ref offset);
- debugDirectoryEntry.PointerToRawData = data.ReadUInt32(ref offset);
-
- debugDirectoryTable.Add(debugDirectoryEntry);
- }
-
- debugTable.DebugDirectoryTable = debugDirectoryTable.ToArray();
-
- // 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
-
- return debugTable;
- }
-
- ///
- /// Parse a byte array into a export table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Section table to use for virtual address translation
- /// Filled export table on success, null on error
- private static ExportTable ParseExportTable(byte[] data, int offset, SectionHeader[] sections)
- {
- // TODO: Use marshalling here instead of building
- var exportTable = new ExportTable();
-
- var exportDirectoryTable = new ExportDirectoryTable();
-
- exportDirectoryTable.ExportFlags = data.ReadUInt32(ref offset);
- exportDirectoryTable.TimeDateStamp = data.ReadUInt32(ref offset);
- exportDirectoryTable.MajorVersion = data.ReadUInt16(ref offset);
- exportDirectoryTable.MinorVersion = data.ReadUInt16(ref offset);
- exportDirectoryTable.NameRVA = data.ReadUInt32(ref offset);
- exportDirectoryTable.OrdinalBase = data.ReadUInt32(ref offset);
- exportDirectoryTable.AddressTableEntries = data.ReadUInt32(ref offset);
- exportDirectoryTable.NumberOfNamePointers = data.ReadUInt32(ref offset);
- exportDirectoryTable.ExportAddressTableRVA = data.ReadUInt32(ref offset);
- exportDirectoryTable.NamePointerRVA = data.ReadUInt32(ref offset);
- exportDirectoryTable.OrdinalTableRVA = data.ReadUInt32(ref offset);
-
- exportTable.ExportDirectoryTable = exportDirectoryTable;
-
- // Name
- if (exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections) != 0)
- {
- offset = (int)exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections);
- string name = data.ReadString(ref offset, Encoding.ASCII);
- exportDirectoryTable.Name = name;
- }
-
- // Address table
- if (exportDirectoryTable.AddressTableEntries != 0 && exportDirectoryTable.ExportAddressTableRVA.ConvertVirtualAddress(sections) != 0)
- {
- offset = (int)exportDirectoryTable.ExportAddressTableRVA.ConvertVirtualAddress(sections);
- var exportAddressTable = new ExportAddressTableEntry[exportDirectoryTable.AddressTableEntries];
-
- for (int i = 0; i < exportDirectoryTable.AddressTableEntries; i++)
- {
- var addressTableEntry = new ExportAddressTableEntry();
-
- // TODO: Use the optional header address and length to determine if export or forwarder
- addressTableEntry.ExportRVA = data.ReadUInt32(ref offset);
- addressTableEntry.ForwarderRVA = addressTableEntry.ExportRVA;
-
- exportAddressTable[i] = addressTableEntry;
- }
-
- exportTable.ExportAddressTable = exportAddressTable;
- }
-
- // Name pointer table
- if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.NamePointerRVA.ConvertVirtualAddress(sections) != 0)
- {
- offset = (int)exportDirectoryTable.NamePointerRVA.ConvertVirtualAddress(sections);
- var namePointerTable = new ExportNamePointerTable();
-
- namePointerTable.Pointers = new uint[exportDirectoryTable.NumberOfNamePointers];
- for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++)
- {
- uint pointer = data.ReadUInt32(ref offset);
- namePointerTable.Pointers[i] = pointer;
- }
-
- exportTable.NamePointerTable = namePointerTable;
- }
-
- // Ordinal table
- if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.OrdinalTableRVA.ConvertVirtualAddress(sections) != 0)
- {
- offset = (int)exportDirectoryTable.OrdinalTableRVA.ConvertVirtualAddress(sections);
- var exportOrdinalTable = new ExportOrdinalTable();
-
- exportOrdinalTable.Indexes = new ushort[exportDirectoryTable.NumberOfNamePointers];
- for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++)
- {
- ushort pointer = data.ReadUInt16(ref offset);
- exportOrdinalTable.Indexes[i] = pointer;
- }
-
- exportTable.OrdinalTable = exportOrdinalTable;
- }
-
- // Name table
- if (exportDirectoryTable.NumberOfNamePointers != 0 && exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections) != 0)
- {
- offset = (int)exportDirectoryTable.NameRVA.ConvertVirtualAddress(sections);
- var exportNameTable = new ExportNameTable();
-
- exportNameTable.Strings = new string[exportDirectoryTable.NumberOfNamePointers];
- for (int i = 0; i < exportDirectoryTable.NumberOfNamePointers; i++)
- {
- string str = data.ReadString(ref offset, Encoding.ASCII);
- exportNameTable.Strings[i] = str;
- }
-
- exportTable.ExportNameTable = exportNameTable;
- }
-
- return exportTable;
- }
-
- ///
- /// Parse a byte array into a import table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Optional header magic number indicating PE32 or PE32+
- /// Section table to use for virtual address translation
- /// Filled import table on success, null on error
- private static ImportTable ParseImportTable(byte[] data, int offset, OptionalHeaderMagicNumber magic, SectionHeader[] sections)
- {
- // TODO: Use marshalling here instead of building
- var importTable = new ImportTable();
-
- // Import directory table
- var importDirectoryTable = new List();
-
- // Loop until the last item (all nulls) are found
- while (true)
- {
- var importDirectoryTableEntry = new ImportDirectoryTableEntry();
-
- importDirectoryTableEntry.ImportLookupTableRVA = data.ReadUInt32(ref offset);
- importDirectoryTableEntry.TimeDateStamp = data.ReadUInt32(ref offset);
- importDirectoryTableEntry.ForwarderChain = data.ReadUInt32(ref offset);
- importDirectoryTableEntry.NameRVA = data.ReadUInt32(ref offset);
- importDirectoryTableEntry.ImportAddressTableRVA = data.ReadUInt32(ref offset);
-
- importDirectoryTable.Add(importDirectoryTableEntry);
-
- // All zero values means the last entry
- if (importDirectoryTableEntry.ImportLookupTableRVA == 0
- && importDirectoryTableEntry.TimeDateStamp == 0
- && importDirectoryTableEntry.ForwarderChain == 0
- && importDirectoryTableEntry.NameRVA == 0
- && importDirectoryTableEntry.ImportAddressTableRVA == 0)
- break;
- }
-
- importTable.ImportDirectoryTable = importDirectoryTable.ToArray();
-
- // Names
- for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++)
- {
- var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
- if (importDirectoryTableEntry.NameRVA.ConvertVirtualAddress(sections) == 0)
- continue;
-
- int nameAddress = (int)importDirectoryTableEntry.NameRVA.ConvertVirtualAddress(sections);
- string name = data.ReadString(ref nameAddress, Encoding.ASCII);
- importDirectoryTableEntry.Name = name;
- }
-
- // Lookup tables
- var importLookupTables = new Dictionary();
-
- for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++)
- {
- var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
- if (importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(sections) == 0)
- continue;
-
- int tableAddress = (int)importDirectoryTableEntry.ImportLookupTableRVA.ConvertVirtualAddress(sections);
- var entryLookupTable = new List();
-
- while (true)
- {
- var entryLookupTableEntry = new ImportLookupTableEntry();
-
- if (magic == OptionalHeaderMagicNumber.PE32)
- {
- uint entryValue = data.ReadUInt32(ref tableAddress);
- entryLookupTableEntry.OrdinalNameFlag = (entryValue & 0x80000000) != 0;
- if (entryLookupTableEntry.OrdinalNameFlag)
- entryLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x80000000);
- else
- entryLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x80000000);
- }
- else if (magic == OptionalHeaderMagicNumber.PE32Plus)
- {
- ulong entryValue = data.ReadUInt64(ref tableAddress);
- entryLookupTableEntry.OrdinalNameFlag = (entryValue & 0x8000000000000000) != 0;
- if (entryLookupTableEntry.OrdinalNameFlag)
- entryLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x8000000000000000);
- else
- entryLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x8000000000000000);
- }
-
- entryLookupTable.Add(entryLookupTableEntry);
-
- // All zero values means the last entry
- if (entryLookupTableEntry.OrdinalNameFlag == false
- && entryLookupTableEntry.OrdinalNumber == 0
- && entryLookupTableEntry.HintNameTableRVA == 0)
- break;
- }
-
- importLookupTables[i] = entryLookupTable.ToArray();
- }
-
- importTable.ImportLookupTables = importLookupTables;
-
- // Address tables
- var importAddressTables = new Dictionary();
-
- for (int i = 0; i < importTable.ImportDirectoryTable.Length; i++)
- {
- var importDirectoryTableEntry = importTable.ImportDirectoryTable[i];
- if (importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(sections) == 0)
- continue;
-
- int tableAddress = (int)importDirectoryTableEntry.ImportAddressTableRVA.ConvertVirtualAddress(sections);
- var addressLookupTable = new List();
-
- while (true)
- {
- var addressLookupTableEntry = new ImportAddressTableEntry();
-
- if (magic == OptionalHeaderMagicNumber.PE32)
- {
- uint entryValue = data.ReadUInt32(ref tableAddress);
- addressLookupTableEntry.OrdinalNameFlag = (entryValue & 0x80000000) != 0;
- if (addressLookupTableEntry.OrdinalNameFlag)
- addressLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x80000000);
- else
- addressLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x80000000);
- }
- else if (magic == OptionalHeaderMagicNumber.PE32Plus)
- {
- ulong entryValue = data.ReadUInt64(ref tableAddress);
- addressLookupTableEntry.OrdinalNameFlag = (entryValue & 0x8000000000000000) != 0;
- if (addressLookupTableEntry.OrdinalNameFlag)
- addressLookupTableEntry.OrdinalNumber = (ushort)(entryValue & ~0x8000000000000000);
- else
- addressLookupTableEntry.HintNameTableRVA = (uint)(entryValue & ~0x8000000000000000);
- }
-
- addressLookupTable.Add(addressLookupTableEntry);
-
- // All zero values means the last entry
- if (addressLookupTableEntry.OrdinalNameFlag == false
- && addressLookupTableEntry.OrdinalNumber == 0
- && addressLookupTableEntry.HintNameTableRVA == 0)
- break;
- }
-
- importAddressTables[i] = addressLookupTable.ToArray();
- }
-
- importTable.ImportAddressTables = importAddressTables;
-
- // Hint/Name table
- var importHintNameTable = new List();
-
- if (importTable.ImportLookupTables != null && importTable.ImportLookupTables.Count > 0)
- {
- // Get the addresses of the hint/name table entries
- List hintNameTableEntryAddresses = new List();
-
- // If we have import lookup tables
- if (importTable.ImportLookupTables != null && importLookupTables.Count > 0)
- {
- var addresses = importTable.ImportLookupTables
- .SelectMany(kvp => kvp.Value)
- .Select(ilte => (int)ilte.HintNameTableRVA.ConvertVirtualAddress(sections));
- hintNameTableEntryAddresses.AddRange(addresses);
- }
-
- // If we have import address tables
- if (importTable.ImportAddressTables != null && importTable.ImportAddressTables.Count > 0)
- {
- var addresses = importTable.ImportAddressTables
- .SelectMany(kvp => kvp.Value)
- .Select(iate => (int)iate.HintNameTableRVA.ConvertVirtualAddress(sections));
- hintNameTableEntryAddresses.AddRange(addresses);
- }
-
- // Sanitize the addresses
- hintNameTableEntryAddresses = hintNameTableEntryAddresses.Where(addr => addr != 0)
- .Distinct()
- .OrderBy(a => a)
- .ToList();
-
- // If we have any addresses, add them to the table
- if (hintNameTableEntryAddresses.Any())
- {
- for (int i = 0; i < hintNameTableEntryAddresses.Count; i++)
- {
- int hintNameTableEntryAddress = hintNameTableEntryAddresses[i];
- var hintNameTableEntry = new HintNameTableEntry();
-
- hintNameTableEntry.Hint = data.ReadUInt16(ref hintNameTableEntryAddress);
- hintNameTableEntry.Name = data.ReadString(ref hintNameTableEntryAddress, Encoding.ASCII);
-
- importHintNameTable.Add(hintNameTableEntry);
- }
- }
- }
-
- importTable.HintNameTable = importHintNameTable.ToArray();
-
- return importTable;
- }
-
- ///
- /// Parse a byte array into a resource directory table
- ///
- /// Byte array to parse
- /// Offset into the byte array
- /// Initial offset to use in address comparisons
- /// Section table to use for virtual address translation
- /// Filled resource directory table on success, null on error
- private static ResourceDirectoryTable ParseResourceDirectoryTable(byte[] data, int offset, long initialOffset, SectionHeader[] sections)
- {
- // TODO: Use marshalling here instead of building
- var resourceDirectoryTable = new ResourceDirectoryTable();
-
- resourceDirectoryTable.Characteristics = data.ReadUInt32(ref offset);
- if (resourceDirectoryTable.Characteristics != 0)
- return null;
-
- resourceDirectoryTable.TimeDateStamp = data.ReadUInt32(ref offset);
- resourceDirectoryTable.MajorVersion = data.ReadUInt16(ref offset);
- resourceDirectoryTable.MinorVersion = data.ReadUInt16(ref offset);
- resourceDirectoryTable.NumberOfNameEntries = data.ReadUInt16(ref offset);
- resourceDirectoryTable.NumberOfIDEntries = data.ReadUInt16(ref offset);
-
- // If we have no entries
- int totalEntryCount = resourceDirectoryTable.NumberOfNameEntries + resourceDirectoryTable.NumberOfIDEntries;
- if (totalEntryCount == 0)
- return resourceDirectoryTable;
-
- // Perform top-level pass of data
- resourceDirectoryTable.Entries = new ResourceDirectoryEntry[totalEntryCount];
- for (int i = 0; i < totalEntryCount; i++)
- {
- var entry = new ResourceDirectoryEntry();
- uint newOffset = data.ReadUInt32(ref offset);
- if ((newOffset & 0x80000000) != 0)
- entry.NameOffset = newOffset & ~0x80000000;
- else
- entry.IntegerID = newOffset;
-
- newOffset = data.ReadUInt32(ref offset);
- if ((newOffset & 0x80000000) != 0)
- entry.SubdirectoryOffset = newOffset & ~0x80000000;
- else
- entry.DataEntryOffset = newOffset;
-
- // Read the name from the offset, if needed
- if (entry.NameOffset > 0)
- {
- int nameOffset = (int)(entry.NameOffset + (uint)initialOffset);
-
- var resourceDirectoryString = new ResourceDirectoryString();
-
- resourceDirectoryString.Length = data.ReadUInt16(ref nameOffset);
- if (resourceDirectoryString.Length > 0)
- resourceDirectoryString.UnicodeString = data.ReadBytes(ref nameOffset, resourceDirectoryString.Length * 2);
-
- entry.Name = resourceDirectoryString;
- }
-
- resourceDirectoryTable.Entries[i] = entry;
- }
-
- // Loop through and process the entries
- foreach (var entry in resourceDirectoryTable.Entries)
- {
- if (entry.DataEntryOffset > 0)
- {
- int dataEntryOffset = (int)(entry.DataEntryOffset + (uint)initialOffset);
-
- var resourceDataEntry = new ResourceDataEntry();
- resourceDataEntry.DataRVA = data.ReadUInt32(ref dataEntryOffset);
- resourceDataEntry.Size = data.ReadUInt32(ref dataEntryOffset);
- resourceDataEntry.Codepage = data.ReadUInt32(ref dataEntryOffset);
- resourceDataEntry.Reserved = data.ReadUInt32(ref dataEntryOffset);
-
- // Read the data from the offset
- dataEntryOffset = (int)resourceDataEntry.DataRVA.ConvertVirtualAddress(sections);
- if (dataEntryOffset > 0 && resourceDataEntry.Size > 0)
- resourceDataEntry.Data = data.ReadBytes(ref dataEntryOffset, (int)resourceDataEntry.Size);
-
- entry.DataEntry = resourceDataEntry;
- }
- else if (entry.SubdirectoryOffset > 0)
- {
- int subdirectoryOffset = (int)(entry.SubdirectoryOffset + (uint)initialOffset);
- entry.Subdirectory = ParseResourceDirectoryTable(data, subdirectoryOffset, initialOffset, sections);
- }
- }
-
- return resourceDirectoryTable;
+ // Create a memory stream and parse that
+ MemoryStream dataStream = new MemoryStream(data);
+ dataStream.Position = offset;
+ return ParseExecutable(dataStream);
}
#endregion