diff --git a/BurnOutSharp.Builders/LinearExecutable.cs b/BurnOutSharp.Builders/LinearExecutable.cs index 99d14e07..e2ee9796 100644 --- a/BurnOutSharp.Builders/LinearExecutable.cs +++ b/BurnOutSharp.Builders/LinearExecutable.cs @@ -86,29 +86,365 @@ namespace BurnOutSharp.Builders #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++) + long offset = informationBlock.ObjectTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) { - var objectTableEntry = ParseObjectTableEntry(data); - if (objectTableEntry == null) - return null; + // Seek to the object table + data.Seek(offset, SeekOrigin.Begin); - executable.ObjectTable[i] = objectTableEntry; + // 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 entry = ParseObjectTableEntry(data); + if (entry == null) + return null; + + executable.ObjectTable[i] = entry; + } } #endregion - // TODO: Implement LE/LX parsing + #region Object Page Map + + // Get the object page map offset + offset = informationBlock.ObjectPageMapOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the object page map + data.Seek(offset, SeekOrigin.Begin); + + // Create the object page map + executable.ObjectPageMap = new ObjectPageMapEntry[informationBlock.ObjectTableCount]; + + // Try to parse the object page map + for (int i = 0; i < executable.ObjectPageMap.Length; i++) + { + var entry = ParseObjectPageMapEntry(data); + if (entry == null) + return null; + + executable.ObjectPageMap[i] = entry; + } + } + + #endregion + + #region Object Iterate Data Map + + offset = informationBlock.ObjectIterateDataMapOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the object page map + data.Seek(offset, SeekOrigin.Begin); + + // TODO: Implement when model found + // No model has been found in the documentation about what + // each of the entries looks like for this map. + } + + #endregion + + #region Resource Table + + // Get the resource table offset + offset = informationBlock.ResourceTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the resource table + data.Seek(offset, SeekOrigin.Begin); + + // Create the resource table + executable.ResourceTable = new ResourceTableEntry[informationBlock.ResourceTableCount]; + + // Try to parse the resource table + for (int i = 0; i < executable.ResourceTable.Length; i++) + { + var entry = ParseResourceTableEntry(data); + if (entry == null) + return null; + + executable.ResourceTable[i] = entry; + } + } + + #endregion + + #region Resident Names Table + + // Get the resident names table offset + offset = informationBlock.ResidentNamesTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the resident names table + data.Seek(offset, SeekOrigin.Begin); + + // Create the resident names table + var residentNamesTable = new List(); + + // Try to parse the resident names table + while (true) + { + var entry = ParseResidentNamesTableEntry(data); + residentNamesTable.Add(entry); + + // If we have a 0-length entry + if (entry.Length == 0) + break; + } + + // Assign the resident names table + executable.ResidentNamesTable = residentNamesTable.ToArray(); + } + + #endregion + + #region Entry Table + + // Get the entry table offset + offset = informationBlock.EntryTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the entry table + data.Seek(offset, SeekOrigin.Begin); + + // Create the entry table + var entryTable = new List(); + + // Try to parse the entry table + while (true) + { + var bundle = ParseEntryTableBundle(data); + entryTable.Add(bundle); + + // If we have a 0-length entry + if (bundle.Entries == 0) + break; + } + + // Assign the entry table + executable.EntryTable = entryTable.ToArray(); + } + + #endregion + + #region Module Format Directives Table + + // Get the module format directives table offset + offset = informationBlock.ModuleDirectivesTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the module format directives table + data.Seek(offset, SeekOrigin.Begin); + + // Create the module format directives table + executable.ModuleFormatDirectivesTable = new ModuleFormatDirectivesTableEntry[informationBlock.ModuleDirectivesCount]; + + // Try to parse the module format directives table + for (int i = 0; i < executable.ModuleFormatDirectivesTable.Length; i++) + { + var entry = ParseModuleFormatDirectivesTableEntry(data); + if (entry == null) + return null; + + executable.ModuleFormatDirectivesTable[i] = entry; + } + } + + #endregion + + #region Verify Record Directive Table + + // TODO: Figure out where the offset to this table is stored + // The documentation suggests it's either part of or immediately following + // the Module Format Directives Table + + #endregion + + #region Fix-up Page Table + + // Get the fix-up page table offset + offset = informationBlock.FixupPageTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the fix-up page table + data.Seek(offset, SeekOrigin.Begin); + + // Create the fix-up page table + executable.FixupPageTable = new FixupPageTableEntry[executable.ObjectPageMap.Length + 1]; + + // Try to parse the fix-up page table + for (int i = 0; i < executable.FixupPageTable.Length; i++) + { + var entry = ParseFixupPageTableEntry(data); + if (entry == null) + return null; + + executable.FixupPageTable[i] = entry; + } + } + + #endregion + + #region Fix-up Record Table + + // Get the fix-up record table offset + offset = informationBlock.FixupRecordTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the fix-up record table + data.Seek(offset, SeekOrigin.Begin); + + // Create the fix-up record table + executable.FixupRecordTable = new FixupRecordTableEntry[executable.ObjectPageMap.Length + 1]; + + // Try to parse the fix-up record table + for (int i = 0; i < executable.FixupRecordTable.Length; i++) + { + var entry = ParseFixupRecordTableEntry(data); + if (entry == null) + return null; + + executable.FixupRecordTable[i] = entry; + } + } + + #endregion + + #region Imported Module Name Table + + // Get the imported module name table offset + offset = informationBlock.ImportedModulesNameTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the imported module name table + data.Seek(offset, SeekOrigin.Begin); + + // Create the imported module name table + executable.ImportModuleNameTable = new ImportModuleNameTableEntry[informationBlock.ImportedModulesCount]; + + // Try to parse the imported module name table + for (int i = 0; i < executable.ImportModuleNameTable.Length; i++) + { + var entry = ParseImportModuleNameTableEntry(data); + if (entry == null) + return null; + + executable.ImportModuleNameTable[i] = entry; + } + } + + #endregion + + #region Imported Module Procedure Name Table + + // Get the imported module procedure name table offset + offset = informationBlock.ImportProcedureNameTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the imported module procedure name table + data.Seek(offset, SeekOrigin.Begin); + + // Get the size of the imported module procedure name table + long tableSize = informationBlock.FixupPageTableOffset + + informationBlock.FixupSectionSize + - informationBlock.ImportProcedureNameTableOffset; + + // Create the imported module procedure name table + var importModuleProcedureNameTable = new List(); + + // Try to parse the imported module procedure name table + while (data.Position < offset + tableSize) + { + var entry = ParseImportModuleProcedureNameTableEntry(data); + if (entry == null) + return null; + + importModuleProcedureNameTable.Add(entry); + } + + // Assign the resident names table + executable.ImportModuleProcedureNameTable = importModuleProcedureNameTable.ToArray(); + } + + #endregion + + #region Per-Page Checksum Table + + // Get the per-page checksum table offset + offset = informationBlock.PerPageChecksumTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the per-page checksum name table + data.Seek(offset, SeekOrigin.Begin); + + // Create the per-page checksum name table + executable.PerPageChecksumTable = new PerPageChecksumTableEntry[informationBlock.ModuleNumberPages]; + + // Try to parse the per-page checksum name table + for (int i = 0; i < executable.PerPageChecksumTable.Length; i++) + { + var entry = ParsePerPageChecksumTableEntry(data); + if (entry == null) + return null; + + executable.PerPageChecksumTable[i] = entry; + } + } + + #endregion + + #region Non-Resident Names Table + + // Get the non-resident names table offset + offset = informationBlock.NonResidentNamesTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the non-resident names table + data.Seek(offset, SeekOrigin.Begin); + + // Create the non-resident names table + var nonResidentNamesTable = new List(); + + // Try to parse the non-resident names table + while (true) + { + var entry = ParseNonResidentNameTableEntry(data); + nonResidentNamesTable.Add(entry); + + // If we have a 0-length entry + if (entry.Length == 0) + break; + } + + // Assign the non-resident names table + executable.NonResidentNamesTable = nonResidentNamesTable.ToArray(); + } + + #endregion + + #region Debug Information + + // Get the debug information offset + offset = informationBlock.NonResidentNamesTableOffset + stub.Header.NewExeHeaderAddr; + if (offset > stub.Header.NewExeHeaderAddr && offset < data.Length) + { + // Seek to the debug information + data.Seek(offset, SeekOrigin.Begin); + + // Try to parse the debug information + var debugInformation = ParseDebugInformation(data, informationBlock.DebugInformationLength); + if (debugInformation == null) + return null; + + // Set the debug information + executable.DebugInformation = debugInformation; + } + + #endregion return executable; } @@ -121,7 +457,7 @@ namespace BurnOutSharp.Builders private static InformationBlock ParseInformationBlock(Stream data) { // TODO: Use marshalling here instead of building - InformationBlock informationBlock = new InformationBlock(); + var informationBlock = new InformationBlock(); byte[] magic = data.ReadBytes(2); informationBlock.Signature = Encoding.ASCII.GetString(magic); @@ -185,16 +521,423 @@ namespace BurnOutSharp.Builders private static ObjectTableEntry ParseObjectTableEntry(Stream data) { // TODO: Use marshalling here instead of building - ObjectTableEntry objectTableEntry = new ObjectTableEntry(); + var entry = 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(); + entry.VirtualSegmentSize = data.ReadUInt32(); + entry.RelocationBaseAddress = data.ReadUInt32(); + entry.ObjectFlags = (ObjectFlags)data.ReadUInt16(); + entry.PageTableIndex = data.ReadUInt32(); + entry.PageTableEntries = data.ReadUInt32(); + entry.Reserved = data.ReadUInt32(); - return objectTableEntry; + return entry; + } + + /// + /// Parse a Stream into an object page map entry + /// + /// Stream to parse + /// Filled object page map entry on success, null on error + private static ObjectPageMapEntry ParseObjectPageMapEntry(Stream data) + { + // TODO: Use marshalling here instead of building + var entry = new ObjectPageMapEntry(); + + entry.PageDataOffset = data.ReadUInt32(); + entry.DataSize = data.ReadUInt16(); + entry.Flags = (ObjectPageFlags)data.ReadUInt16(); + + return entry; + } + + /// + /// Parse a Stream into a resource table entry + /// + /// Stream to parse + /// Filled resource table entry on success, null on error + private static ResourceTableEntry ParseResourceTableEntry(Stream data) + { + // TODO: Use marshalling here instead of building + var entry = new ResourceTableEntry(); + + entry.TypeID = (ResourceTableEntryType)data.ReadUInt32(); + entry.NameID = data.ReadUInt16(); + entry.ResourceSize = data.ReadUInt32(); + entry.ObjectNumber = data.ReadUInt16(); + entry.Offset = data.ReadUInt32(); + + return entry; + } + + /// + /// Parse a Stream into a resident names table entry + /// + /// Stream to parse + /// Filled resident names table entry on success, null on error + private static ResidentNamesTableEntry ParseResidentNamesTableEntry(Stream data) + { + // TODO: Use marshalling here instead of building + var entry = new ResidentNamesTableEntry(); + + entry.Length = data.ReadByteValue(); + if (entry.Length > 0) + { + byte[] name = data.ReadBytes(entry.Length); + entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); + } + entry.OrdinalNumber = data.ReadUInt16(); + + return entry; + } + + /// + /// Parse a Stream into an entry table bundle + /// + /// Stream to parse + /// Filled entry table bundle on success, null on error + private static EntryTableBundle ParseEntryTableBundle(Stream data) + { + // TODO: Use marshalling here instead of building + var bundle = new EntryTableBundle(); + + bundle.Entries = data.ReadByteValue(); + if (bundle.Entries == 0) + return bundle; + + bundle.BundleType = (BundleType)data.ReadByteValue(); + bundle.TableEntries = new EntryTableEntry[bundle.Entries]; + for (int i = 0; i < bundle.Entries; i++) + { + var entry = new EntryTableEntry(); + + switch (bundle.BundleType & ~BundleType.ParameterTypingInformationPresent) + { + case BundleType.UnusedEntry: + // Empty entry with no information + break; + + case BundleType.SixteenBitEntry: + entry.SixteenBitObjectNumber = data.ReadUInt16(); + entry.SixteenBitEntryFlags = (EntryFlags)data.ReadByteValue(); + entry.SixteenBitOffset = data.ReadUInt16(); + break; + + case BundleType.TwoEightySixCallGateEntry: + entry.TwoEightySixObjectNumber = data.ReadUInt16(); + entry.TwoEightySixEntryFlags = (EntryFlags)data.ReadByteValue(); + entry.TwoEightySixOffset = data.ReadUInt16(); + entry.TwoEightySixCallgate = data.ReadUInt16(); + break; + + case BundleType.ThirtyTwoBitEntry: + entry.ThirtyTwoBitObjectNumber = data.ReadUInt16(); + entry.ThirtyTwoBitEntryFlags = (EntryFlags)data.ReadByteValue(); + entry.ThirtyTwoBitOffset = data.ReadUInt32(); + break; + + case BundleType.ForwarderEntry: + entry.ForwarderReserved = data.ReadUInt16(); + entry.ForwarderFlags = (ForwarderFlags)data.ReadByteValue(); + entry.ForwarderModuleOrdinalNumber = data.ReadUInt16(); + entry.ProcedureNameOffset = data.ReadUInt32(); + entry.ImportOrdinalNumber = data.ReadUInt32(); + break; + + default: + return null; + } + + bundle.TableEntries[i] = entry; + } + + return bundle; + } + + /// + /// Parse a Stream into a module format directives table entry + /// + /// Stream to parse + /// Filled module format directives table entry on success, null on error + private static ModuleFormatDirectivesTableEntry ParseModuleFormatDirectivesTableEntry(Stream data) + { + // TODO: Use marshalling here instead of building + var entry = new ModuleFormatDirectivesTableEntry(); + + entry.DirectiveNumber = (DirectiveNumber)data.ReadUInt16(); + entry.DirectiveDataLength = data.ReadUInt16(); + entry.DirectiveDataOffset = data.ReadUInt32(); + + return entry; + } + + /// + /// Parse a Stream into a verify record directive table entry + /// + /// Stream to parse + /// Filled verify record directive table entry on success, null on error + private static VerifyRecordDirectiveTableEntry ParseVerifyRecordDirectiveTableEntry(Stream data) + { + // TODO: Use marshalling here instead of building + var entry = new VerifyRecordDirectiveTableEntry(); + + entry.EntryCount = data.ReadUInt16(); + entry.OrdinalIndex = data.ReadUInt16(); + entry.Version = data.ReadUInt16(); + entry.ObjectEntriesCount = data.ReadUInt16(); + entry.ObjectNumberInModule = data.ReadUInt16(); + entry.ObjectLoadBaseAddress = data.ReadUInt16(); + entry.ObjectVirtualAddressSize = data.ReadUInt16(); + + return entry; + } + + /// + /// Parse a Stream into a fix-up page table entry + /// + /// Stream to parse + /// Filled fix-up page table entry on success, null on error + private static FixupPageTableEntry ParseFixupPageTableEntry(Stream data) + { + // TODO: Use marshalling here instead of building + var entry = new FixupPageTableEntry(); + + entry.Offset = data.ReadUInt32(); + + return entry; + } + + /// + /// Parse a Stream into a fix-up record table entry + /// + /// Stream to parse + /// Filled fix-up record table entry on success, null on error + private static FixupRecordTableEntry ParseFixupRecordTableEntry(Stream data) + { + // TODO: Use marshalling here instead of building + var entry = new FixupRecordTableEntry(); + + entry.SourceType = (FixupRecordSourceType)data.ReadByteValue(); + entry.TargetFlags = (FixupRecordTargetFlags)data.ReadByteValue(); + + // Source list flag + if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag)) + entry.SourceOffsetListCount = data.ReadByteValue(); + else + entry.SourceOffset = data.ReadUInt16(); + + switch (entry.TargetFlags & FixupRecordTargetFlags.FixupTargetTypeMask) + { + // OBJECT / TRGOFF + case FixupRecordTargetFlags.InternalReference: + + // 16-bit Object Number/Module Ordinal Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + entry.TargetObjectNumberWORD = data.ReadUInt16(); + else + entry.TargetObjectNumberByte = data.ReadByteValue(); + + // 16-bit Selector fixup + if (!entry.SourceType.HasFlag(FixupRecordSourceType.SixteenBitSelectorFixup)) + { + // 32-bit Target Offset Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) + entry.TargetOffsetDWORD = data.ReadUInt32(); + else + entry.TargetOffsetWORD = data.ReadUInt16(); + } + + break; + + // MOD ORD# / IMPORT ORD / ADDITIVE + case FixupRecordTargetFlags.ImportedReferenceByOrdinal: + + // 16-bit Object Number/Module Ordinal Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16(); + else + entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue(); + + // 8-bit Ordinal Flag & 32-bit Target Offset Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.EightBitOrdinalFlag)) + entry.ImportedOrdinalNumberByte = data.ReadByteValue(); + else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) + entry.ImportedOrdinalNumberDWORD = data.ReadUInt32(); + else + entry.ImportedOrdinalNumberWORD = data.ReadUInt16(); + + // Additive Fixup Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) + { + // 32-bit Additive Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) + entry.AdditiveFixupValueDWORD = data.ReadUInt32(); + else + entry.AdditiveFixupValueWORD = data.ReadUInt16(); + } + + break; + + // MOD ORD# / PROCEDURE NAME OFFSET / ADDITIVE + case FixupRecordTargetFlags.ImportedReferenceByName: + + // 16-bit Object Number/Module Ordinal Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16(); + else + entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue(); + + // 32-bit Target Offset Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) + entry.OffsetImportProcedureNameTableDWORD = data.ReadUInt32(); + else + entry.OffsetImportProcedureNameTableWORD = data.ReadUInt16(); + + // Additive Fixup Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) + { + // 32-bit Additive Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) + entry.AdditiveFixupValueDWORD = data.ReadUInt32(); + else + entry.AdditiveFixupValueWORD = data.ReadUInt16(); + } + + break; + + // ORD # / ADDITIVE + case FixupRecordTargetFlags.InternalReferenceViaEntryTable: + + // 16-bit Object Number/Module Ordinal Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16(); + else + entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue(); + + // Additive Fixup Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) + { + // 32-bit Additive Flag + if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) + entry.AdditiveFixupValueDWORD = data.ReadUInt32(); + else + entry.AdditiveFixupValueWORD = data.ReadUInt16(); + } + + break; + } + + #region SCROFFn + + if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag)) + { + entry.SourceOffsetList = new ushort[entry.SourceOffsetListCount]; + for (int i = 0; i < entry.SourceOffsetList.Length; i++) + { + entry.SourceOffsetList[i] = data.ReadUInt16(); + } + } + + #endregion + + return entry; + } + + /// + /// Parse a Stream into a import module name table entry + /// + /// Stream to parse + /// Filled import module name table entry on success, null on error + private static ImportModuleNameTableEntry ParseImportModuleNameTableEntry(Stream data) + { + // TODO: Use marshalling here instead of building + var entry = new ImportModuleNameTableEntry(); + + entry.Length = data.ReadByteValue(); + if (entry.Length > 0) + { + byte[] name = data.ReadBytes(entry.Length); + entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); + } + + return entry; + } + + /// + /// Parse a Stream into a import module name table entry + /// + /// Stream to parse + /// Filled import module name table entry on success, null on error + private static ImportModuleProcedureNameTableEntry ParseImportModuleProcedureNameTableEntry(Stream data) + { + // TODO: Use marshalling here instead of building + var entry = new ImportModuleProcedureNameTableEntry(); + + entry.Length = data.ReadByteValue(); + if (entry.Length > 0) + { + byte[] name = data.ReadBytes(entry.Length); + entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); + } + + return entry; + } + + /// + /// Parse a Stream into a per-page checksum table entry + /// + /// Stream to parse + /// Filled per-page checksum table entry on success, null on error + private static PerPageChecksumTableEntry ParsePerPageChecksumTableEntry(Stream data) + { + // TODO: Use marshalling here instead of building + var entry = new PerPageChecksumTableEntry(); + + entry.Checksum = data.ReadUInt32(); + + return entry; + } + + /// + /// Parse a Stream into a non-resident names table entry + /// + /// Stream to parse + /// Filled non-resident names table entry on success, null on error + private static NonResidentNamesTableEntry ParseNonResidentNameTableEntry(Stream data) + { + // TODO: Use marshalling here instead of building + var entry = new NonResidentNamesTableEntry(); + + entry.Length = data.ReadByteValue(); + if (entry.Length > 0) + { + byte[] name = data.ReadBytes(entry.Length); + entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); + } + entry.OrdinalNumber = data.ReadUInt16(); + + return entry; + } + + /// + /// Parse a Stream into a debug information + /// + /// Stream to parse + /// Total size of the debug information + /// Filled debug information on success, null on error + private static DebugInformation ParseDebugInformation(Stream data, long size) + { + // TODO: Use marshalling here instead of building + var debugInformation = new DebugInformation(); + + byte[] signature = data.ReadBytes(3); + debugInformation.Signature = Encoding.ASCII.GetString(signature); + if (debugInformation.Signature != DebugInformationSignatureString) + return null; + + debugInformation.FormatType = (DebugFormatType)data.ReadByteValue(); + debugInformation.DebuggerData = data.ReadBytes((int)(size - 4)); + + return debugInformation; } #endregion diff --git a/BurnOutSharp.Models/LinearExecutable/DebugInformation.cs b/BurnOutSharp.Models/LinearExecutable/DebugInformation.cs index f3b7e687..e916de4c 100644 --- a/BurnOutSharp.Models/LinearExecutable/DebugInformation.cs +++ b/BurnOutSharp.Models/LinearExecutable/DebugInformation.cs @@ -27,10 +27,12 @@ namespace BurnOutSharp.Models.LinearExecutable /// public DebugFormatType FormatType; - // DEBUGGER DATA = Debugger specific data. - // The format of the debugger data is defined by the debugger that is being used. - // The values defined for the type field are not enforced by the system. It is - // the responsibility of the linker or debugging tools to follow the convention - // for the type field that is defined here. + /// + /// The format of the debugger data is defined by the debugger that is being used. + /// The values defined for the type field are not enforced by the system. It is + /// the responsibility of the linker or debugging tools to follow the convention + /// for the type field that is defined here. + /// + public byte[] DebuggerData; } } diff --git a/BurnOutSharp.Models/LinearExecutable/EntryTableBundle.cs b/BurnOutSharp.Models/LinearExecutable/EntryTableBundle.cs new file mode 100644 index 00000000..955515bf --- /dev/null +++ b/BurnOutSharp.Models/LinearExecutable/EntryTableBundle.cs @@ -0,0 +1,59 @@ +using System.Runtime.InteropServices; + +namespace BurnOutSharp.Models.LinearExecutable +{ + /// + /// The entry table contains object and offset information that is used to resolve + /// fixup references to the entry points within this module. Not all entry points + /// in the entry table will be exported, some entry points will only be used + /// within the module. An ordinal number is used to index into the entry table. + /// The entry table entries are numbered starting from one. + /// + /// The list of entries are compressed into 'bundles', where possible. The entries + /// within each bundle are all the same size. A bundle starts with a count field + /// which indicates the number of entries in the bundle. The count is followed by + /// a type field which identifies the bundle format. This provides both a means + /// for saving space as well as a mechanism for extending the bundle types. + /// + /// + /// + [StructLayout(LayoutKind.Sequential)] + public sealed class EntryTableBundle + { + /// + /// Number of entries. + /// + /// + /// This is the number of entries in this bundle. + /// + /// A zero value for the number of entries identifies the end of the + /// entry table. There is no further bundle information when the number + /// of entries is zero. In other words the entry table is terminated by + /// a single zero byte. + /// + /// For , this is the number of unused + /// entries to skip. + /// For , this is the number of 16-bit + /// entries in this bundle. The flags and offset value are repeated this + /// number of times. + /// For , this is the number + /// of 286 call gate entries in this bundle. The flags, callgate, and offset + /// value are repeated this number of times. + /// For , this is the number + /// of 32-bit entries in this bundle. The flags and offset value are repeated + /// this number of times. + /// For , this field is reserved for future use. + /// + public byte Entries; + + /// + /// This defines the bundle type which determines the contents of the BUNDLE INFO. + /// + public BundleType BundleType; + + /// + /// Table entries in the bundle + /// + public EntryTableEntry[] TableEntries; + } +} diff --git a/BurnOutSharp.Models/LinearExecutable/EntryTableEntry.cs b/BurnOutSharp.Models/LinearExecutable/EntryTableEntry.cs index af7713a6..52c0c9ca 100644 --- a/BurnOutSharp.Models/LinearExecutable/EntryTableEntry.cs +++ b/BurnOutSharp.Models/LinearExecutable/EntryTableEntry.cs @@ -2,55 +2,11 @@ namespace BurnOutSharp.Models.LinearExecutable { - /// - /// The entry table contains object and offset information that is used to resolve - /// fixup references to the entry points within this module. Not all entry points - /// in the entry table will be exported, some entry points will only be used - /// within the module. An ordinal number is used to index into the entry table. - /// The entry table entries are numbered starting from one. - /// - /// The list of entries are compressed into 'bundles', where possible. The entries - /// within each bundle are all the same size. A bundle starts with a count field - /// which indicates the number of entries in the bundle. The count is followed by - /// a type field which identifies the bundle format. This provides both a means - /// for saving space as well as a mechanism for extending the bundle types. - /// /// /// [StructLayout(LayoutKind.Explicit)] public sealed class EntryTableEntry { - /// - /// Number of entries. - /// - /// - /// This is the number of entries in this bundle. - /// - /// A zero value for the number of entries identifies the end of the - /// entry table. There is no further bundle information when the number - /// of entries is zero. In other words the entry table is terminated by - /// a single zero byte. - /// - /// For , this is the number of unused - /// entries to skip. - /// For , this is the number of 16-bit - /// entries in this bundle. The flags and offset value are repeated this - /// number of times. - /// For , this is the number - /// of 286 call gate entries in this bundle. The flags, callgate, and offset - /// value are repeated this number of times. - /// For , this is the number - /// of 32-bit entries in this bundle. The flags and offset value are repeated - /// this number of times. - /// For , this field is reserved for future use. - /// - [FieldOffset(0)] public byte Entries; - - /// - /// This defines the bundle type which determines the contents of the BUNDLE INFO. - /// - [FieldOffset(1)] public BundleType BundleType; - #region 16-bit Entry /// @@ -59,7 +15,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// This is the object number for the entries in this bundle. /// - [FieldOffset(2)] public ushort SixteenBitObjectNumber; + [FieldOffset(0)] public ushort SixteenBitObjectNumber; /// /// Entry flags. @@ -67,7 +23,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// These are the flags for this entry point. /// - [FieldOffset(4)] public EntryFlags SixteenBitEntryFlags; + [FieldOffset(2)] public EntryFlags SixteenBitEntryFlags; /// /// Offset in object. @@ -75,7 +31,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// This is the offset in the object for the entry point defined at this ordinal number. /// - [FieldOffset(5)] public ushort SixteenBitOffset; + [FieldOffset(3)] public ushort SixteenBitOffset; #endregion @@ -87,7 +43,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// This is the object number for the entries in this bundle. /// - [FieldOffset(2)] public ushort TwoEightySixObjectNumber; + [FieldOffset(0)] public ushort TwoEightySixObjectNumber; /// /// Entry flags. @@ -95,7 +51,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// These are the flags for this entry point. /// - [FieldOffset(4)] public EntryFlags TwoEightySixEntryFlags; + [FieldOffset(2)] public EntryFlags TwoEightySixEntryFlags; /// /// Offset in object. @@ -103,7 +59,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// This is the offset in the object for the entry point defined at this ordinal number. /// - [FieldOffset(5)] public ushort TwoEightySixOffset; + [FieldOffset(3)] public ushort TwoEightySixOffset; /// /// Callgate selector. @@ -115,7 +71,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// offset is place in the relocation fixup address. The segment number and offset /// in segment is placed in the LDT callgate. /// - [FieldOffset(7)] public ushort TwoEightySixCallgate; + [FieldOffset(5)] public ushort TwoEightySixCallgate; #endregion @@ -127,7 +83,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// This is the object number for the entries in this bundle. /// - [FieldOffset(2)] public ushort ThirtyTwoBitObjectNumber; + [FieldOffset(0)] public ushort ThirtyTwoBitObjectNumber; /// /// Entry flags. @@ -135,7 +91,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// These are the flags for this entry point. /// - [FieldOffset(4)] public EntryFlags ThirtyTwoBitEntryFlags; + [FieldOffset(2)] public EntryFlags ThirtyTwoBitEntryFlags; /// /// Offset in object. @@ -143,7 +99,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// This is the offset in the object for the entry point defined at this ordinal number. /// - [FieldOffset(5)] public uint ThirtyTwoBitOffset; + [FieldOffset(3)] public uint ThirtyTwoBitOffset; #endregion @@ -155,7 +111,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// This field is reserved for future use. /// - [FieldOffset(2)] public ushort ForwarderReserved; + [FieldOffset(0)] public ushort ForwarderReserved; /// /// Forwarder flags. @@ -163,7 +119,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// These are the flags for this entry point. /// - [FieldOffset(4)] public ForwarderFlags ForwarderFlags; + [FieldOffset(2)] public ForwarderFlags ForwarderFlags; /// /// Module Ordinal Number @@ -171,7 +127,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// This is the index into the Import Module Name Table for this forwarder. /// - [FieldOffset(5)] public ushort ForwarderModuleOrdinalNumber; + [FieldOffset(3)] public ushort ForwarderModuleOrdinalNumber; /// /// Procedure Name Offset @@ -199,7 +155,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// single libraries, one could provide entry points for the three libraries /// that are forwarders pointing to the common implementation. /// - [FieldOffset(7)] public uint ProcedureNameOffset; + [FieldOffset(5)] public uint ProcedureNameOffset; /// /// Import Ordinal Number @@ -227,8 +183,8 @@ namespace BurnOutSharp.Models.LinearExecutable /// single libraries, one could provide entry points for the three libraries /// that are forwarders pointing to the common implementation. /// - [FieldOffset(7)] public uint ImportOrdinalNumber; + [FieldOffset(5)] public uint ImportOrdinalNumber; #endregion } -} +} \ No newline at end of file diff --git a/BurnOutSharp.Models/LinearExecutable/Executable.cs b/BurnOutSharp.Models/LinearExecutable/Executable.cs index 8f9b0a93..42f28312 100644 --- a/BurnOutSharp.Models/LinearExecutable/Executable.cs +++ b/BurnOutSharp.Models/LinearExecutable/Executable.cs @@ -26,11 +26,11 @@ namespace BurnOutSharp.Models.LinearExecutable public ObjectTableEntry[] ObjectTable { get; set; } /// - /// Object page table + /// Object page map /// - public ObjectPageTableEntry[] ObjectPageTable { get; set; } + public ObjectPageMapEntry[] ObjectPageMap { get; set; } - // TODO: Object iterate data map table [Does this exist?] + // TODO: Object iterate data map table (Undefined) /// /// Resource table @@ -40,12 +40,12 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// Resident Name table /// - public ResidentNameTableEntry[] ResidentNameTable { get; set; } + public ResidentNamesTableEntry[] ResidentNamesTable { get; set; } /// /// Entry table /// - public EntryTableEntry[] EntryTable { get; set; } + public EntryTableBundle[] EntryTable { get; set; } /// /// Module format directives table (optional) @@ -57,11 +57,6 @@ namespace BurnOutSharp.Models.LinearExecutable /// public VerifyRecordDirectiveTableEntry[] VerifyRecordDirectiveTable { get; set; } - /// - /// Per-Page checksum table - /// - public PerPageChecksumTableEntry[] PerPageChecksumTable { get; set; } - /// /// Fix-up page table /// @@ -82,14 +77,15 @@ namespace BurnOutSharp.Models.LinearExecutable /// public ImportModuleProcedureNameTableEntry[] ImportModuleProcedureNameTable { get; set; } - // TODO: Preload Pages - // TODO: Demand Load Pages - // TODO: Iterated Pages + /// + /// Per-Page checksum table + /// + public PerPageChecksumTableEntry[] PerPageChecksumTable { get; set; } /// /// Non-Resident Name table /// - public NonResidentNameTableEntry[] NonResidentNameTable { get; set; } + public NonResidentNamesTableEntry[] NonResidentNamesTable { get; set; } // TODO: Non-resident directives data (Undefined) diff --git a/BurnOutSharp.Models/LinearExecutable/ImportModuleNameTableEntry.cs b/BurnOutSharp.Models/LinearExecutable/ImportModuleNameTableEntry.cs index d590df16..789aaac7 100644 --- a/BurnOutSharp.Models/LinearExecutable/ImportModuleNameTableEntry.cs +++ b/BurnOutSharp.Models/LinearExecutable/ImportModuleNameTableEntry.cs @@ -36,6 +36,6 @@ namespace BurnOutSharp.Models.LinearExecutable /// This is a variable length string with it's length defined in bytes by /// the LEN field. The string is case sensitive and is not null terminated. /// - public byte[] Name; + public string Name; } } diff --git a/BurnOutSharp.Models/LinearExecutable/ImportProcedureNameTableEntry.cs b/BurnOutSharp.Models/LinearExecutable/ImportProcedureNameTableEntry.cs index 8c51ed2d..e8842343 100644 --- a/BurnOutSharp.Models/LinearExecutable/ImportProcedureNameTableEntry.cs +++ b/BurnOutSharp.Models/LinearExecutable/ImportProcedureNameTableEntry.cs @@ -44,6 +44,6 @@ namespace BurnOutSharp.Models.LinearExecutable /// This is a variable length string with it's length defined in bytes by /// the LEN field. The string is case sensitive and is not null terminated. /// - public byte[] Name; + public string Name; } } diff --git a/BurnOutSharp.Models/LinearExecutable/NonResidentNameTableEntry.cs b/BurnOutSharp.Models/LinearExecutable/NonResidentNamesTableEntry.cs similarity index 97% rename from BurnOutSharp.Models/LinearExecutable/NonResidentNameTableEntry.cs rename to BurnOutSharp.Models/LinearExecutable/NonResidentNamesTableEntry.cs index bd43420e..63b127ff 100644 --- a/BurnOutSharp.Models/LinearExecutable/NonResidentNameTableEntry.cs +++ b/BurnOutSharp.Models/LinearExecutable/NonResidentNamesTableEntry.cs @@ -28,7 +28,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// [StructLayout(LayoutKind.Sequential)] - public sealed class NonResidentNameTableEntry + public sealed class NonResidentNamesTableEntry { /// /// String Length. @@ -51,7 +51,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// This is a variable length string with it's length defined in bytes by the LEN field. /// The string is case case sensitive and is not null terminated. /// - public byte[] Name; + public string Name; /// /// Ordinal number. diff --git a/BurnOutSharp.Models/LinearExecutable/ObjectPageTableEntry.cs b/BurnOutSharp.Models/LinearExecutable/ObjectPageMapEntry.cs similarity index 98% rename from BurnOutSharp.Models/LinearExecutable/ObjectPageTableEntry.cs rename to BurnOutSharp.Models/LinearExecutable/ObjectPageMapEntry.cs index d4750883..9e63caef 100644 --- a/BurnOutSharp.Models/LinearExecutable/ObjectPageTableEntry.cs +++ b/BurnOutSharp.Models/LinearExecutable/ObjectPageMapEntry.cs @@ -16,7 +16,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// [StructLayout(LayoutKind.Sequential)] - public sealed class ObjectPageTableEntry + public sealed class ObjectPageMapEntry { /// /// Offset to the page data in the EXE file. diff --git a/BurnOutSharp.Models/LinearExecutable/ResidentNameTableEntry.cs b/BurnOutSharp.Models/LinearExecutable/ResidentNamesTableEntry.cs similarity index 88% rename from BurnOutSharp.Models/LinearExecutable/ResidentNameTableEntry.cs rename to BurnOutSharp.Models/LinearExecutable/ResidentNamesTableEntry.cs index 5f3b69ae..133c903e 100644 --- a/BurnOutSharp.Models/LinearExecutable/ResidentNameTableEntry.cs +++ b/BurnOutSharp.Models/LinearExecutable/ResidentNamesTableEntry.cs @@ -11,12 +11,12 @@ namespace BurnOutSharp.Models.LinearExecutable /// information in the entry table. /// /// The resident name table is kept resident in system memory while the module is - /// loaded.It is intended to contain the exported entry point names that are - /// frequently dynamically linked to by name.Non-resident names are not kept in + /// loaded. It is intended to contain the exported entry point names that are + /// frequently dynamically linked to by name. Non-resident names are not kept in /// memory and are read from the EXE file when a dynamic link reference is made. /// Exported entry point names that are infrequently dynamically linked to by name /// or are commonly referenced by ordinal number should be placed in the - /// non-resident name table.The trade off made for references by name is performance + /// non-resident name table. The trade off made for references by name is performance /// vs memory usage. /// /// Import references by name require these tables to be searched to obtain the entry @@ -28,7 +28,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// /// [StructLayout(LayoutKind.Sequential)] - public sealed class ResidentNameTableEntry + public sealed class ResidentNamesTableEntry { /// /// String Length. @@ -51,7 +51,7 @@ namespace BurnOutSharp.Models.LinearExecutable /// This is a variable length string with it's length defined in bytes by the LEN field. /// The string is case case sensitive and is not null terminated. /// - public byte[] Name; + public string Name; /// /// Ordinal number. diff --git a/BurnOutSharp.Wrappers/LinearExecutable.cs b/BurnOutSharp.Wrappers/LinearExecutable.cs index ee62dffe..112ceb66 100644 --- a/BurnOutSharp.Wrappers/LinearExecutable.cs +++ b/BurnOutSharp.Wrappers/LinearExecutable.cs @@ -223,7 +223,7 @@ namespace BurnOutSharp.Wrappers public Models.LinearExecutable.ObjectTableEntry[] ObjectTable => _executable.ObjectTable; /// - public Models.LinearExecutable.ObjectPageTableEntry[] ObjectPageTable => _executable.ObjectPageTable; + public Models.LinearExecutable.ObjectPageMapEntry[] ObjectPageTable => _executable.ObjectPageMap; // TODO: Object iterate data map table [Does this exist?] @@ -231,10 +231,10 @@ namespace BurnOutSharp.Wrappers public Models.LinearExecutable.ResourceTableEntry[] ResourceTable => _executable.ResourceTable; /// - public Models.LinearExecutable.ResidentNameTableEntry[] ResidentNameTable => _executable.ResidentNameTable; + public Models.LinearExecutable.ResidentNamesTableEntry[] ResidentNameTable => _executable.ResidentNamesTable; /// - public Models.LinearExecutable.EntryTableEntry[] EntryTable => _executable.EntryTable; + public Models.LinearExecutable.EntryTableBundle[] EntryTable => _executable.EntryTable; /// public Models.LinearExecutable.ModuleFormatDirectivesTableEntry[] ModuleFormatDirectivesTable => _executable.ModuleFormatDirectivesTable; @@ -262,7 +262,7 @@ namespace BurnOutSharp.Wrappers // TODO: Iterated Pages /// - public Models.LinearExecutable.NonResidentNameTableEntry[] NonResidentNameTable => _executable.NonResidentNameTable; + public Models.LinearExecutable.NonResidentNamesTableEntry[] NonResidentNameTable => _executable.NonResidentNamesTable; /// public Models.LinearExecutable.DebugInformation DebugInformation => _executable.DebugInformation;