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;