diff --git a/BurnOutSharp.Builders/LinearExecutable.cs b/BurnOutSharp.Builders/LinearExecutable.cs
index 68d1d619..99d14e07 100644
--- a/BurnOutSharp.Builders/LinearExecutable.cs
+++ b/BurnOutSharp.Builders/LinearExecutable.cs
@@ -1,5 +1,10 @@
-using System.IO;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
using BurnOutSharp.Models.LinearExecutable;
+using BurnOutSharp.Utilities;
+using static BurnOutSharp.Models.LinearExecutable.Constants;
namespace BurnOutSharp.Builders
{
@@ -53,6 +58,8 @@ namespace BurnOutSharp.Builders
// Create a new executable to fill
var executable = new Executable();
+ #region MS-DOS Stub
+
// Parse the MS-DOS stub
var stub = MSDOS.ParseExecutable(data);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
@@ -61,19 +68,133 @@ namespace BurnOutSharp.Builders
// Set the MS-DOS stub
executable.Stub = stub;
+ #endregion
+
+ #region Information Block
+
+ // Try to parse the executable header
+ data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin);
+ var informationBlock = ParseInformationBlock(data);
+ if (informationBlock == null)
+ return null;
+
+ // Set the executable header
+ executable.InformationBlock = informationBlock;
+
+ #endregion
+
+ #region Object Table
+
+ // Get the object table offset
+ long objectTableOffset = informationBlock.ObjectTableOffset + stub.Header.NewExeHeaderAddr;
+ if (objectTableOffset < 0 || objectTableOffset >= data.Length)
+ return null;
+
+ // Seek to the object table
+ data.Seek(objectTableOffset, SeekOrigin.Begin);
+
+ // Create the object table
+ executable.ObjectTable = new ObjectTableEntry[informationBlock.ObjectTableCount];
+
+ // Try to parse the object table
+ for (int i = 0; i < executable.ObjectTable.Length; i++)
+ {
+ var objectTableEntry = ParseObjectTableEntry(data);
+ if (objectTableEntry == null)
+ return null;
+
+ executable.ObjectTable[i] = objectTableEntry;
+ }
+
+ #endregion
+
// TODO: Implement LE/LX parsing
- return null;
+
+ return executable;
}
///
- /// Parse a Stream into a Linear Executable information block
+ /// Parse a Stream into an information block
///
/// Stream to parse
- /// Filled executable header on success, null on error
+ /// Filled information block on success, null on error
private static InformationBlock ParseInformationBlock(Stream data)
{
- // TODO: Implement LE/LX information block parsing
- return null;
+ // TODO: Use marshalling here instead of building
+ InformationBlock informationBlock = new InformationBlock();
+
+ byte[] magic = data.ReadBytes(2);
+ informationBlock.Signature = Encoding.ASCII.GetString(magic);
+ if (informationBlock.Signature != LESignatureString && informationBlock.Signature != LXSignatureString)
+ return null;
+
+ informationBlock.ByteOrder = (ByteOrder)data.ReadByteValue();
+ informationBlock.WordOrder = (WordOrder)data.ReadByteValue();
+ informationBlock.ExecutableFormatLevel = data.ReadUInt32();
+ informationBlock.CPUType = (CPUType)data.ReadUInt16();
+ informationBlock.ModuleOS = (OperatingSystem)data.ReadUInt16();
+ informationBlock.ModuleVersion = data.ReadUInt32();
+ informationBlock.ModuleTypeFlags = (ModuleFlags)data.ReadUInt32();
+ informationBlock.ModuleNumberPages = data.ReadUInt32();
+ informationBlock.InitialObjectCS = data.ReadUInt32();
+ informationBlock.InitialEIP = data.ReadUInt32();
+ informationBlock.InitialObjectSS = data.ReadUInt32();
+ informationBlock.InitialESP = data.ReadUInt32();
+ informationBlock.MemoryPageSize = data.ReadUInt32();
+ informationBlock.BytesOnLastPage = data.ReadUInt32();
+ informationBlock.FixupSectionSize = data.ReadUInt32();
+ informationBlock.FixupSectionChecksum = data.ReadUInt32();
+ informationBlock.LoaderSectionSize = data.ReadUInt32();
+ informationBlock.LoaderSectionChecksum = data.ReadUInt32();
+ informationBlock.ObjectTableOffset = data.ReadUInt32();
+ informationBlock.ObjectTableCount = data.ReadUInt32();
+ informationBlock.ObjectPageMapOffset = data.ReadUInt32();
+ informationBlock.ObjectIterateDataMapOffset = data.ReadUInt32();
+ informationBlock.ResourceTableOffset = data.ReadUInt32();
+ informationBlock.ResourceTableCount = data.ReadUInt32();
+ informationBlock.ResidentNamesTableOffset = data.ReadUInt32();
+ informationBlock.EntryTableOffset = data.ReadUInt32();
+ informationBlock.ModuleDirectivesTableOffset = data.ReadUInt32();
+ informationBlock.ModuleDirectivesCount = data.ReadUInt32();
+ informationBlock.FixupPageTableOffset = data.ReadUInt32();
+ informationBlock.FixupRecordTableOffset = data.ReadUInt32();
+ informationBlock.ImportedModulesNameTableOffset = data.ReadUInt32();
+ informationBlock.ImportedModulesCount = data.ReadUInt32();
+ informationBlock.ImportProcedureNameTableOffset = data.ReadUInt32();
+ informationBlock.PerPageChecksumTableOffset = data.ReadUInt32();
+ informationBlock.DataPagesOffset = data.ReadUInt32();
+ informationBlock.PreloadPageCount = data.ReadUInt32();
+ informationBlock.NonResidentNamesTableOffset = data.ReadUInt32();
+ informationBlock.NonResidentNamesTableLength = data.ReadUInt32();
+ informationBlock.NonResidentNamesTableChecksum = data.ReadUInt32();
+ informationBlock.AutomaticDataObject = data.ReadUInt32();
+ informationBlock.DebugInformationOffset = data.ReadUInt32();
+ informationBlock.DebugInformationLength = data.ReadUInt32();
+ informationBlock.PreloadInstancePagesNumber = data.ReadUInt32();
+ informationBlock.DemandInstancePagesNumber = data.ReadUInt32();
+ informationBlock.ExtraHeapAllocation = data.ReadUInt32();
+
+ return informationBlock;
+ }
+
+ ///
+ /// Parse a Stream into an object table entry
+ ///
+ /// Stream to parse
+ /// Filled object table entry on success, null on error
+ private static ObjectTableEntry ParseObjectTableEntry(Stream data)
+ {
+ // TODO: Use marshalling here instead of building
+ ObjectTableEntry objectTableEntry = new ObjectTableEntry();
+
+ objectTableEntry.VirtualSegmentSize = data.ReadUInt32();
+ objectTableEntry.RelocationBaseAddress = data.ReadUInt32();
+ objectTableEntry.ObjectFlags = (ObjectFlags)data.ReadUInt16();
+ objectTableEntry.PageTableIndex = data.ReadUInt32();
+ objectTableEntry.PageTableEntries = data.ReadUInt32();
+ objectTableEntry.Reserved = data.ReadUInt32();
+
+ return objectTableEntry;
}
#endregion
diff --git a/BurnOutSharp.Models/LinearExecutable/InformationBlock.cs b/BurnOutSharp.Models/LinearExecutable/InformationBlock.cs
index 08eaac4d..996cc866 100644
--- a/BurnOutSharp.Models/LinearExecutable/InformationBlock.cs
+++ b/BurnOutSharp.Models/LinearExecutable/InformationBlock.cs
@@ -24,7 +24,7 @@ namespace BurnOutSharp.Models.LinearExecutable
/// The signature word is used by the loader to identify the EXE
/// file as a valid 32-bit Linear Executable Module Format.
///
- public char[] Signature;
+ public string Signature;
///
/// Byte Ordering.
diff --git a/BurnOutSharp.Wrappers/LinearExecutable.cs b/BurnOutSharp.Wrappers/LinearExecutable.cs
index ca6a866d..ee62dffe 100644
--- a/BurnOutSharp.Wrappers/LinearExecutable.cs
+++ b/BurnOutSharp.Wrappers/LinearExecutable.cs
@@ -78,7 +78,7 @@ namespace BurnOutSharp.Wrappers
#region Information Block
///
- public char[] Signature => _executable.InformationBlock.Signature;
+ public string Signature => _executable.InformationBlock.Signature;
///
public Models.LinearExecutable.ByteOrder ByteOrder => _executable.InformationBlock.ByteOrder;