Migrate remaining models from Models

This commit is contained in:
Matt Nadareski
2025-09-26 12:09:34 -04:00
parent 2e7f18c407
commit 5af4de2878
244 changed files with 16541 additions and 219 deletions

View File

@@ -28,7 +28,6 @@
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
<PackageReference Include="SabreTools.Models" Version="1.7.2" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -2,8 +2,8 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.LinearExecutable;
using static SabreTools.Models.LinearExecutable.Constants;
using SabreTools.Serialization.Models.LinearExecutable;
using static SabreTools.Serialization.Models.LinearExecutable.Constants;
namespace SabreTools.Serialization.Deserializers
{

View File

@@ -1,8 +1,8 @@
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.MSDOS;
using static SabreTools.Models.MSDOS.Constants;
using SabreTools.Serialization.Models.MSDOS;
using static SabreTools.Serialization.Models.MSDOS.Constants;
namespace SabreTools.Serialization.Deserializers
{

View File

@@ -2,9 +2,9 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.NewExecutable;
using SabreTools.Serialization.Extensions;
using static SabreTools.Models.NewExecutable.Constants;
using SabreTools.Serialization.Models.NewExecutable;
using static SabreTools.Serialization.Models.NewExecutable.Constants;
namespace SabreTools.Serialization.Deserializers
{
@@ -253,7 +253,7 @@ namespace SabreTools.Serialization.Deserializers
obj.MovableEntriesCount = data.ReadUInt16LittleEndian();
obj.SegmentAlignmentShiftCount = data.ReadUInt16LittleEndian();
obj.ResourceEntriesCount = data.ReadUInt16LittleEndian();
obj.TargetOperatingSystem = (SabreTools.Models.NewExecutable.OperatingSystem)data.ReadByteValue();
obj.TargetOperatingSystem = (SabreTools.Serialization.Models.NewExecutable.OperatingSystem)data.ReadByteValue();
obj.AdditionalFlags = (OS2Flag)data.ReadByteValue();
obj.ReturnThunkOffset = data.ReadUInt16LittleEndian();
obj.SegmentReferenceThunkOffset = data.ReadUInt16LittleEndian();

View File

@@ -3,12 +3,12 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.COFF;
using SabreTools.Models.COFF.SymbolTableEntries;
using SabreTools.Models.PortableExecutable;
using SabreTools.Serialization.Extensions;
using static SabreTools.Models.COFF.Constants;
using static SabreTools.Models.PortableExecutable.Constants;
using SabreTools.Serialization.Models.COFF;
using SabreTools.Serialization.Models.COFF.SymbolTableEntries;
using SabreTools.Serialization.Models.PortableExecutable;
using static SabreTools.Serialization.Models.COFF.Constants;
using static SabreTools.Serialization.Models.PortableExecutable.Constants;
namespace SabreTools.Serialization.Deserializers
{
@@ -74,7 +74,7 @@ namespace SabreTools.Serialization.Deserializers
#region Optional Header
// If the optional header exists
SabreTools.Models.PortableExecutable.OptionalHeader? optionalHeader = null;
SabreTools.Serialization.Models.PortableExecutable.OptionalHeader? optionalHeader = null;
if (fileHeader.SizeOfOptionalHeader > 0)
{
// Parse the optional header
@@ -337,12 +337,12 @@ namespace SabreTools.Serialization.Deserializers
int length = tableSize - tableOffset;
// Add the hidden entry
pex.ResourceDirectoryTable.Entries[localEntries.Length - 1] = new SabreTools.Models.PortableExecutable.Resource.DirectoryEntry
pex.ResourceDirectoryTable.Entries[localEntries.Length - 1] = new SabreTools.Serialization.Models.PortableExecutable.Resource.DirectoryEntry
{
Name = new SabreTools.Models.PortableExecutable.Resource.DirectoryString { UnicodeString = Encoding.Unicode.GetBytes("HIDDEN RESOURCE") },
Name = new SabreTools.Serialization.Models.PortableExecutable.Resource.DirectoryString { UnicodeString = Encoding.Unicode.GetBytes("HIDDEN RESOURCE") },
IntegerID = uint.MaxValue,
DataEntryOffset = (uint)tableOffset,
DataEntry = new SabreTools.Models.PortableExecutable.Resource.DataEntry
DataEntry = new SabreTools.Serialization.Models.PortableExecutable.Resource.DataEntry
{
Size = (uint)length,
Data = tableData.ReadBytes(ref tableOffset, length),
@@ -471,12 +471,12 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <returns>Filled attribute certificate on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.AttributeCertificate.Entry[]? ParseAttributeCertificateTable(byte[]? data)
public static SabreTools.Serialization.Models.PortableExecutable.AttributeCertificate.Entry[]? ParseAttributeCertificateTable(byte[]? data)
{
if (data == null)
return null;
var obj = new List<SabreTools.Models.PortableExecutable.AttributeCertificate.Entry>();
var obj = new List<SabreTools.Serialization.Models.PortableExecutable.AttributeCertificate.Entry>();
int offset = 0;
while (offset < data.Length)
@@ -502,9 +502,9 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <returns>Filled AttributeCertificateTableEntry on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.AttributeCertificate.Entry? ParseAttributeCertificateTableEntry(byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.AttributeCertificate.Entry? ParseAttributeCertificateTableEntry(byte[] data, ref int offset)
{
var obj = new SabreTools.Models.PortableExecutable.AttributeCertificate.Entry();
var obj = new SabreTools.Serialization.Models.PortableExecutable.AttributeCertificate.Entry();
obj.Length = data.ReadUInt32LittleEndian(ref offset);
if (obj.Length < 8)
@@ -526,9 +526,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled BaseRelocationBlock on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.BaseRelocation.Block? ParseBaseRelocationBlock(byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.BaseRelocation.Block? ParseBaseRelocationBlock(byte[] data, ref int offset)
{
var obj = new SabreTools.Models.PortableExecutable.BaseRelocation.Block();
var obj = new SabreTools.Serialization.Models.PortableExecutable.BaseRelocation.Block();
obj.PageRVA = data.ReadUInt32LittleEndian(ref offset);
obj.BlockSize = data.ReadUInt32LittleEndian(ref offset);
@@ -542,7 +542,7 @@ namespace SabreTools.Serialization.Deserializers
return obj;
int entryCount = ((int)obj.BlockSize - 8) / 2;
obj.TypeOffsetFieldEntries = new SabreTools.Models.PortableExecutable.BaseRelocation.TypeOffsetFieldEntry[entryCount];
obj.TypeOffsetFieldEntries = new SabreTools.Serialization.Models.PortableExecutable.BaseRelocation.TypeOffsetFieldEntry[entryCount];
for (int i = 0; i < obj.TypeOffsetFieldEntries.Length; i++)
{
if (offset + 2 >= data.Length)
@@ -559,12 +559,12 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <returns>Filled base relocation table on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.BaseRelocation.Block[]? ParseBaseRelocationTable(byte[]? data)
public static SabreTools.Serialization.Models.PortableExecutable.BaseRelocation.Block[]? ParseBaseRelocationTable(byte[]? data)
{
if (data == null)
return null;
var obj = new List<SabreTools.Models.PortableExecutable.BaseRelocation.Block>();
var obj = new List<SabreTools.Serialization.Models.PortableExecutable.BaseRelocation.Block>();
int offset = 0;
while (offset + 8 <= data.Length)
@@ -591,9 +591,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled BaseRelocationTypeOffsetFieldEntry on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.BaseRelocation.TypeOffsetFieldEntry ParseBaseRelocationTypeOffsetFieldEntry(byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.BaseRelocation.TypeOffsetFieldEntry ParseBaseRelocationTypeOffsetFieldEntry(byte[] data, ref int offset)
{
var obj = new SabreTools.Models.PortableExecutable.BaseRelocation.TypeOffsetFieldEntry();
var obj = new SabreTools.Serialization.Models.PortableExecutable.BaseRelocation.TypeOffsetFieldEntry();
ushort typeAndOffsetField = data.ReadUInt16LittleEndian(ref offset);
obj.BaseRelocationType = (BaseRelocationTypes)(typeAndOffsetField >> 12);
@@ -640,9 +640,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled DebugDirectoryEntry on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.DebugData.Entry ParseDebugDirectoryEntry(byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.DebugData.Entry ParseDebugDirectoryEntry(byte[] data, ref int offset)
{
var obj = new SabreTools.Models.PortableExecutable.DebugData.Entry();
var obj = new SabreTools.Serialization.Models.PortableExecutable.DebugData.Entry();
obj.Characteristics = data.ReadUInt32LittleEndian(ref offset);
obj.TimeDateStamp = data.ReadUInt32LittleEndian(ref offset);
@@ -661,14 +661,14 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <returns>Filled DebugTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.DebugData.Table? ParseDebugTable(byte[]? data)
public static SabreTools.Serialization.Models.PortableExecutable.DebugData.Table? ParseDebugTable(byte[]? data)
{
if (data == null)
return null;
var obj = new SabreTools.Models.PortableExecutable.DebugData.Table();
var obj = new SabreTools.Serialization.Models.PortableExecutable.DebugData.Table();
var table = new List<SabreTools.Models.PortableExecutable.DebugData.Entry>();
var table = new List<SabreTools.Serialization.Models.PortableExecutable.DebugData.Entry>();
int offset = 0;
while (offset < data.Length)
@@ -693,12 +693,12 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <returns>Filled DelayLoadDirectoryTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.DelayLoad.DirectoryTable? ParseDelayLoadDirectoryTable(byte[]? data)
public static SabreTools.Serialization.Models.PortableExecutable.DelayLoad.DirectoryTable? ParseDelayLoadDirectoryTable(byte[]? data)
{
if (data == null)
return null;
var obj = new SabreTools.Models.PortableExecutable.DelayLoad.DirectoryTable();
var obj = new SabreTools.Serialization.Models.PortableExecutable.DelayLoad.DirectoryTable();
int offset = 0;
obj.Attributes = data.ReadUInt32LittleEndian(ref offset);
@@ -737,9 +737,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Stream to parse</param>
/// <param name="entries">Number of entries in the table</param>
/// <returns>Filled ExportAddressTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Export.AddressTableEntry[] ParseExportAddressTable(Stream data, uint entries)
public static SabreTools.Serialization.Models.PortableExecutable.Export.AddressTableEntry[] ParseExportAddressTable(Stream data, uint entries)
{
var obj = new SabreTools.Models.PortableExecutable.Export.AddressTableEntry[entries];
var obj = new SabreTools.Serialization.Models.PortableExecutable.Export.AddressTableEntry[entries];
for (int i = 0; i < obj.Length; i++)
{
@@ -754,9 +754,9 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled ExportAddressTableEntry on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Export.AddressTableEntry ParseExportAddressTableEntry(Stream data)
public static SabreTools.Serialization.Models.PortableExecutable.Export.AddressTableEntry ParseExportAddressTableEntry(Stream data)
{
var obj = new SabreTools.Models.PortableExecutable.Export.AddressTableEntry();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Export.AddressTableEntry();
obj.ExportRVA = data.ReadUInt32LittleEndian();
obj.ForwarderRVA = obj.ExportRVA;
@@ -770,9 +770,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled ExportDirectoryTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Export.DirectoryTable ParseExportDirectoryTable(byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.Export.DirectoryTable ParseExportDirectoryTable(byte[] data, ref int offset)
{
var obj = new SabreTools.Models.PortableExecutable.Export.DirectoryTable();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Export.DirectoryTable();
obj.ExportFlags = data.ReadUInt32LittleEndian(ref offset);
obj.TimeDateStamp = data.ReadUInt32LittleEndian(ref offset);
@@ -797,9 +797,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="pointers">Set of pointers to process</param>
/// <param name="sections">Section table to use for virtual address translation</param>
/// <returns>Filled ExportNameTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Export.NameTable ParseExportNameTable(Stream data, long initialOffset, uint[] pointers, SectionHeader[] sections)
public static SabreTools.Serialization.Models.PortableExecutable.Export.NameTable ParseExportNameTable(Stream data, long initialOffset, uint[] pointers, SectionHeader[] sections)
{
var obj = new SabreTools.Models.PortableExecutable.Export.NameTable();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Export.NameTable();
obj.Strings = new string[pointers.Length];
for (int i = 0; i < obj.Strings.Length; i++)
@@ -827,9 +827,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Stream to parse</param>
/// <param name="entries">Number of entries in the table</param>
/// <returns>Filled ExportNamePointerTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Export.NamePointerTable ParseExportNamePointerTable(Stream data, uint entries)
public static SabreTools.Serialization.Models.PortableExecutable.Export.NamePointerTable ParseExportNamePointerTable(Stream data, uint entries)
{
var obj = new SabreTools.Models.PortableExecutable.Export.NamePointerTable();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Export.NamePointerTable();
obj.Pointers = new uint[entries];
for (int i = 0; i < obj.Pointers.Length; i++)
@@ -846,9 +846,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Stream to parse</param>
/// <param name="entries">Number of entries in the table</param>
/// <returns>Filled ExportOrdinalTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Export.OrdinalTable ParseExportOrdinalTable(Stream data, uint entries)
public static SabreTools.Serialization.Models.PortableExecutable.Export.OrdinalTable ParseExportOrdinalTable(Stream data, uint entries)
{
var obj = new SabreTools.Models.PortableExecutable.Export.OrdinalTable();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Export.OrdinalTable();
obj.Indexes = new ushort[entries];
for (int i = 0; i < obj.Indexes.Length; i++)
@@ -921,13 +921,13 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="importAddressTables">Import address tables</param>
/// <param name="sections">Section table to use for virtual address translation</param>
/// <returns>Filled HintNameTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Import.HintNameTableEntry[] ParseHintNameTable(Stream data,
public static SabreTools.Serialization.Models.PortableExecutable.Import.HintNameTableEntry[] ParseHintNameTable(Stream data,
long initialOffset,
Dictionary<int, SabreTools.Models.PortableExecutable.Import.LookupTableEntry[]?> importLookupTables,
Dictionary<int, SabreTools.Models.PortableExecutable.Import.AddressTableEntry[]?> importAddressTables,
Dictionary<int, SabreTools.Serialization.Models.PortableExecutable.Import.LookupTableEntry[]?> importLookupTables,
Dictionary<int, SabreTools.Serialization.Models.PortableExecutable.Import.AddressTableEntry[]?> importAddressTables,
SectionHeader[] sections)
{
var importHintNameTable = new List<SabreTools.Models.PortableExecutable.Import.HintNameTableEntry>();
var importHintNameTable = new List<SabreTools.Serialization.Models.PortableExecutable.Import.HintNameTableEntry>();
if (importLookupTables.Count > 0 || importAddressTables.Count > 0)
{
@@ -1005,9 +1005,9 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled HintNameTableEntry on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Import.HintNameTableEntry ParseHintNameTableEntry(Stream data)
public static SabreTools.Serialization.Models.PortableExecutable.Import.HintNameTableEntry ParseHintNameTableEntry(Stream data)
{
var obj = new SabreTools.Models.PortableExecutable.Import.HintNameTableEntry();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Import.HintNameTableEntry();
obj.Hint = data.ReadUInt16LittleEndian();
obj.Name = data.ReadNullTerminatedAnsiString();
@@ -1021,9 +1021,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Stream to parse</param>
/// <param name="magic">Optional header magic number indicating PE32 or PE32+</param>
/// <returns>Filled ImportAddressTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Import.AddressTableEntry[] ParseImportAddressTable(Stream data, OptionalHeaderMagicNumber magic)
public static SabreTools.Serialization.Models.PortableExecutable.Import.AddressTableEntry[] ParseImportAddressTable(Stream data, OptionalHeaderMagicNumber magic)
{
var obj = new List<SabreTools.Models.PortableExecutable.Import.AddressTableEntry>();
var obj = new List<SabreTools.Serialization.Models.PortableExecutable.Import.AddressTableEntry>();
// Loop until the last item (all nulls) are found
while (data.Position < data.Length)
@@ -1050,13 +1050,13 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="entries">Directory table entries containing the addresses</param>
/// <param name="sections">Section table to use for virtual address translation</param>
/// <returns>Filled ImportAddressTables on success, null on error</returns>
public static Dictionary<int, SabreTools.Models.PortableExecutable.Import.AddressTableEntry[]?> ParseImportAddressTables(Stream data,
public static Dictionary<int, SabreTools.Serialization.Models.PortableExecutable.Import.AddressTableEntry[]?> ParseImportAddressTables(Stream data,
long initialOffset,
OptionalHeaderMagicNumber magic,
SabreTools.Models.PortableExecutable.Import.DirectoryTableEntry[] entries,
SabreTools.Serialization.Models.PortableExecutable.Import.DirectoryTableEntry[] entries,
SectionHeader[] sections)
{
var obj = new Dictionary<int, SabreTools.Models.PortableExecutable.Import.AddressTableEntry[]?>();
var obj = new Dictionary<int, SabreTools.Serialization.Models.PortableExecutable.Import.AddressTableEntry[]?>();
for (int i = 0; i < entries.Length; i++)
{
@@ -1083,9 +1083,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Stream to parse</param>
/// <param name="magic">Optional header magic number</param>
/// <returns>Filled ImportAddressTableEntry on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Import.AddressTableEntry ParseImportAddressTableEntry(Stream data, OptionalHeaderMagicNumber magic)
public static SabreTools.Serialization.Models.PortableExecutable.Import.AddressTableEntry ParseImportAddressTableEntry(Stream data, OptionalHeaderMagicNumber magic)
{
var obj = new SabreTools.Models.PortableExecutable.Import.AddressTableEntry();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Import.AddressTableEntry();
if (magic == OptionalHeaderMagicNumber.PE32)
{
@@ -1115,9 +1115,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled ImportDirectoryTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Import.DirectoryTableEntry[] ParseImportDirectoryTable(byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.Import.DirectoryTableEntry[] ParseImportDirectoryTable(byte[] data, ref int offset)
{
var obj = new List<SabreTools.Models.PortableExecutable.Import.DirectoryTableEntry>();
var obj = new List<SabreTools.Serialization.Models.PortableExecutable.Import.DirectoryTableEntry>();
// Loop until the last item (all nulls) are found
while (offset < data.Length)
@@ -1143,9 +1143,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled ImportDirectoryTableEntry on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Import.DirectoryTableEntry ParseImportDirectoryTableEntry(byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.Import.DirectoryTableEntry ParseImportDirectoryTableEntry(byte[] data, ref int offset)
{
var obj = new SabreTools.Models.PortableExecutable.Import.DirectoryTableEntry();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Import.DirectoryTableEntry();
obj.ImportLookupTableRVA = data.ReadUInt32LittleEndian(ref offset);
obj.TimeDateStamp = data.ReadUInt32LittleEndian(ref offset);
@@ -1162,9 +1162,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Stream to parse</param>
/// <param name="magic">Optional header magic number indicating PE32 or PE32+</param>
/// <returns>Filled ImportLookupTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Import.LookupTableEntry[] ParseImportLookupTable(Stream data, OptionalHeaderMagicNumber magic)
public static SabreTools.Serialization.Models.PortableExecutable.Import.LookupTableEntry[] ParseImportLookupTable(Stream data, OptionalHeaderMagicNumber magic)
{
var obj = new List<SabreTools.Models.PortableExecutable.Import.LookupTableEntry>();
var obj = new List<SabreTools.Serialization.Models.PortableExecutable.Import.LookupTableEntry>();
// Loop until the last item (all nulls) are found
while (data.Position < data.Length)
@@ -1191,14 +1191,14 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="entries">Directory table entries containing the addresses</param>
/// <param name="sections">Section table to use for virtual address translation</param>
/// <returns>Filled ImportLookupTables on success, null on error</returns>
public static Dictionary<int, SabreTools.Models.PortableExecutable.Import.LookupTableEntry[]?> ParseImportLookupTables(Stream data,
public static Dictionary<int, SabreTools.Serialization.Models.PortableExecutable.Import.LookupTableEntry[]?> ParseImportLookupTables(Stream data,
long initialOffset,
OptionalHeaderMagicNumber magic,
SabreTools.Models.PortableExecutable.Import.DirectoryTableEntry[] entries,
SabreTools.Serialization.Models.PortableExecutable.Import.DirectoryTableEntry[] entries,
SectionHeader[] sections)
{
// Lookup tables
var obj = new Dictionary<int, SabreTools.Models.PortableExecutable.Import.LookupTableEntry[]?>();
var obj = new Dictionary<int, SabreTools.Serialization.Models.PortableExecutable.Import.LookupTableEntry[]?>();
for (int i = 0; i < entries.Length; i++)
{
@@ -1225,9 +1225,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Stream to parse</param>
/// <param name="magic">Optional header magic number</param>
/// <returns>Filled ImportLookupTableEntry on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Import.LookupTableEntry ParseImportLookupTableEntry(Stream data, OptionalHeaderMagicNumber magic)
public static SabreTools.Serialization.Models.PortableExecutable.Import.LookupTableEntry ParseImportLookupTableEntry(Stream data, OptionalHeaderMagicNumber magic)
{
var obj = new SabreTools.Models.PortableExecutable.Import.LookupTableEntry();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Import.LookupTableEntry();
if (magic == OptionalHeaderMagicNumber.PE32)
{
@@ -1275,11 +1275,11 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Stream to parse</param>
/// <param name="optionalSize">Size of the optional header</param>
/// <returns>Filled OptionalHeader on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.OptionalHeader ParseOptionalHeader(Stream data, int optionalSize)
public static SabreTools.Serialization.Models.PortableExecutable.OptionalHeader ParseOptionalHeader(Stream data, int optionalSize)
{
long initialOffset = data.Position;
var obj = new SabreTools.Models.PortableExecutable.OptionalHeader();
var obj = new SabreTools.Serialization.Models.PortableExecutable.OptionalHeader();
#region Standard Fields
@@ -1431,7 +1431,7 @@ namespace SabreTools.Serialization.Deserializers
ref int dataOffset,
long tableStart,
long tableLength,
SabreTools.Models.PortableExecutable.Resource.DirectoryTable? table,
SabreTools.Serialization.Models.PortableExecutable.Resource.DirectoryTable? table,
SectionHeader[] sections)
{
if (tableData == null)
@@ -1484,9 +1484,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled ResourceDataEntry on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Resource.DataEntry ParseResourceDataEntry(byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.Resource.DataEntry ParseResourceDataEntry(byte[] data, ref int offset)
{
var obj = new SabreTools.Models.PortableExecutable.Resource.DataEntry();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Resource.DataEntry();
obj.DataRVA = data.ReadUInt32LittleEndian(ref offset);
obj.Size = data.ReadUInt32LittleEndian(ref offset);
@@ -1503,9 +1503,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="offset">Offset into the byte array</param>
/// <param name="nameEntry">Indicates if the value is a name entry or not</param>
/// <returns>Filled ResourceDirectoryEntry on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Resource.DirectoryEntry ParseResourceDirectoryEntry(byte[] data, ref int offset, bool nameEntry)
public static SabreTools.Serialization.Models.PortableExecutable.Resource.DirectoryEntry ParseResourceDirectoryEntry(byte[] data, ref int offset, bool nameEntry)
{
var obj = new SabreTools.Models.PortableExecutable.Resource.DirectoryEntry();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Resource.DirectoryEntry();
// TODO: Figure out why the high bit is set for names
// The original version of this code also had this fix, but there
@@ -1532,9 +1532,9 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled ResourceDirectoryString on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Resource.DirectoryString ParseResourceDirectoryString(byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.Resource.DirectoryString ParseResourceDirectoryString(byte[] data, ref int offset)
{
var obj = new SabreTools.Models.PortableExecutable.Resource.DirectoryString();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Resource.DirectoryString();
obj.Length = data.ReadUInt16LittleEndian(ref offset);
if (obj.Length > 0 && offset + (obj.Length * 2) <= data.Length)
@@ -1549,12 +1549,12 @@ namespace SabreTools.Serialization.Deserializers
/// <param name="tableData">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled ResourceDirectoryTable on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Resource.DirectoryTable? ParseResourceDirectoryTable(byte[]? tableData, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.Resource.DirectoryTable? ParseResourceDirectoryTable(byte[]? tableData, ref int offset)
{
if (tableData == null)
return null;
var obj = new SabreTools.Models.PortableExecutable.Resource.DirectoryTable();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Resource.DirectoryTable();
obj.Characteristics = tableData.ReadUInt32LittleEndian(ref offset);
if (obj.Characteristics != 0)
@@ -1568,7 +1568,7 @@ namespace SabreTools.Serialization.Deserializers
// Create the entry array
int totalEntryCount = obj.NumberOfNameEntries + obj.NumberOfIDEntries;
obj.Entries = new SabreTools.Models.PortableExecutable.Resource.DirectoryEntry[totalEntryCount];
obj.Entries = new SabreTools.Serialization.Models.PortableExecutable.Resource.DirectoryEntry[totalEntryCount];
if (obj.Entries.Length == 0)
return obj;

View File

@@ -1,7 +1,7 @@
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.SecuROM;
using SabreTools.Serialization.Models.SecuROM;
namespace SabreTools.Serialization.Deserializers
{

View File

@@ -2,8 +2,8 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.SecuROM;
using static SabreTools.Models.SecuROM.Constants;
using SabreTools.Serialization.Models.SecuROM;
using static SabreTools.Serialization.Models.SecuROM.Constants;
namespace SabreTools.Serialization.Deserializers
{

View File

@@ -1,8 +1,8 @@
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.SecuROM;
using static SabreTools.Models.SecuROM.Constants;
using SabreTools.Serialization.Models.SecuROM;
using static SabreTools.Serialization.Models.SecuROM.Constants;
namespace SabreTools.Serialization.Deserializers
{

View File

@@ -2,7 +2,7 @@ using System;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.WiseInstaller;
using SabreTools.Serialization.Models.WiseInstaller;
namespace SabreTools.Serialization.Deserializers
{

View File

@@ -2,8 +2,8 @@ using System;
using System.Collections.Generic;
using System.IO;
using SabreTools.IO.Extensions;
using SabreTools.Models.WiseInstaller;
using SabreTools.Models.WiseInstaller.Actions;
using SabreTools.Serialization.Models.WiseInstaller;
using SabreTools.Serialization.Models.WiseInstaller.Actions;
namespace SabreTools.Serialization.Deserializers
{

View File

@@ -2,8 +2,8 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Extensions;
using SabreTools.Models.WiseInstaller;
using static SabreTools.Models.WiseInstaller.Constants;
using SabreTools.Serialization.Models.WiseInstaller;
using static SabreTools.Serialization.Models.WiseInstaller.Constants;
namespace SabreTools.Serialization.Deserializers
{

View File

@@ -1,4 +1,4 @@
using SabreTools.Models.NewExecutable;
using SabreTools.Serialization.Models.NewExecutable;
namespace SabreTools.Serialization.Extensions
{

View File

@@ -4,10 +4,10 @@ using System.IO;
using System.Text;
using System.Xml.Serialization;
using SabreTools.IO.Extensions;
using SabreTools.Models.COFF;
using SabreTools.Models.PortableExecutable;
using SabreTools.Models.PortableExecutable.Resource.Entries;
using SabreTools.Models.SecuROM;
using SabreTools.Serialization.Models.COFF;
using SabreTools.Serialization.Models.PortableExecutable;
using SabreTools.Serialization.Models.PortableExecutable.Resource.Entries;
using SabreTools.Serialization.Models.SecuROM;
namespace SabreTools.Serialization.Extensions
{
@@ -110,9 +110,9 @@ namespace SabreTools.Serialization.Extensions
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled NB10ProgramDatabase on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.DebugData.NB10ProgramDatabase? ParseNB10ProgramDatabase(this byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.DebugData.NB10ProgramDatabase? ParseNB10ProgramDatabase(this byte[] data, ref int offset)
{
var obj = new SabreTools.Models.PortableExecutable.DebugData.NB10ProgramDatabase();
var obj = new SabreTools.Serialization.Models.PortableExecutable.DebugData.NB10ProgramDatabase();
obj.Signature = data.ReadUInt32LittleEndian(ref offset);
if (obj.Signature != 0x3031424E)
@@ -132,9 +132,9 @@ namespace SabreTools.Serialization.Extensions
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled RSDSProgramDatabase on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.DebugData.RSDSProgramDatabase? ParseRSDSProgramDatabase(this byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.DebugData.RSDSProgramDatabase? ParseRSDSProgramDatabase(this byte[] data, ref int offset)
{
var obj = new SabreTools.Models.PortableExecutable.DebugData.RSDSProgramDatabase();
var obj = new SabreTools.Serialization.Models.PortableExecutable.DebugData.RSDSProgramDatabase();
obj.Signature = data.ReadUInt32LittleEndian(ref offset);
if (obj.Signature != 0x53445352)
@@ -231,7 +231,7 @@ namespace SabreTools.Serialization.Extensions
/// </summary>
/// <param name="entry">Resource data entry to parse into an accelerator table resource</param>
/// <returns>A filled accelerator table resource on success, null on error</returns>
public static AcceleratorTableEntry[]? AsAcceleratorTableResource(this SabreTools.Models.PortableExecutable.Resource.DataEntry? entry)
public static AcceleratorTableEntry[]? AsAcceleratorTableResource(this SabreTools.Serialization.Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have data that's invalid for this resource type, we can't do anything
if (entry?.Data == null || entry.Data.Length % 8 != 0)
@@ -260,7 +260,7 @@ namespace SabreTools.Serialization.Extensions
/// </summary>
/// <param name="entry">Resource data entry to parse into a side-by-side assembly manifest</param>
/// <returns>A filled side-by-side assembly manifest on success, null on error</returns>
public static AssemblyManifest? AsAssemblyManifest(this SabreTools.Models.PortableExecutable.Resource.DataEntry? entry)
public static AssemblyManifest? AsAssemblyManifest(this SabreTools.Serialization.Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -282,7 +282,7 @@ namespace SabreTools.Serialization.Extensions
/// </summary>
/// <param name="entry">Resource data entry to parse into a dialog box</param>
/// <returns>A filled dialog box on success, null on error</returns>
public static DialogBoxResource? AsDialogBox(this SabreTools.Models.PortableExecutable.Resource.DataEntry? entry)
public static DialogBoxResource? AsDialogBox(this SabreTools.Serialization.Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -759,7 +759,7 @@ namespace SabreTools.Serialization.Extensions
/// </summary>
/// <param name="entry">Resource data entry to parse into a font group</param>
/// <returns>A filled font group on success, null on error</returns>
public static FontGroupHeader? AsFontGroup(this SabreTools.Models.PortableExecutable.Resource.DataEntry? entry)
public static FontGroupHeader? AsFontGroup(this SabreTools.Serialization.Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -828,7 +828,7 @@ namespace SabreTools.Serialization.Extensions
/// </summary>
/// <param name="entry">Resource data entry to parse into a menu</param>
/// <returns>A filled menu on success, null on error</returns>
public static MenuResource? AsMenu(this SabreTools.Models.PortableExecutable.Resource.DataEntry? entry)
public static MenuResource? AsMenu(this SabreTools.Serialization.Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -924,7 +924,7 @@ namespace SabreTools.Serialization.Extensions
/// </summary>
/// <param name="entry">Resource data entry to parse into a message table resource</param>
/// <returns>A filled message table resource on success, null on error</returns>
public static MessageResourceData? AsMessageResourceData(this SabreTools.Models.PortableExecutable.Resource.DataEntry? entry)
public static MessageResourceData? AsMessageResourceData(this SabreTools.Serialization.Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -1012,10 +1012,10 @@ namespace SabreTools.Serialization.Extensions
// Align to the DWORD boundary if we're not at the end
data.AlignToBoundary(ref offset, 4);
var stringFileInfoChildren = new List<SabreTools.Models.PortableExecutable.Resource.Entries.StringTable>();
var stringFileInfoChildren = new List<SabreTools.Serialization.Models.PortableExecutable.Resource.Entries.StringTable>();
while ((offset - currentOffset) < stringFileInfo.Length)
{
var stringTable = new SabreTools.Models.PortableExecutable.Resource.Entries.StringTable();
var stringTable = new SabreTools.Serialization.Models.PortableExecutable.Resource.Entries.StringTable();
stringTable.Length = data.ReadUInt16LittleEndian(ref offset);
stringTable.ValueLength = data.ReadUInt16LittleEndian(ref offset);
@@ -1069,7 +1069,7 @@ namespace SabreTools.Serialization.Extensions
/// </summary>
/// <param name="entry">Resource data entry to parse into a string table resource</param>
/// <returns>A filled string table resource on success, null on error</returns>
public static Dictionary<int, string?>? AsStringTable(this SabreTools.Models.PortableExecutable.Resource.DataEntry? entry)
public static Dictionary<int, string?>? AsStringTable(this SabreTools.Serialization.Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -1161,7 +1161,7 @@ namespace SabreTools.Serialization.Extensions
/// </summary>
/// <param name="entry">Resource data entry to parse into a version info resource</param>
/// <returns>A filled version info resource on success, null on error</returns>
public static VersionInfo? AsVersionInfo(this SabreTools.Models.PortableExecutable.Resource.DataEntry? entry)
public static VersionInfo? AsVersionInfo(this SabreTools.Serialization.Models.PortableExecutable.Resource.DataEntry? entry)
{
// If we have an invalid entry, just skip
if (entry?.Data == null)
@@ -1358,10 +1358,10 @@ namespace SabreTools.Serialization.Extensions
/// <param name="data">Data to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>A filled ResourceHeader on success, null on error</returns>
public static SabreTools.Models.PortableExecutable.Resource.ResourceHeader ParseResourceHeader(this byte[] data, ref int offset)
public static SabreTools.Serialization.Models.PortableExecutable.Resource.ResourceHeader ParseResourceHeader(this byte[] data, ref int offset)
{
// Read in the table
var obj = new SabreTools.Models.PortableExecutable.Resource.ResourceHeader();
var obj = new SabreTools.Serialization.Models.PortableExecutable.Resource.ResourceHeader();
obj.DataSize = data.ReadUInt32LittleEndian(ref offset);
obj.HeaderSize = data.ReadUInt32LittleEndian(ref offset);

View File

@@ -0,0 +1,61 @@
namespace SabreTools.Serialization.Models.AdvancedInstaller
{
/// <summary>
/// Single entry in the file table
/// </summary>
public class FileEntry
{
/// <summary>
/// Unknown
/// </summary>
/// <remarks>
/// Observed values:
/// - 00 00 00 00 (INI)
/// - 01 00 00 00 (MSI, CAB)
/// - 05 00 00 00 (DLL)
/// </remarks>
public uint Unknown0 { get; set; }
/// <summary>
/// Unknown
/// </summary>
/// <remarks>
/// Observed values:
/// - 00 00 00 00 (MSI)
/// - 01 00 00 00 (CAB)
/// - 03 00 00 00 (INI)
/// - 0C 00 00 00 (DLL)
/// </remarks>
public uint? Unknown1 { get; set; }
/// <summary>
/// Unknown, always 0?
/// </summary>
/// <remarks>
/// Observed values:
/// - 00 00 00 00 (DLL, MSI, CAB, INI)
/// </remarks>
public uint? Unknown2 { get; set; }
/// <summary>
/// Size of the file
/// </summary>
public uint FileSize { get; set; }
/// <summary>
/// Offset of the file relative to the start
/// of the SFX stub
/// </summary>
public uint FileOffset { get; set; }
/// <summary>
/// Size of the file name in characters
/// </summary>
public uint NameSize { get; set; }
/// <summary>
/// Unicode-encoded file name
/// </summary>
public string? Name { get; set; }
}
}

View File

@@ -0,0 +1,109 @@
namespace SabreTools.Serialization.Models.AdvancedInstaller
{
/// <summary>
/// Structure similar to the end of central directory
/// header in PKZIP files
/// </summary>
public class Footer
{
/// <summary>
/// Unknown
/// </summary>
/// <remarks>
/// Observed values:
/// - 00 00 00 00
/// </remarks>
public uint Unknown0 { get; set; }
/// <summary>
/// Size of the original filename?
/// </summary>
/// <remarks>Doesn't exist in some cases?</remarks>
public uint? OriginalFilenameSize { get; set; }
/// <summary>
/// Unicode-encoded original filename?
/// </summary>
/// <remarks>Doesn't exist in some cases?</remarks>
public string? OriginalFilename { get; set; }
/// <summary>
/// Unknown, possibly a string count?
/// </summary>
/// <remarks>
/// Only seen when the preceeding two fields exist
///
/// Observed values:
/// - 01 00 00 00
/// </remarks>
public uint? Unknown1 { get; set; }
/// <summary>
/// Pointer to <see cref="Unknown0"/>?
/// </summary>
public uint FooterOffset { get; set; }
/// <summary>
/// Number of entries that preceed the footer
/// </summary>
public uint EntryCount { get; set; }
/// <summary>
/// Unknown
/// </summary>
/// <remarks>
/// Observed values:
/// - 64 00 00 00
/// </remarks>
public uint Unknown2 { get; set; }
/// <summary>
/// Unknown offset
/// </summary>
/// <remarks>
/// Points to <see cref="Unknown0"/> if no original filename.
/// Points to <see cref="EntryCount"/> if contains an original filename.
/// </remarks>
public uint UnknownOffset { get; set; }
/// <summary>
/// Offset of the start of the file table
/// </summary>
public uint TablePointer { get; set; }
/// <summary>
/// Offset to the start of the file data
/// </summary>
public uint FileDataStart { get; set; }
/// <summary>
/// Hex string that looks like a key or other identifier
/// </summary>
/// <remarks>32 bytes</remarks>
public string? HexString { get; set; }
/// <summary>
/// Unknown
/// </summary>
/// <remarks>
/// Offset pointer to <see cref="FileDataStart"/>
/// relative to the end of the signature if no filename
/// exists.
///
/// Observed values:
/// - 32 00 00 00 (No original filename)
/// - 13 02 00 00 (Original filename)
/// </remarks>
public uint Unknown3 { get; set; }
/// <summary>
/// "ADVINSTSFX"
/// </summary>
public string? Signature { get; set; }
/// <summary>
/// Unknown, always 0? Padding?
/// </summary>
public ushort? Unknown4 { get; set; }
}
}

View File

@@ -0,0 +1,29 @@
namespace SabreTools.Serialization.Models.AdvancedInstaller
{
/// <summary>
/// Represents the structure at the end of a Caphyon
/// Advanced Installer SFX file. These SFX files store
/// all files uncompressed sequentially in the overlay
/// of an executable.
///
/// The design is similar to the end of central directory
/// in a PKZIP file. The footer needs to be read before
/// the entry table as both the pointer to the start of
/// the table as well as the entry count are included there.
///
/// The layout of this is derived from the layout in the
/// physical file.
/// </summary>
public class SFX
{
/// <summary>
/// Set of file entries
/// </summary>
public FileEntry[]? Entries { get; set; }
/// <summary>
/// Footer representing the central directory
/// </summary>
public Footer? Footer { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
namespace SabreTools.Serialization.Models.COFF
{
public static class Constants
{
/// <summary>
/// Fixed size of <see cref="FileHeader"/>
/// </summary>
public const int FileHeaderSize = 20;
/// <summary>
/// Fixed size of <see cref="SectionHeader"/>
/// </summary>
public const int SectionHeaderSize = 40;
/// <summary>
/// Fixed size of <see cref="SymbolTableEntries.BaseEntry"/>
/// </summary>
public const int SymbolTableEntrySize = 18;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.COFF
{
/// <summary>
/// At the beginning of an object file, or immediately after the signature
/// of an image file, is a standard COFF file header in the following format.
/// Note that the Windows loader limits the number of sections to 96.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class FileHeader
{
/// <summary>
/// The number that identifies the type of target machine.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public MachineType Machine;
/// <summary>
/// The number of sections. This indicates the size of the section table,
/// which immediately follows the headers.
/// </summary>
public ushort NumberOfSections;
/// <summary>
/// The low 32 bits of the number of seconds since 00:00 January 1, 1970
/// (a C run-time time_t value), which indicates when the file was created.
/// </summary>
public uint TimeDateStamp;
/// <summary>
/// The file offset of the COFF symbol table, or zero if no COFF symbol table
/// is present. This value should be zero for an image because COFF debugging
/// information is deprecated.
/// </summary>
public uint PointerToSymbolTable;
/// <summary>
/// The number of entries in the symbol table. This data can be used to locate
/// the string table, which immediately follows the symbol table. This value
/// should be zero for an image because COFF debugging information is deprecated.
/// </summary>
public uint NumberOfSymbols;
/// <summary>
/// The size of the optional header, which is required for executable files but
/// not for object files. This value should be zero for an object file.
/// </summary>
public ushort SizeOfOptionalHeader;
/// <summary>
/// The flags that indicate the attributes of the file.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public Characteristics Characteristics;
}
}

View File

@@ -0,0 +1,42 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.COFF
{
/// <summary>
/// COFF line numbers are no longer produced and, in the future, will
/// not be consumed.
///
/// COFF line numbers indicate the relationship between code and line
/// numbers in source files. The Microsoft format for COFF line numbers
/// is similar to standard COFF, but it has been extended to allow a
/// single section to relate to line numbers in multiple source files.
///
/// COFF line numbers consist of an array of fixed-length records.
/// The location (file offset) and size of the array are specified in
/// the section header.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Explicit)]
public sealed class LineNumber
{
/// <summary>
/// Used when Linenumber is zero: index to symbol table entry for a function.
/// This format is used to indicate the function to which a group of
/// line-number records refers.
/// </summary>
[FieldOffset(0)] public uint SymbolTableIndex;
/// <summary>
/// Used when Linenumber is non-zero: the RVA of the executable code that
/// corresponds to the source line indicated. In an object file, this
/// contains the VA within the section.
/// </summary>
[FieldOffset(0)] public uint VirtualAddress;
/// <summary>
/// When nonzero, this field specifies a one-based line number. When zero,
/// the Type field is interpreted as a symbol table index for a function.
/// </summary>
[FieldOffset(4)] public ushort Linenumber;
}
}

View File

@@ -0,0 +1,88 @@
namespace SabreTools.Serialization.Models.COFF
{
/// <summary>
/// Every image file has an optional header that provides information to the loader.
/// This header is optional in the sense that some files (specifically, object files)
/// do not have it. For image files, this header is required. An object file can have
/// an optional header, but generally this header has no function in an object file
/// except to increase its size.
///
/// Note that the size of the optional header is not fixed. The SizeOfOptionalHeader
/// field in the COFF header must be used to validate that a probe into the file for
/// a particular data directory does not go beyond SizeOfOptionalHeader.
///
/// The NumberOfRvaAndSizes field of the optional header should also be used to ensure
/// that no probe for a particular data directory entry goes beyond the optional header.
/// In addition, it is important to validate the optional header magic number for format
/// compatibility.
///
/// The optional header magic number determines whether an image is a PE32 or
/// PE32+ executable.
///
/// PE32+ images allow for a 64-bit address space while limiting the image size to
/// 2 gigabytes. Other PE32+ modifications are addressed in their respective sections.
///
/// The first eight fields of the optional header are standard fields that are defined
/// for every implementation of COFF. These fields contain general information that is
/// useful for loading and running an executable file. They are unchanged for the
/// PE32+ format.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class OptionalHeader
{
/// <summary>
/// The unsigned integer that identifies the state of the image file. The most
/// common number is 0x10B, which identifies it as a normal executable file.
/// 0x107 identifies it as a ROM image, and 0x20B identifies it as a PE32+ executable.
/// </summary>
public OptionalHeaderMagicNumber Magic { get; set; }
/// <summary>
/// The linker major version number.
/// </summary>
public byte MajorLinkerVersion { get; set; }
/// <summary>
/// The linker minor version number.
/// </summary>
public byte MinorLinkerVersion { get; set; }
/// <summary>
/// The size of the code (text) section, or the sum of all code sections if there
/// are multiple sections.
/// </summary>
public uint SizeOfCode { get; set; }
/// <summary>
/// The size of the initialized data section, or the sum of all such sections if
/// there are multiple data sections.
/// </summary>
public uint SizeOfInitializedData { get; set; }
/// <summary>
/// The size of the uninitialized data section (BSS), or the sum of all such sections
/// if there are multiple BSS sections.
/// </summary>
public uint SizeOfUninitializedData { get; set; }
/// <summary>
/// The address of the entry point relative to the image base when the executable file
/// is loaded into memory. For program images, this is the starting address. For
/// device drivers, this is the address of the initialization function. An entry point
/// is optional for DLLs. When no entry point is present, this field must be zero.
/// </summary>
public uint AddressOfEntryPoint { get; set; }
/// <summary>
/// The address that is relative to the image base of the beginning-of-code section when
/// it is loaded into memory.
/// </summary>
public uint BaseOfCode { get; set; }
/// <summary>
/// The address that is relative to the image base of the beginning-of-data section when
/// it is loaded into memory.
/// </summary>
public uint BaseOfData { get; set; }
}
}

View File

@@ -0,0 +1,47 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.COFF
{
/// <summary>
/// Object files contain COFF relocations, which specify how the section data
/// should be modified when placed in the image file and subsequently loaded
/// into memory.
///
/// Image files do not contain COFF relocations, because all referenced symbols
/// have already been assigned addresses in a flat address space. An image
/// contains relocation information in the form of base relocations in the
/// .reloc section (unless the image has the IMAGE_FILE_RELOCS_STRIPPED attribute).
///
/// For each section in an object file, an array of fixed-length records holds
/// the section's COFF relocations. The position and length of the array are
/// specified in the section header.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class Relocation
{
/// <summary>
/// The address of the item to which relocation is applied. This is the
/// offset from the beginning of the section, plus the value of the
/// section's RVA/Offset field. See Section Table (Section Headers).
/// For example, if the first byte of the section has an address of 0x10,
/// the third byte has an address of 0x12.
/// </summary>
public uint VirtualAddress;
/// <summary>
/// A zero-based index into the symbol table. This symbol gives the address
/// that is to be used for the relocation. If the specified symbol has section
/// storage class, then the symbol's address is the address with the first
/// section of the same name.
/// </summary>
public uint SymbolTableIndex;
/// <summary>
/// A value that indicates the kind of relocation that should be performed.
/// Valid relocation types depend on machine type.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public RelocationType TypeIndicator;
}
}

View File

@@ -0,0 +1,111 @@
namespace SabreTools.Serialization.Models.COFF
{
/// <summary>
/// Each row of the section table is, in effect, a section header. This table
/// immediately follows the optional header, if any. This positioning is required
/// because the file header does not contain a direct pointer to the section table.
/// Instead, the location of the section table is determined by calculating the
/// location of the first byte after the headers. Make sure to use the size of
/// the optional header as specified in the file header.
///
/// The number of entries in the section table is given by the NumberOfSections
/// field in the file header. Entries in the section table are numbered starting
/// from one (1). The code and data memory section entries are in the order chosen
/// by the linker.
///
/// In an image file, the VAs for sections must be assigned by the linker so that
/// they are in ascending order and adjacent, and they must be a multiple of the
/// SectionAlignment value in the optional header.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class SectionHeader
{
/// <summary>
/// An 8-byte, null-padded UTF-8 encoded string. If the string is exactly 8
/// characters long, there is no terminating null. For longer names, this field
/// contains a slash (/) that is followed by an ASCII representation of a
/// decimal number that is an offset into the string table. Executable images
/// do not use a string table and do not support section names longer than 8
/// characters. Long names in object files are truncated if they are emitted
/// to an executable file.
/// </summary>
/// <remarks>8 bytes</remarks>
public byte[]? Name { get; set; }
/// <summary>
/// The total size of the section when loaded into memory. If this value is
/// greater than SizeOfRawData, the section is zero-padded. This field is valid
/// only for executable images and should be set to zero for object files.
/// </summary>
public uint VirtualSize { get; set; }
/// <summary>
/// For executable images, the address of the first byte of the section relative
/// to the image base when the section is loaded into memory. For object files,
/// this field is the address of the first byte before relocation is applied { get; set; }
/// for simplicity, compilers should set this to zero. Otherwise, it is an
/// arbitrary value that is subtracted from offsets during relocation.
/// </summary>
public uint VirtualAddress { get; set; }
/// <summary>
/// The size of the section (for object files) or the size of the initialized
/// data on disk (for image files). For executable images, this must be a multiple
/// of FileAlignment from the optional header. If this is less than VirtualSize,
/// the remainder of the section is zero-filled. Because the SizeOfRawData field
/// is rounded but the VirtualSize field is not, it is possible for SizeOfRawData
/// to be greater than VirtualSize as well. When a section contains only
/// uninitialized data, this field should be zero.
/// </summary>
public uint SizeOfRawData { get; set; }
/// <summary>
/// The file pointer to the first page of the section within the COFF file. For
/// executable images, this must be a multiple of FileAlignment from the optional
/// header. For object files, the value should be aligned on a 4-byte boundary
/// for best performance. When a section contains only uninitialized data, this
/// field should be zero.
/// </summary>
public uint PointerToRawData { get; set; }
/// <summary>
/// The file pointer to the beginning of relocation entries for the section. This
/// is set to zero for executable images or if there are no relocations.
/// </summary>
public uint PointerToRelocations { get; set; }
/// <summary>
/// The file pointer to the beginning of line-number entries for the section. This
/// is set to zero if there are no COFF line numbers. This value should be zero for
/// an image because COFF debugging information is deprecated.
/// </summary>
public uint PointerToLinenumbers { get; set; }
/// <summary>
/// The number of relocation entries for the section. This is set to zero for
/// executable images.
/// </summary>
public ushort NumberOfRelocations { get; set; }
/// <summary>
/// The number of line-number entries for the section. This value should be zero
/// for an image because COFF debugging information is deprecated.
/// </summary>
public ushort NumberOfLinenumbers { get; set; }
/// <summary>
/// The flags that describe the characteristics of the section.
/// </summary>
public SectionFlags Characteristics { get; set; }
/// <summary>
/// COFF Relocations (Object Only)
/// </summary>
public Relocation[]? COFFRelocations { get; set; }
/// <summary>
/// COFF Line Numbers (Deprecated)
/// </summary>
public LineNumber[]? COFFLineNumbers { get; set; }
}
}

View File

@@ -0,0 +1,25 @@
namespace SabreTools.Serialization.Models.COFF
{
/// <summary>
/// Immediately following the COFF symbol table is the COFF string table. The
/// position of this table is found by taking the symbol table address in the
/// COFF header and adding the number of symbols multiplied by the size of a symbol.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class StringTable
{
/// <summary>
/// At the beginning of the COFF string table are 4 bytes that contain the
/// total size (in bytes) of the rest of the string table. This size includes
/// the size field itself, so that the value in this location would be 4 if no
/// strings were present.
/// </summary>
public uint TotalSize { get; set; }
/// <summary>
/// Following the size are null-terminated strings that are pointed to by symbols
/// in the COFF symbol table.
/// </summary>
public string[]? Strings { get; set; }
}
}

View File

@@ -0,0 +1,32 @@
namespace SabreTools.Serialization.Models.COFF.SymbolTableEntries
{
/// <summary>
/// The symbol table in this section is inherited from the traditional
/// COFF format. It is distinct from Microsoft Visual C++ debug information.
/// A file can contain both a COFF symbol table and Visual C++ debug
/// information, and the two are kept separate. Some Microsoft tools use
/// the symbol table for limited but important purposes, such as
/// communicating COMDAT information to the linker. Section names and file
/// names, as well as code and data symbols, are listed in the symbol table.
///
/// The location of the symbol table is indicated in the COFF header.
///
/// The symbol table is an array of records, each 18 bytes long. Each record
/// is either a standard or auxiliary symbol-table record. A standard record
/// defines a symbol or name.
///
/// Auxiliary symbol table records always follow, and apply to, some standard
/// symbol table record. An auxiliary record can have any format that the tools
/// can recognize, but 18 bytes must be allocated for them so that symbol table
/// is maintained as an array of regular size. Currently, Microsoft tools
/// recognize auxiliary formats for the following kinds of records: function
/// definitions, function begin and end symbols (.bf and .ef), weak externals,
/// file names, and section definitions.
///
/// The traditional COFF design also includes auxiliary-record formats for arrays
/// and structures.Microsoft tools do not use these, but instead place that
/// symbolic information in Visual C++ debug format in the debug sections.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public abstract class BaseEntry { }
}

View File

@@ -0,0 +1,33 @@
namespace SabreTools.Serialization.Models.COFF.SymbolTableEntries
{
/// <summary>
/// Auxiliary Format 6: CLR Token Definition (Object Only)
///
/// This auxiliary symbol generally follows the IMAGE_SYM_CLASS_CLR_TOKEN. It is
/// used to associate a token with the COFF symbol table's namespace.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class CLRTokenDefinition : BaseEntry
{
/// <summary>
/// Must be IMAGE_AUX_SYMBOL_TYPE_TOKEN_DEF (1).
/// </summary>
public byte AuxType { get; set; }
/// <summary>
/// Reserved, must be zero.
/// </summary>
public byte Reserved1 { get; set; }
/// <summary>
/// The symbol index of the COFF symbol to which this CLR token definition refers.
/// </summary>
public uint SymbolTableIndex { get; set; }
/// <summary>
/// Reserved, must be zero.
/// </summary>
/// <remarks>12 bytes</remarks>
public byte[]? Reserved2 { get; set; }
}
}

View File

@@ -0,0 +1,53 @@
namespace SabreTools.Serialization.Models.COFF.SymbolTableEntries
{
/// <summary>
/// Auxiliary Format 2: .bf and .ef Symbols
///
/// For each function definition in the symbol table, three items describe
/// the beginning, ending, and number of lines. Each of these symbols has
/// storage class FUNCTION (101):
///
/// A symbol record named .bf (begin function). The Value field is unused.
///
/// A symbol record named .lf (lines in function). The Value field gives the
/// number of lines in the function.
///
/// A symbol record named .ef (end of function). The Value field has the same
/// number as the Total Size field in the function-definition symbol record.
///
// The .bf and .ef symbol records (but not .lf records) are followed by an
// auxiliary record with the following format:
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class Descriptor : BaseEntry
{
/// <summary>
/// Unused
/// </summary>
public uint Unused1 { get; set; }
/// <summary>
/// The actual ordinal line number (1, 2, 3, and so on) within the source file,
/// corresponding to the .bf or .ef record.
/// </summary>
public ushort Linenumber { get; set; }
/// <summary>
/// Unused
/// </summary>
/// <remarks>6 bytes</remarks>
public byte[]? Unused2 { get; set; }
/// <summary>
/// The symbol-table index of the next .bf symbol record. If the function is the
/// last in the symbol table, this field is set to zero. It is not used for
/// .ef records.
/// </summary>
public uint PointerToNextFunction { get; set; }
/// <summary>
/// Unused
/// </summary>
public ushort Unused3 { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
namespace SabreTools.Serialization.Models.COFF.SymbolTableEntries
{
/// <summary>
/// Auxiliary Format 4: Files
///
/// This format follows a symbol-table record with storage class FILE (103).
/// The symbol name itself should be .file, and the auxiliary record that
/// follows it gives the name of a source-code file.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class FileRecord : BaseEntry
{
/// <summary>
/// An ANSI string that gives the name of the source file. This is padded
/// with nulls if it is less than the maximum length.
/// </summary>
/// <remarks>18 bytes</remarks>
public byte[]? FileName { get; set; }
}
}

View File

@@ -0,0 +1,47 @@
namespace SabreTools.Serialization.Models.COFF.SymbolTableEntries
{
/// <summary>
/// Auxiliary Format 1: Function Definitions
///
/// A symbol table record marks the beginning of a function definition if it
/// has all of the following: a storage class of EXTERNAL (2), a Type value
/// that indicates it is a function (0x20), and a section number that is
/// greater than zero. Note that a symbol table record that has a section
/// number of UNDEFINED (0) does not define the function and does not have
/// an auxiliary record. Function-definition symbol records are followed by
/// an auxiliary record in the format described below:
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class FunctionDefinition : BaseEntry
{
/// <summary>
/// The symbol-table index of the corresponding .bf (begin function)
/// symbol record.
/// </summary>
public uint TagIndex { get; set; }
/// <summary>
/// The size of the executable code for the function itself. If the function
/// is in its own section, the SizeOfRawData in the section header is greater
/// or equal to this field, depending on alignment considerations.
/// </summary>
public uint TotalSize { get; set; }
/// <summary>
/// The file offset of the first COFF line-number entry for the function, or
/// zero if none exists.
/// </summary>
public uint PointerToLinenumber { get; set; }
/// <summary>
/// The symbol-table index of the record for the next function. If the function
/// is the last in the symbol table, this field is set to zero.
/// </summary>
public uint PointerToNextFunction { get; set; }
/// <summary>
/// Unused
/// </summary>
public ushort Unused { get; set; }
}
}

View File

@@ -0,0 +1,54 @@
namespace SabreTools.Serialization.Models.COFF.SymbolTableEntries
{
/// <summary>
/// Auxiliary Format 5: Section Definitions
///
/// This format follows a symbol-table record that defines a section. Such a
/// record has a symbol name that is the name of a section (such as .text or
/// .drectve) and has storage class STATIC (3). The auxiliary record provides
/// information about the section to which it refers. Thus, it duplicates some
/// of the information in the section header.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class SectionDefinition : BaseEntry
{
/// <summary>
/// The size of section data; the same as SizeOfRawData in the section header.
/// </summary>
public uint Length { get; set; }
/// <summary>
/// The number of relocation entries for the section.
/// </summary>
public ushort NumberOfRelocations { get; set; }
/// <summary>
/// The number of line-number entries for the section.
/// </summary>
public ushort NumberOfLinenumbers { get; set; }
/// <summary>
/// The checksum for communal data. It is applicable if the IMAGE_SCN_LNK_COMDAT
/// flag is set in the section header.
/// </summary>
public uint CheckSum { get; set; }
/// <summary>
/// One-based index into the section table for the associated section. This is
/// used when the COMDAT selection setting is 5.
/// </summary>
public ushort Number { get; set; }
/// <summary>
/// The COMDAT selection number. This is applicable if the section is a
/// COMDAT section.
/// </summary>
public byte Selection { get; set; }
/// <summary>
/// Unused
/// </summary>
/// <remarks>3 bytes</remarks>
public byte[]? Unused { get; set; }
}
}

View File

@@ -0,0 +1,58 @@
namespace SabreTools.Serialization.Models.COFF.SymbolTableEntries
{
/// <summary>
/// A standard record defines a symbol or name.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class StandardRecord : BaseEntry
{
#region Symbol Name
/// <summary>
/// An array of 8 bytes. This array is padded with nulls on the right if
/// the name is less than 8 bytes long.
/// </summary>
public byte[]? ShortName { get; set; } = new byte[8];
/// <summary>
/// A field that is set to all zeros if the name is longer than 8 bytes.
/// </summary>
public uint Zeroes { get; set; }
/// <summary>
/// An offset into the string table.
/// </summary>
public uint Offset { get; set; }
#endregion
/// <summary>
/// The value that is associated with the symbol. The interpretation of this
/// field depends on SectionNumber and StorageClass. A typical meaning is the
/// relocatable address.
/// </summary>
public uint Value { get; set; }
/// <summary>
/// The signed integer that identifies the section, using a one-based index
/// into the section table. Some values have special meaning.
/// </summary>
public SectionNumber SectionNumber { get; set; }
/// <summary>
/// A number that represents type. Microsoft tools set this field to 0x20
/// (function) or 0x0 (not a function).
/// </summary>
public SymbolType SymbolType { get; set; }
/// <summary>
/// An enumerated value that represents storage class.
/// </summary>
public StorageClass StorageClass { get; set; }
/// <summary>
/// The number of auxiliary symbol table entries that follow this record.
/// </summary>
public byte NumberOfAuxSymbols { get; set; }
}
}

View File

@@ -0,0 +1,45 @@
namespace SabreTools.Serialization.Models.COFF.SymbolTableEntries
{
/// <summary>
/// Auxiliary Format 3: Weak Externals
///
/// "Weak externals" are a mechanism for object files that allows flexibility at
/// link time. A module can contain an unresolved external symbol (sym1), but it
/// can also include an auxiliary record that indicates that if sym1 is not
/// present at link time, another external symbol (sym2) is used to resolve
/// references instead.
///
/// If a definition of sym1 is linked, then an external reference to the symbol
/// is resolved normally. If a definition of sym1 is not linked, then all references
/// to the weak external for sym1 refer to sym2 instead. The external symbol, sym2,
/// must always be linked; typically, it is defined in the module that contains
/// the weak reference to sym1.
///
/// Weak externals are represented by a symbol table record with EXTERNAL storage
/// class, UNDEF section number, and a value of zero. The weak-external symbol
/// record is followed by an auxiliary record with the following format:
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public class WeakExternal : BaseEntry
{
/// <summary>
/// The symbol-table index of sym2, the symbol to be linked if sym1 is not found.
/// </summary>
public uint TagIndex { get; set; }
/// <summary>
/// A value of IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY indicates that no library search
/// for sym1 should be performed.
/// A value of IMAGE_WEAK_EXTERN_SEARCH_LIBRARY indicates that a library search for
/// sym1 should be performed.
/// A value of IMAGE_WEAK_EXTERN_SEARCH_ALIAS indicates that sym1 is an alias for sym2.
/// </summary>
public uint Characteristics { get; set; }
/// <summary>
/// Unused
/// </summary>
/// <remarks>10 bytes</remarks>
public byte[]? Unused { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
/// <remarks>
/// Information sourced from https://stackoverflow.com/questions/18720045/what-are-the-list-of-all-possible-values-for-dvclal
/// </remarks>
namespace SabreTools.Serialization.Models.Delphi
{
public static class Constants
{
public static readonly byte[] DVCLALStandard = [0x23, 0x78, 0x5D, 0x23, 0xB6, 0xA5, 0xF3, 0x19, 0x43, 0xF3, 0x40, 0x02, 0x26, 0xD1, 0x11, 0xC7];
public static readonly byte[] DVCLALProfessional = [0xA2, 0x8C, 0xDF, 0x98, 0x7B, 0x3C, 0x3A, 0x79, 0x26, 0x71, 0x3F, 0x09, 0x0F, 0x2A, 0x25, 0x17];
public static readonly byte[] DVCLALEnterprise = [0x26, 0x3D, 0x4F, 0x38, 0xC2, 0x82, 0x37, 0xB8, 0xF3, 0x24, 0x42, 0x03, 0x17, 0x9B, 0x3A, 0x83];
}
}

View File

@@ -0,0 +1,14 @@
/// <remarks>
/// Information sourced from https://docwiki.embarcadero.com/Libraries/Alexandria/en/System.PackageInfoTable
/// </remarks>
namespace SabreTools.Serialization.Models.Delphi
{
public class PackageInfoTable
{
public int UnitCount { get; set; }
public PackageUnitEntry[]? UnitInfo { get; set; }
public PackageTypeInfo? TypeInfo { get; set; }
}
}

View File

@@ -0,0 +1,19 @@
/// <remarks>
/// Information sourced from https://docwiki.embarcadero.com/Libraries/Sydney/en/System.TPackageTypeInfo
/// </remarks>
namespace SabreTools.Serialization.Models.Delphi
{
public class PackageTypeInfo
{
public int TypeCount { get; set; }
/// <remarks>
/// System-dependent pointer type, assumed to be encoded for x86
/// </remarks>
public uint[]? TypeTable { get; set; }
public int UnitCount { get; set; }
public string[]? UnitNames { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
using System.Runtime.InteropServices;
/// <remarks>
/// Information sourced from https://docwiki.embarcadero.com/Libraries/Alexandria/en/System.PackageUnitEntry
/// </remarks>
namespace SabreTools.Serialization.Models.Delphi
{
[StructLayout(LayoutKind.Sequential)]
public class PackageUnitEntry
{
/// <remarks>
/// System-dependent pointer type, assumed to be encoded for x86
/// </remarks>
public uint Init;
/// <remarks>
/// System-dependent pointer type, assumed to be encoded for x86
/// </remarks>
public uint FInit;
}
}

View File

@@ -0,0 +1,21 @@
namespace SabreTools.Serialization.Models.LinearExecutable
{
public static class Constants
{
public static readonly byte[] DebugInformationSignatureBytes = [0x4e, 0x42, 0x30];
public const string DebugInformationSignatureString = "NB0";
public static readonly byte[] LESignatureBytes = [0x4c, 0x45];
public const string LESignatureString = "LE";
public const ushort LESignatureUInt16 = 0x454c;
public static readonly byte[] LXSignatureBytes = [0x4c, 0x58];
public const string LXSignatureString = "LX";
public const ushort LXSignatureUInt16 = 0x584c;
}
}

View File

@@ -0,0 +1,35 @@
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The debug information is defined by the debugger and is not controlled by
/// the linear EXE format or linker. The only data defined by the linear EXE
/// format relative to the debug information is it's offset in the EXE file and
/// length in bytes as defined in the linear EXE header.
///
/// To support multiple debuggers the first word of the debug information is a
/// type field which determines the format of the debug information.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
public sealed class DebugInformation
{
/// <summary>
/// The signature consists of a string of three (3) ASCII characters: "NB0"
/// </summary>
public string? Signature { get; set; }
/// <summary>
/// This defines the type of debugger data that exists in the remainder of the
/// debug information.
/// </summary>
public DebugFormatType FormatType { get; set; }
/// <summary>
/// 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.
/// </summary>
public byte[]? DebuggerData { get; set; }
}
}

View File

@@ -0,0 +1,56 @@
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// 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.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
public sealed class EntryTableBundle
{
/// <summary>
/// Number of entries.
/// </summary>
/// <remarks>
/// 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 <see cref="BundleType.UnusedEntry"/>, this is the number of unused
/// entries to skip.
/// For <see cref="BundleType.SixteenBitEntry"/>, this is the number of 16-bit
/// entries in this bundle. The flags and offset value are repeated this
/// number of times.
/// For <see cref="BundleType.TwoEightySixCallGateEntry"/>, 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 <see cref="BundleType.ThirtyTwoBitEntry"/>, this is the number
/// of 32-bit entries in this bundle. The flags and offset value are repeated
/// this number of times.
/// For <see cref="BundleType.ForwarderEntry"/>, this field is reserved for future use.
/// </remarks>
public byte Entries { get; set; }
/// <summary>
/// This defines the bundle type which determines the contents of the BUNDLE INFO.
/// </summary>
public BundleType BundleType { get; set; }
/// <summary>
/// Table entries in the bundle
/// </summary>
public EntryTableEntry[]? TableEntries { get; set; }
}
}

View File

@@ -0,0 +1,190 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Explicit)]
public sealed class EntryTableEntry
{
#region 16-bit Entry
/// <summary>
/// Object number.
/// </summary>
/// <remarks>
/// This is the object number for the entries in this bundle.
/// </remarks>
[FieldOffset(0)] public ushort SixteenBitObjectNumber;
/// <summary>
/// Entry flags.
/// </summary>
/// <remarks>
/// These are the flags for this entry point.
/// </remarks>
[FieldOffset(2)] public EntryFlags SixteenBitEntryFlags;
/// <summary>
/// Offset in object.
/// </summary>
/// <remarks>
/// This is the offset in the object for the entry point defined at this ordinal number.
/// </remarks>
[FieldOffset(3)] public ushort SixteenBitOffset;
#endregion
#region 286 Call Gate Entry
/// <summary>
/// Object number.
/// </summary>
/// <remarks>
/// This is the object number for the entries in this bundle.
/// </remarks>
[FieldOffset(0)] public ushort TwoEightySixObjectNumber;
/// <summary>
/// Entry flags.
/// </summary>
/// <remarks>
/// These are the flags for this entry point.
/// </remarks>
[FieldOffset(2)] public EntryFlags TwoEightySixEntryFlags;
/// <summary>
/// Offset in object.
/// </summary>
/// <remarks>
/// This is the offset in the object for the entry point defined at this ordinal number.
/// </remarks>
[FieldOffset(3)] public ushort TwoEightySixOffset;
/// <summary>
/// Callgate selector.
/// </summary>
/// <remarks>
/// The callgate selector is a reserved field used by the loader to store a call
/// gate selector value for references to ring 2 entry points. When a ring 3
/// reference to a ring 2 entry point is made, the callgate selector with a zero
/// offset is place in the relocation fixup address. The segment number and offset
/// in segment is placed in the LDT callgate.
/// </remarks>
[FieldOffset(5)] public ushort TwoEightySixCallgate;
#endregion
#region 32-bit Entry
/// <summary>
/// Object number.
/// </summary>
/// <remarks>
/// This is the object number for the entries in this bundle.
/// </remarks>
[FieldOffset(0)] public ushort ThirtyTwoBitObjectNumber;
/// <summary>
/// Entry flags.
/// </summary>
/// <remarks>
/// These are the flags for this entry point.
/// </remarks>
[FieldOffset(2)] public EntryFlags ThirtyTwoBitEntryFlags;
/// <summary>
/// Offset in object.
/// </summary>
/// <remarks>
/// This is the offset in the object for the entry point defined at this ordinal number.
/// </remarks>
[FieldOffset(3)] public uint ThirtyTwoBitOffset;
#endregion
#region Forwarder Entry
/// <summary>
/// 0
/// </summary>
/// <remarks>
/// This field is reserved for future use.
/// </remarks>
[FieldOffset(0)] public ushort ForwarderReserved;
/// <summary>
/// Forwarder flags.
/// </summary>
/// <remarks>
/// These are the flags for this entry point.
/// </remarks>
[FieldOffset(2)] public ForwarderFlags ForwarderFlags;
/// <summary>
/// Module Ordinal Number
/// </summary>
/// <remarks>
/// This is the index into the Import Module Name Table for this forwarder.
/// </remarks>
[FieldOffset(3)] public ushort ForwarderModuleOrdinalNumber;
/// <summary>
/// Procedure Name Offset
/// </summary>
/// <remarks>
/// If the FLAGS field indicates import by ordinal, then this field is the
/// ordinal number into the Entry Table of the target module, otherwise this
/// field is the offset into the Procedure Names Table of the target module.
///
/// A Forwarder entry (type = 4) is an entry point whose value is an imported
/// reference. When a load time fixup occurs whose target is a forwarder, the
/// loader obtains the address imported by the forwarder and uses that imported
/// address to resolve the fixup.
///
/// A forwarder may refer to an entry point in another module which is itself a
/// forwarder, so there can be a chain of forwarders. The loader will traverse
/// the chain until it finds a non-forwarded entry point which terminates the
/// chain, and use this to resolve the original fixup. Circular chains are
/// detected by the loader and result in a load time error. A maximum of 1024
/// forwarders is allowed in a chain; more than this results in a load time error.
///
/// Forwarders are useful for merging and recombining API calls into different
/// sets of libraries, while maintaining compatibility with applications. For
/// example, if one wanted to combine MONCALLS, MOUCALLS, and VIOCALLS into a
/// single libraries, one could provide entry points for the three libraries
/// that are forwarders pointing to the common implementation.
/// </remarks>
[FieldOffset(5)] public uint ProcedureNameOffset;
/// <summary>
/// Import Ordinal Number
/// </summary>
/// <remarks>
/// If the FLAGS field indicates import by ordinal, then this field is the
/// ordinal number into the Entry Table of the target module, otherwise this
/// field is the offset into the Procedure Names Table of the target module.
///
/// A Forwarder entry (type = 4) is an entry point whose value is an imported
/// reference. When a load time fixup occurs whose target is a forwarder, the
/// loader obtains the address imported by the forwarder and uses that imported
/// address to resolve the fixup.
///
/// A forwarder may refer to an entry point in another module which is itself a
/// forwarder, so there can be a chain of forwarders. The loader will traverse
/// the chain until it finds a non-forwarded entry point which terminates the
/// chain, and use this to resolve the original fixup. Circular chains are
/// detected by the loader and result in a load time error. A maximum of 1024
/// forwarders is allowed in a chain; more than this results in a load time error.
///
/// Forwarders are useful for merging and recombining API calls into different
/// sets of libraries, while maintaining compatibility with applications. For
/// example, if one wanted to combine MONCALLS, MOUCALLS, and VIOCALLS into a
/// single libraries, one could provide entry points for the three libraries
/// that are forwarders pointing to the common implementation.
/// </remarks>
[FieldOffset(5)] public uint ImportOrdinalNumber;
#endregion
}
}

View File

@@ -0,0 +1,680 @@
using System;
namespace SabreTools.Serialization.Models.LinearExecutable
{
[Flags]
public enum BundleType : byte
{
/// <summary>
/// Unused Entry.
/// </summary>
UnusedEntry = 0x00,
/// <summary>
/// 16-bit Entry.
/// </summary>
SixteenBitEntry = 0x01,
/// <summary>
/// 286 Call Gate Entry.
/// </summary>
TwoEightySixCallGateEntry = 0x02,
/// <summary>
/// 32-bit Entry.
/// </summary>
ThirtyTwoBitEntry = 0x03,
/// <summary>
/// Forwarder Entry.
/// </summary>
ForwarderEntry = 0x04,
/// <summary>
/// Parameter Typing Information Present.
/// </summary>
/// <remarks>
/// This bit signifies that additional information is contained in the
/// linear EXE module and will be used in the future for parameter type checking.
/// </remarks>
ParameterTypingInformationPresent = 0x80,
}
public enum ByteOrder : byte
{
/// <summary>
/// little-endian
/// </summary>
LE = 0x00,
/// <summary>
/// big-endian
/// </summary>
/// <remarks>non-zero</remarks>
BE = 0x01,
}
public enum CPUType : ushort
{
/// <summary>
/// Intel 80286 or upwardly compatible
/// </summary>
Intel80286 = 0x01,
/// <summary>
/// Intel 80386 or upwardly compatible
/// </summary>
Intel80386 = 0x02,
/// <summary>
/// Intel 80486 or upwardly compatible
/// </summary>
Intel80486 = 0x03,
/// <summary>
/// Intel 80586 or upwardly compatible
/// </summary>
Intel80586 = 0x04,
/// <summary>
/// Intel i860 (N10) or compatible
/// </summary>
Inteli860 = 0x20,
/// <summary>
/// Intel "N11" or compatible
/// </summary>
IntelN11 = 0x21,
/// <summary>
/// MIPS Mark I (R2000, R3000) or compatible
/// </summary>
MIPSMarkI = 0x40,
/// <summary>
/// MIPS Mark II ( R6000 ) or compatible
/// </summary>
MIPSMarkII = 0x41,
/// <summary>
/// MIPS Mark III ( R4000 ) or compatible
/// </summary>
MIPSMarkIII = 0x42,
}
public enum DebugFormatType : byte
{
/// <summary>
/// 32-bit CodeView debugger format.
/// </summary>
CodeView32Bit = 0x00,
/// <summary>
/// AIX debugger format.
/// </summary>
AIXDebugger = 0x01,
/// <summary>
/// 16-bit CodeView debugger format.
/// </summary>
CodeView16Bit = 0x02,
/// <summary>
/// 32-bit OS/2 PM debugger (IBM) format.
/// </summary>
OS2PM32Bit = 0x04,
}
public enum DirectiveNumber : ushort
{
/// <summary>
/// Resident Flag Mask.
/// </summary>
/// <remarks>
/// Directive numbers with this bit set indicate that the directive data
/// is in the resident area and will be kept resident in memory when the
/// module is loaded.
/// </remarks>
ResidentFlagMask = 0x8000,
/// <summary>
/// Verify Record Directive. (Verify record is a resident table.)
/// </summary>
VerifyRecordDirective = 0x8001,
/// <summary>
/// Language Information Directive. (This is a non-resident table.)
/// </summary>
LanguageInformationDirective = 0x0002,
/// <summary>
/// Co-Processor Required Support Table.
/// </summary>
CoProcessorRequiredSupportTable = 0x0003,
/// <summary>
/// Thread State Initialization Directive.
/// </summary>
ThreadStateInitializationDirective = 0x0004,
// Additional directives can be added as needed in the future, as long as
// they do not overlap previously defined directive numbers.
}
[Flags]
public enum EntryFlags : byte
{
/// <summary>
/// Exported entry flag.
/// </summary>
ExportedEntry = 0x01,
/// <summary>
/// Parameter word count mask.
/// </summary>
ParameterWordCountMask = 0xF8,
}
[Flags]
public enum FixupRecordSourceType : byte
{
/// <summary>
/// Source mask.
/// </summary>
SourceMask = 0x0F,
/// <summary>
/// Byte fixup (8-bits).
/// </summary>
ByteFixup = 0x00,
/// <summary>
/// (undefined).
/// </summary>
Undefined1 = 0x01,
/// <summary>
/// 16-bit Selector fixup (16-bits).
/// </summary>
SixteenBitSelectorFixup = 0x02,
/// <summary>
/// 16:16 Pointer fixup (32-bits).
/// </summary>
SixteenSixteenPointerFixup = 0x03,
/// <summary>
/// (undefined).
/// </summary>
Undefined4 = 0x04,
/// <summary>
/// 16-bit Offset fixup (16-bits).
/// </summary>
SixteenBitOffsetFixup = 0x05,
/// <summary>
/// 16:32 Pointer fixup (48-bits).
/// </summary>
SixteenThirtyTwoPointerFixup = 0x06,
/// <summary>
/// 32-bit Offset fixup (32-bits).
/// </summary>
ThirtyTwoBitOffsetFixup = 0x07,
/// <summary>
/// 32-bit Self-relative offset fixup (32-bits).
/// </summary>
ThirtyTwoBitSelfRelativeOffsetFixup = 0x08,
/// <summary>
/// Fixup to Alias Flag.
/// </summary>
/// <remarks>
/// When the 'Fixup to Alias' Flag is set, the source fixup refers to
/// the 16:16 alias for the object. This is only valid for source types
/// of 2, 3, and 6. For fixups such as this, the linker and loader will
/// be required to perform additional checks such as ensuring that the
/// target offset for this fixup is less than 64K.
/// </remarks>
FixupToAliasFlag = 0x10,
/// <summary>
/// Source List Flag.
/// </summary>
/// <remarks>
/// When the 'Source List' Flag is set, the SRCOFF field is compressed
/// to a byte and contains the number of source offsets, and a list of source
/// offsets follows the end of fixup record (after the optional additive value).
/// </remarks>
SourceListFlag = 0x20,
}
[Flags]
public enum FixupRecordTargetFlags : byte
{
/// <summary>
/// Fixup target type mask.
/// </summary>
FixupTargetTypeMask = 0x03,
/// <summary>
/// Internal reference.
/// </summary>
InternalReference = 0x00,
/// <summary>
/// Imported reference by ordinal.
/// </summary>
ImportedReferenceByOrdinal = 0x01,
/// <summary>
/// Imported reference by name.
/// </summary>
ImportedReferenceByName = 0x02,
/// <summary>
/// Internal reference via entry table.
/// </summary>
InternalReferenceViaEntryTable = 0x03,
/// <summary>
/// Additive Fixup Flag.
/// </summary>
/// <remarks>
/// When set, an additive value trails the fixup record (before the optional
/// source offset list).
/// </remarks>
AdditiveFixupFlag = 0x04,
/// <summary>
/// Reserved. Must be zero.
/// </summary>
Reserved = 0x08,
/// <summary>
/// 32-bit Target Offset Flag.
/// </summary>
/// <remarks>
/// When set, the target offset is 32-bits, otherwise it is 16-bits.
/// </remarks>
ThirtyTwoBitTargetOffsetFlag = 0x10,
/// <summary>
/// 32-bit Additive Fixup Flag.
/// </summary>
/// When set, the additive value is 32-bits, otherwise it is 16-bits.
/// </remarks>
ThirtyTwoBitAdditiveFixupFlag = 0x20,
/// <summary>
/// 16-bit Object Number/Module Ordinal Flag.
/// </summary>
/// <remarks>
/// When set, the object number or module ordinal number is 16-bits,
/// otherwise it is 8-bits.
/// </remarks>
SixteenBitObjectNumberModuleOrdinalFlag = 0x40,
/// <summary>
/// 8-bit Ordinal Flag.
/// </summary>
/// <remarks>
/// When set, the ordinal number is 8-bits, otherwise it is 16-bits.
/// </remarks>
EightBitOrdinalFlag = 0x80,
}
[Flags]
public enum ForwarderFlags : byte
{
/// <summary>
/// Import by ordinal.
/// </summary>
ImportByOrdinal = 0x01,
/// <summary>
/// Reserved for future use; should be zero.
/// </summary>
Reserved = 0xF7,
}
[Flags]
public enum ModuleFlags : uint
{
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved0 = 0x00000001,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved1 = 0x00000002,
/// <summary>
/// Per-Process Library Initialization.
/// </summary>
/// <remarks>
/// The setting of this bit requires the EIP Object # and EIP fields
/// to have valid values. If the EIP Object # and EIP fields are
/// valid and this bit is NOT set, then Global Library Initialization
/// is assumed. Setting this bit for an EXE file is invalid.
/// </remarks>
Initialization = 0x00000004,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved3 = 0x00000008,
/// <summary>
/// Internal fixups for the module have been applied.
/// </summary>
/// <remarks>
/// The setting of this bit in a Linear Executable Module indicates that
/// each object of the module has a preferred load address specified in
/// the Object Table Reloc Base Addr. If the module's objects can not be
/// loaded at these preferred addresses, then the relocation records that
/// have been retained in the file data will be applied.
/// </remarks>
InternalFixupsApplied = 0x00000010,
/// <summary>
/// External fixups for the module have been applied.
/// </summary>
ExternalFixupsApplied = 0x00000020,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved6 = 0x00000040,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved7 = 0x00000080,
/// <summary>
/// Incompatible with PM windowing.
/// </summary>
IncompatibleWithPMWindowing = 0x00000100,
/// <summary>
/// Incompatible with PM windowing.
/// </summary>
CompatibleWithPMWindowing = 0x00000200,
/// <summary>
/// Uses PM windowing API.
/// </summary>
UsesPMWindowing = 0x00000300,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved10 = 0x00000400,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved11 = 0x00000800,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved12 = 0x00001000,
/// <summary>
/// Module is not loadable.
/// </summary>
/// <remarks>
/// When the 'Module is not loadable' flag is set, it indicates that
/// either errors were detected at link time or that the module is
/// being incrementally linked and therefore can't be loaded.
/// </remarks>
ModuleNotLoadable = 0x00002000,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved14 = 0x00004000,
/// <summary>
/// Module type mask.
/// </summary>
ModuleTypeMask = 0x00038000,
/// <summary>
/// Program module.
/// </summary>
/// <remarks>
/// A module can not contain dynamic links to other modules that have
/// the 'program module' type.
/// </remarks>
ProgramModule = 0x00000000,
/// <summary>
/// Library module.
/// </summary>
LibraryModule = 0x00008000,
/// <summary>
/// Protected Memory Library module.
/// </summary>
ProtectedMemoryLibraryModule = 0x00018000,
/// <summary>
/// Physical Device Driver module.
/// </summary>
PhysicalDeviceDriverModule = 0x00020000,
/// <summary>
/// Virtual Device Driver module.
/// </summary>
VirtualDeviceDriverModule = 0x00028000,
/// <summary>
/// Per-process Library Termination.
/// </summary>
/// <remarks>
/// The setting of this bit requires the EIP Object # and EIP fields
/// to have valid values. If the EIP Object # and EIP fields are
/// valid and this bit is NOT set, then Global Library Termination
/// is assumed. Setting this bit for an EXE file is invalid.
/// </remarks>
PerProcessLibraryTermination = 0x40000000,
}
[Flags]
public enum ObjectFlags : ushort
{
/// <summary>
/// Readable Object.
/// </summary>
ReadableObject = 0x0001,
/// <summary>
/// Writable Object.
/// </summary>
WritableObject = 0x0002,
/// <summary>
/// Executable Object.
/// </summary>
ExecutableObject = 0x0004,
// The readable, writable and executable flags provide support for all possible
// protections. In systems where all of these protections are not supported,
// the loader will be responsible for making the appropriate protection match
// for the system.
/// <summary>
/// Resource Object.
/// </summary>
ResourceObject = 0x0008,
/// <summary>
/// Discardable Object.
/// </summary>
DiscardableObject = 0x0010,
/// <summary>
/// Object is Shared.
/// </summary>
Shared = 0x0020,
/// <summary>
/// Object has Preload Pages.
/// </summary>
HasPreloadPages = 0x0040,
/// <summary>
/// Object has Invalid Pages.
/// </summary>
HasInvalidPages = 0x0080,
/// <summary>
/// Object has Zero Filled Pages.
/// </summary>
HasZeroFilledPages = 0x0100,
/// <summary>
/// Object is Resident (valid for VDDs, PDDs only).
/// </summary>
Resident = 0x0200,
/// <summary>
/// Object is Resident & Contiguous (VDDs, PDDs only).
/// </summary>
ResidentAndContiguous = 0x0300,
/// <summary>
/// Object is Resident & 'long-lockable' (VDDs, PDDs only).
/// </summary>
ResidentAndLongLockable = 0x0400,
/// <summary>
/// Reserved for system use.
/// </summary>
Reserved = 0x0800,
/// <summary>
/// 16:16 Alias Required (80x86 Specific).
/// </summary>
AliasRequired = 0x1000,
/// <summary>
/// Big/Default Bit Setting (80x86 Specific).
/// </summary>
BitSetting = 0x2000,
// The 'big/default' bit, for data segments, controls the setting of the
// Big bit in the segment descriptor. (The Big bit, or B-bit, determines
// whether ESP or SP is used as the stack pointer.) For code segments,
// this bit controls the setting of the Default bit in the segment
// descriptor. (The Default bit, or D-bit, determines whether the default
// word size is 32-bits or 16-bits. It also affects the interpretation of
// the instruction stream.)
/// <summary>
/// Object is conforming for code (80x86 Specific).
/// </summary>
Conforming = 0x4000,
/// <summary>
/// Object I/O privilege level (80x86 Specific). Only used for 16:16 Alias Objects.
/// </summary>
PrivilegeLevel = 0x8000,
}
[Flags]
public enum ObjectPageFlags : ushort
{
/// <summary>
/// Legal Physical Page in the module (Offset from Preload Page Section).
/// </summary>
LegalPhysicalPage = 0x0000,
/// <summary>
/// Iterated Data Page (Offset from Iterated Data Pages Section).
/// </summary>
IteratedDataPage = 0x0001,
/// <summary>
/// Invalid Page (zero).
/// </summary>
InvalidPage = 0x0002,
/// <summary>
/// Zero Filled Page (zero).
/// </summary>
ZeroFilledPage = 0x0003,
/// <summary>
/// Range of Pages.
/// </summary>
RangeOfPages = 0x0004,
}
public enum OperatingSystem : ushort
{
/// <summary>
/// Unknown (any "new-format" OS)
/// </summary>
Unknown = 0x00,
/// <summary>
/// OS/2 (default)
/// </summary>
OS2 = 0x01,
/// <summary>
/// Windows
/// </summary>
Windows = 0x02,
/// <summary>
/// DOS 4.x
/// </summary>
DOS4x = 0x03,
/// <summary>
/// Windows 386
/// </summary>
Windows386 = 0x04,
}
public enum ResourceTableEntryType : uint
{
/// <summary>
/// "BTMP" - Bitmap
/// </summary>
BTMP = 0x504d5442,
/// <summary>
/// "EMSG" - Error message string
/// </summary>
EMSG = 0x47534d45,
/// <summary>
/// "FONT" - Fonts
/// </summary>
FONT = 0x544e4f46,
}
public enum WordOrder : byte
{
/// <summary>
/// little-endian
/// </summary>
LE = 0x00,
/// <summary>
/// big-endian
/// </summary>
/// <remarks>non-zero</remarks>
BE = 0x01,
}
}

View File

@@ -0,0 +1,97 @@
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The `LINEAR` executable-file header contains information that the loader requires for
/// segmented executable files. This information includes the linker version number, data
/// specified by linker, data specified by resource compiler, tables of segment data, tables
/// of resource data, and so on. The following illustrations shows the LE file header:
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
public sealed class Executable
{
/// <summary>
/// MS-DOS executable stub
/// </summary>
public MSDOS.Executable? Stub { get; set; }
/// <summary>
/// Information block
/// </summary>
public InformationBlock? InformationBlock { get; set; }
/// <summary>
/// Object table
/// </summary>
public ObjectTableEntry[]? ObjectTable { get; set; }
/// <summary>
/// Object page map
/// </summary>
public ObjectPageMapEntry[]? ObjectPageMap { get; set; }
// TODO: Object iterate data map table (Undefined)
/// <summary>
/// Resource table
/// </summary>
public ResourceTableEntry[]? ResourceTable { get; set; }
/// <summary>
/// Resident Name table
/// </summary>
public ResidentNamesTableEntry[]? ResidentNamesTable { get; set; }
/// <summary>
/// Entry table
/// </summary>
public EntryTableBundle[]? EntryTable { get; set; }
/// <summary>
/// Module format directives table (optional)
/// </summary>
public ModuleFormatDirectivesTableEntry[]? ModuleFormatDirectivesTable { get; set; }
/// <summary>
/// Verify record directive table (optional)
/// </summary>
public VerifyRecordDirectiveTableEntry[]? VerifyRecordDirectiveTable { get; set; }
/// <summary>
/// Fix-up page table
/// </summary>
public FixupPageTableEntry[]? FixupPageTable { get; set; }
/// <summary>
/// Fix-up record table
/// </summary>
public FixupRecordTableEntry[]? FixupRecordTable { get; set; }
/// <summary>
/// Import module name table
/// </summary>
public ImportModuleNameTableEntry[]? ImportModuleNameTable { get; set; }
/// <summary>
/// Import procedure name table
/// </summary>
public ImportModuleProcedureNameTableEntry[]? ImportModuleProcedureNameTable { get; set; }
/// <summary>
/// Per-Page checksum table
/// </summary>
public PerPageChecksumTableEntry[]? PerPageChecksumTable { get; set; }
/// <summary>
/// Non-Resident Name table
/// </summary>
public NonResidentNamesTableEntry[]? NonResidentNamesTable { get; set; }
// TODO: Non-resident directives data (Undefined)
/// <summary>
/// Debug information
/// </summary>
public DebugInformation? DebugInformation { get; set; }
}
}

View File

@@ -0,0 +1,36 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The Fixup Page Table provides a simple mapping of a logical page number
/// to an offset into the Fixup Record Table for that page.
///
/// This table is parallel to the Object Page Table, except that there is
/// one additional entry in this table to indicate the end of the Fixup
/// Record Table.
///
/// The fixup records are kept in order by logical page in the fixup record
/// table. This allows the end of each page's fixup records is defined by the
/// offset for the next logical page's fixup records. This last entry provides
/// support of this mechanism for the last page in the fixup page table.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class FixupPageTableEntry
{
/// <summary>
/// Offset for fixup record for this page. (1 to n)
/// Offset to the end of the fixup records. (n + 1)
/// </summary>
/// <remarks>
/// This field specifies the offset, from the beginning of the fixup record
/// table, to the first fixup record for this page. (1 to n)
///
/// This field specifies the offset following the last fixup record in the
/// fixup record table. This is the last entry in the fixup page table. (n + 1)
/// </remarks>
public uint Offset;
}
}

View File

@@ -0,0 +1,363 @@
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The Fixup Record Table contains entries for all fixups in the linear EXE module.
/// The fixup records for a logical page are grouped together and kept in sorted
/// order by logical page number. The fixups for each page are further sorted such
/// that all external fixups and internal selector/pointer fixups come before
/// internal non-selector/non-pointer fixups. This allows the loader to ignore
/// internal fixups if the loader is able to load all objects at the addresses
/// specified in the object table.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
public sealed class FixupRecordTableEntry
{
/// <summary>
/// Source type.
/// </summary>
/// <remarks>
/// The source type specifies the size and type of the fixup to be performed
/// on the fixup source.
/// </remarks>
public FixupRecordSourceType SourceType { get; set; }
/// <summary>
/// Target Flags.
/// </summary>
/// <remarks>
/// The target flags specify how the target information is interpreted.
/// </remarks>
public FixupRecordTargetFlags TargetFlags { get; set; }
#region Source List Flag
#region Set
/// <summary>
/// Source offset.
/// </summary>
/// <remarks>
/// This field contains either an offset or a count depending on the Source
/// List Flag. If the Source List Flag is set, a list of source offsets
/// follows the additive field and this field contains the count of the
/// entries in the source offset list. Otherwise, this is the single source
/// offset for the fixup. Source offsets are relative to the beginning of
/// the page where the fixup is to be made.
///
/// Note that for fixups that cross page boundaries, a separate fixup record
/// is specified for each page. An offset is still used for the 2nd page but
/// it now becomes a negative offset since the fixup originated on the
/// preceding page. (For example, if only the last one byte of a 32-bit
/// address is on the page to be fixed up, then the offset would have a value
/// of -3.)
/// </remarks>
public ushort SourceOffset { get; set; }
#endregion
#region Unset
/// <summary>
/// Source offset list count.
/// </summary>
/// <remarks>
/// This field contains either an offset or a count depending on the Source
/// List Flag. If the Source List Flag is set, a list of source offsets
/// follows the additive field and this field contains the count of the
/// entries in the source offset list. Otherwise, this is the single source
/// offset for the fixup. Source offsets are relative to the beginning of
/// the page where the fixup is to be made.
///
/// Note that for fixups that cross page boundaries, a separate fixup record
/// is specified for each page. An offset is still used for the 2nd page but
/// it now becomes a negative offset since the fixup originated on the
/// preceding page. (For example, if only the last one byte of a 32-bit
/// address is on the page to be fixed up, then the offset would have a value
/// of -3.)
/// </remarks>
public byte SourceOffsetListCount { get; set; }
#endregion
#endregion
#region OBJECT / TRGOFF
#region 16-bit Object Number/Module Ordinal Flag
#region Set
/// <summary>
/// Target object number.
/// </summary>
/// <remarks>
/// This field is an index into the current module's Object Table to specify
/// the target Object. It is a Byte value when the '16-bit Object Number/Module
/// Ordinal Flag' bit in the target flags field is clear and a Word value when
/// the bit is set.
/// </remarks>
public ushort TargetObjectNumberWORD { get; set; }
#endregion
#region Unset
/// <summary>
/// Target object number.
/// </summary>
/// <remarks>
/// This field is an index into the current module's Object Table to specify
/// the target Object. It is a Byte value when the '16-bit Object Number/Module
/// Ordinal Flag' bit in the target flags field is clear and a Word value when
/// the bit is set.
/// </remarks>
public byte TargetObjectNumberByte { get; set; }
#endregion
#endregion
#region 32-bit Target Offset Flag
#region Set
/// <summary>
/// Target offset.
/// </summary>
/// <remarks>
/// This field is an offset into the specified target Object. It is not
/// present when the Source Type specifies a 16-bit Selector fixup. It
/// is a Word value when the '32-bit Target Offset Flag' bit in the target
/// flags field is clear and a Dword value when the bit is set.
/// </remarks>
public uint TargetOffsetDWORD { get; set; }
#endregion
#region Unset
/// <summary>
/// Target offset.
/// </summary>
/// <remarks>
/// This field is an offset into the specified target Object. It is not
/// present when the Source Type specifies a 16-bit Selector fixup. It
/// is a Word value when the '32-bit Target Offset Flag' bit in the target
/// flags field is clear and a Dword value when the bit is set.
/// </remarks>
public ushort TargetOffsetWORD { get; set; }
#endregion
#endregion
#endregion
#region 16-bit Object Number/Module Ordinal Flag [Incompatible with OBJECT / TRGOFF]
#region Set
/// <summary>
/// Ordinal index into the Import Module Name Table.
/// </summary>
/// <remarks>
/// This value is an ordered index in to the Import Module Name Table for
/// the module containing the procedure entry point. It is a Byte value
/// when the '16-bit Object Number/Module Ordinal' Flag bit in the target
/// flags field is clear and a Word value when the bit is set. The loader
/// creates a table of pointers with each pointer in the table corresponds
/// to the modules named in the Import Module Name Table. This value is used
/// by the loader to index into this table created by the loader to locate
/// the referenced module.
/// </remarks>
public ushort OrdinalIndexImportModuleNameTableWORD { get; set; }
#endregion
#region Unset
/// <summary>
/// Ordinal index into the Import Module Name Table.
/// </summary>
/// <remarks>
/// This value is an ordered index in to the Import Module Name Table for
/// the module containing the procedure entry point. It is a Byte value
/// when the '16-bit Object Number/Module Ordinal' Flag bit in the target
/// flags field is clear and a Word value when the bit is set. The loader
/// creates a table of pointers with each pointer in the table corresponds
/// to the modules named in the Import Module Name Table. This value is used
/// by the loader to index into this table created by the loader to locate
/// the referenced module.
/// </remarks>
public byte OrdinalIndexImportModuleNameTableByte { get; set; }
#endregion
#endregion
#region MOD ORD# / PROCEDURE NAME OFFSET / ADDITIVE
#region 32-bit Target Offset Flag
#region Set
/// <summary>
/// Offset into the Import Procedure Name Table.
/// </summary>
/// <remarks>
/// This field is an offset into the Import Procedure Name Table. It is
/// a Word value when the '32-bit Target Offset Flag' bit in the target
/// flags field is clear and a Dword value when the bit is set.
/// </remarks>
public uint OffsetImportProcedureNameTableDWORD { get; set; }
#endregion
#region Unset
/// <summary>
/// Offset into the Import Procedure Name Table.
/// </summary>
/// <remarks>
/// This field is an offset into the Import Procedure Name Table. It is
/// a Word value when the '32-bit Target Offset Flag' bit in the target
/// flags field is clear and a Dword value when the bit is set.
/// </remarks>
public ushort OffsetImportProcedureNameTableWORD { get; set; }
#endregion
#endregion
#endregion
#region MOD ORD# / IMPORT ORD / ADDITIVE
#region 8-bit Ordinal Flag
#region Set
/// <summary>
/// Imported ordinal number.
/// </summary>
/// <remarks>
/// This is the imported procedure's ordinal number. It is a Byte value when the
/// '8-bit Ordinal' bit in the target flags field is set. Otherwise it is a Word value
/// when the '32-bit Target Offset Flag' bit in the target flags field is clear and a
/// Dword value when the bit is set.
/// </remarks>
public byte ImportedOrdinalNumberByte { get; set; }
#endregion
#region Unset
#region 32-bit Target Offset Flag
#region Set
/// <summary>
/// Imported ordinal number.
/// </summary>
/// <remarks>
/// This is the imported procedure's ordinal number. It is a Byte value when the
/// '8-bit Ordinal' bit in the target flags field is set. Otherwise it is a Word value
/// when the '32-bit Target Offset Flag' bit in the target flags field is clear and a
/// Dword value when the bit is set.
/// </remarks>
public uint ImportedOrdinalNumberDWORD { get; set; }
#endregion
#region Unset
/// <summary>
/// Imported ordinal number.
/// </summary>
/// <remarks>
/// This is the imported procedure's ordinal number. It is a Byte value when the
/// '8-bit Ordinal' bit in the target flags field is set. Otherwise it is a Word value
/// when the '32-bit Target Offset Flag' bit in the target flags field is clear and a
/// Dword value when the bit is set.
/// </remarks>
public uint ImportedOrdinalNumberWORD { get; set; }
#endregion
#endregion
#endregion
#endregion
#endregion
#region Additive Fixup Flag [Incompatible with OBJECT / TRGOFF]
#region Set
#region 32-bit Additive Fixup Flag
#region Set
/// <summary>
/// Additive fixup value.
/// </summary>
/// <remarks>
/// This field exists in the fixup record only when the 'Additive Fixup Flag'
/// bit in the target flags field is set. When the 'Additive Fixup Flag' is
/// clear the fixup record does not contain this field and is immediately
/// followed by the next fixup record (or by the source offset list for this
/// fixup record).
///
/// This value is added to the address derived from the target entry point.
/// This field is a Word value when the '32-bit Additive Flag' bit in the
/// target flags field is clear and a Dword value when the bit is set.
/// </remarks>
public uint AdditiveFixupValueDWORD { get; set; }
#endregion
#region Unset
/// <summary>
/// Additive fixup value.
/// </summary>
/// <remarks>
/// This field exists in the fixup record only when the 'Additive Fixup Flag'
/// bit in the target flags field is set. When the 'Additive Fixup Flag' is
/// clear the fixup record does not contain this field and is immediately
/// followed by the next fixup record (or by the source offset list for this
/// fixup record).
///
/// This value is added to the address derived from the target entry point.
/// This field is a Word value when the '32-bit Additive Flag' bit in the
/// target flags field is clear and a Dword value when the bit is set.
/// </remarks>
public ushort AdditiveFixupValueWORD { get; set; }
#endregion
#endregion
#endregion
#endregion
#region SCROFFn
/// <summary>
/// Source offset list.
/// </summary>
/// <remarks>
/// This list is present if the Source List Flag is set in the Target Flags field.
/// The number of entries in the source offset list is defined in the SRCOFF/CNT
/// field. The source offsets are relative to the beginning of the page where the
/// fixups are to be made.
/// </remarks>
public ushort[]? SourceOffsetList { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,38 @@
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The import module name table defines the module name strings imported through
/// dynamic link references. These strings are referenced through the imported
/// relocation fixups.
///
/// To determine the length of the import module name table subtract the import
/// module name table offset from the import procedure name table offset. These
/// values are located in the linear EXE header. The end of the import module
/// name table is not terminated by a special character, it is followed directly
/// by the import procedure name table.
///
/// The strings are CASE SENSITIVE and NOT NULL TERMINATED.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
public sealed class ImportModuleNameTableEntry
{
/// <summary>
/// String Length.
/// </summary>
/// <remarks>
/// This defines the length of the string in bytes. The length of each
/// ascii name string is limited to 127 characters.
/// </remarks>
public byte Length { get; set; } // TODO: Remove in lieu of AnsiBStr
/// <summary>
/// ASCII String.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public string? Name { get; set; }
}
}

View File

@@ -0,0 +1,46 @@
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The import procedure name table defines the procedure name strings imported
/// by this module through dynamic link references. These strings are referenced
/// through the imported relocation fixups.
///
/// To determine the length of the import procedure name table add the fixup
/// section size to the fixup page table offset, this computes the offset to
/// the end of the fixup section, then subtract the import procedure name table
/// offset. These values are located in the linear EXE header. The import
/// procedure name table is followed by the data pages section. Since the data
/// pages section is aligned on a 'page size' boundary, padded space may exist
/// between the last import name string and the first page in the data pages
/// section. If this padded space exists it will be zero filled.
///
/// The strings are CASE SENSITIVE and NOT NULL TERMINATED.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
public sealed class ImportModuleProcedureNameTableEntry
{
/// <summary>
/// String Length.
/// </summary>
/// <remarks>
/// This defines the length of the string in bytes. The length of each
/// ascii name string is limited to 127 characters.
///
/// The high bit in the LEN field (bit 7) is defined as an Overload bit.
/// This bit signifies that additional information is contained in the
/// linear EXE module and will be used in the future for parameter type
/// checking.
/// </remarks>
public byte Length { get; set; } // TODO: Remove in lieu of AnsiBStr
/// <summary>
/// ASCII String.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public string? Name { get; set; }
}
}

View File

@@ -0,0 +1,432 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The `information block` in the LE header contains the linker version number,
/// length of various tables that further describe the executable file, the
/// offsets from the beginning of the header to the beginning of these tables,
/// the heap and stack sizes, and so on. The following list summarizes the
/// contents of the header `information block` (the locations are relative to
/// the beginning of the block):
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public sealed class InformationBlock
{
/// <summary>
/// Specifies the signature word
/// 'LE' (4Ch 45H)
/// 'LX' (4Ch 58H)
/// </summary>
/// <remarks>
/// The signature word is used by the loader to identify the EXE
/// file as a valid 32-bit Linear Executable Module Format.
/// </remarks>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
public string? Signature;
/// <summary>
/// Byte Ordering.
/// </summary>
/// <remarks>
/// This byte specifies the byte ordering for the linear EXE format.
/// </remarks>
[MarshalAs(UnmanagedType.U1)]
public ByteOrder ByteOrder;
/// <summary>
/// Word Ordering.
/// </summary>
/// <remarks>
/// This byte specifies the Word ordering for the linear EXE format.
/// </remarks>
[MarshalAs(UnmanagedType.U1)]
public WordOrder WordOrder;
/// <summary>
/// Linear EXE Format Level.
/// </summary>
/// <remarks>
/// The Linear EXE Format Level is set to 0 for the initial version of the
/// 32-bit linear EXE format. Each incompatible change to the linear EXE
/// format must increment this value. This allows the system to recognized
/// future EXE file versions so that an appropriate error message may be
/// displayed if an attempt is made to load them.
/// </remarks>
public uint ExecutableFormatLevel;
/// <summary>
/// Module CPU Type.
/// </summary>
/// <remarks>
/// This field specifies the type of CPU required by this module to run.
/// </remarks>
[MarshalAs(UnmanagedType.U2)]
public CPUType CPUType;
/// <summary>
/// Module OS Type.
/// </summary>
/// <remarks>
/// This field specifies the type of Operating system required to run this module.
/// </remarks>
[MarshalAs(UnmanagedType.U2)]
public OperatingSystem ModuleOS;
/// <summary>
/// Module version
/// </summary>
/// <remarks>
/// This is useful for differentiating between revisions of dynamic linked modules.
/// This value is specified at link time by the user.
/// </remarks>
public uint ModuleVersion;
/// <summary>
/// Module type flags
/// </summary>
[MarshalAs(UnmanagedType.U4)]
public ModuleFlags ModuleTypeFlags;
/// <summary>
/// Number of pages in module.
/// </summary>
/// <remarks>
/// This field specifies the number of pages physically contained in this module.
/// In other words, pages containing either enumerated or iterated data, or
/// zero-fill pages that have relocations, not invalid or zero-fill pages implied
/// by the Virtual Size in the Object Table being larger than the number of pages
/// actually in the linear EXE file. These pages are contained in the 'preload
/// pages', 'demand load pages' and 'iterated data pages' sections of the linear
/// EXE module. This is used to determine the size of the page information tables
/// in the linear EXE module.
/// </remarks>
public uint ModuleNumberPages;
/// <summary>
/// The Object number to which the Entry Address is relative.
/// </summary>
/// <remarks>
/// This specifies the object to which the Entry Address is relative. This must be
/// a nonzero value for a program module to be correctly loaded. A zero value for
/// a library module indicates that no library entry routine exists. If this value
/// is zero, then both the Per-process Library Initialization bit and the Per-process
/// Library Termination bit must be clear in the module flags, or else the loader
/// will fail to load the module. Further, if the Per-process Library Termination bit
/// is set, then the object to which this field refers must be a 32-bit object (i.e.,
/// the Big/Default bit must be set in the object flags; see below).
/// </remarks>
public uint InitialObjectCS;
/// <summary>
/// Entry Address of module.
/// </summary>
/// <remarks>
/// The Entry Address is the starting address for program modules and the library
/// initialization and Library termination address for library modules.
/// </remarks>
public uint InitialEIP;
/// <summary>
/// The Object number to which the ESP is relative.
/// </summary>
/// <remarks>
/// This specifies the object to which the starting ESP is relative. This must be a
/// nonzero value for a program module to be correctly loaded. This field is ignored
/// for a library module.
/// </remarks>
public uint InitialObjectSS;
/// <summary>
/// Starting stack address of module.
/// </summary>
/// <remarks>
/// The ESP defines the starting stack pointer address for program modules. A zero
/// value in this field indicates that the stack pointer is to be initialized to the
/// highest address/offset in the object. This field is ignored for a library module.
/// </remarks>
public uint InitialESP;
/// <summary>
/// The size of one page for this system.
/// </summary>
/// <remarks>
/// This field specifies the page size used by the linear EXE format and the system.
/// For the initial version of this linear EXE format the page size is 4Kbytes.
/// (The 4K page size is specified by a value of 4096 in this field.)
/// </remarks>
public uint MemoryPageSize;
/// <summary>
/// The shift left bits for page offsets.
/// </summary>
/// <remarks>
/// This field gives the number of bit positions to shift left when interpreting
/// the Object Page Table entries' page offset field. This determines the alignment
/// of the page information in the file. For example, a value of 4 in this field
/// would align all pages in the Data Pages and Iterated Pages sections on 16 byte
/// (paragraph) boundaries. A Page Offset Shift of 9 would align all pages on a
/// 512 byte (disk sector) basis. The default value for this field is 12 (decimal),
/// which give a 4096 byte alignment. All other offsets are byte aligned.
/// </remarks>
public uint BytesOnLastPage;
/// <summary>
/// Total size of the fixup information in bytes.
/// </summary>
/// <remarks>
/// This includes the following 4 tables:
/// - Fixup Page Table
/// - Fixup Record Table
/// - Import Module name Table
/// - Import Procedure Name Table
/// </remarks>
public uint FixupSectionSize;
/// <summary>
/// Checksum for fixup information.
/// </summary>
/// <remarks>
/// This is a cryptographic checksum covering all of the fixup information. The
/// checksum for the fixup information is kept separate because the fixup data is
/// not always loaded into main memory with the 'loader section'. If the checksum
/// feature is not implemented, then the linker will set these fields to zero.
/// </remarks>
public uint FixupSectionChecksum;
/// <summary>
/// Size of memory resident tables.
/// </summary>
/// <remarks>
/// This is the total size in bytes of the tables required to be memory resident
/// for the module, while the module is in use. This total size includes all
/// tables from the Object Table down to and including the Per-Page Checksum Table.
/// </remarks>
public uint LoaderSectionSize;
/// <summary>
/// Checksum for loader section.
/// </summary>
/// <remarks>
/// This is a cryptographic checksum covering all of the loader section information.
/// If the checksum feature is not implemented, then the linker will set these fields
/// to zero.
/// </remarks>
public uint LoaderSectionChecksum;
/// <summary>
/// Object Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ObjectTableOffset;
/// <summary>
/// Object Table Count.
/// </summary>
/// <remarks>
/// This defines the number of entries in Object Table.
/// </remarks>
public uint ObjectTableCount;
/// <summary>
/// Object Page Table offset
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ObjectPageMapOffset;
/// <summary>
/// Object Iterated Pages offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ObjectIterateDataMapOffset;
/// <summary>
/// Resource Table offset
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ResourceTableOffset;
/// <summary>
/// Number of entries in Resource Table.
/// </summary>
public uint ResourceTableCount;
/// <summary>
/// Resident Name Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ResidentNamesTableOffset;
/// <summary>
/// Entry Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint EntryTableOffset;
/// <summary>
/// Module Format Directives Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ModuleDirectivesTableOffset;
/// <summary>
/// Number of Module Format Directives in the Table.
/// </summary>
/// <remarks>
/// This field specifies the number of entries in the
/// Module Format Directives Table.
/// </remarks>
public uint ModuleDirectivesCount;
/// <summary>
/// Fixup Page Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint FixupPageTableOffset;
/// <summary>
/// Fixup Record Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint FixupRecordTableOffset;
/// <summary>
/// Import Module Name Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ImportedModulesNameTableOffset;
/// <summary>
/// The number of entries in the Import Module Name Table.
/// </summary>
public uint ImportedModulesCount;
/// <summary>
/// Import Procedure Name Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint ImportProcedureNameTableOffset;
/// <summary>
/// Per-page Checksum Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint PerPageChecksumTableOffset;
/// <summary>
/// Data Pages Offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the EXE file.
/// </remarks>
public uint DataPagesOffset;
/// <summary>
/// Number of Preload pages for this module.
/// </summary>
/// <remarks>
/// Note that OS/2 2.0 does not respect the preload of pages as specified
/// in the executable file for performance reasons.
/// </remarks>
public uint PreloadPageCount;
/// <summary>
/// Non-Resident Name Table offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the EXE file.
/// </remarks>
public uint NonResidentNamesTableOffset;
/// <summary>
/// Number of bytes in the Non-resident name table.
/// </summary>
public uint NonResidentNamesTableLength;
/// <summary>
/// Non-Resident Name Table Checksum.
/// </summary>
/// <remarks>
/// This is a cryptographic checksum of the Non-Resident Name Table.
/// </remarks>
public uint NonResidentNamesTableChecksum;
/// <summary>
/// The Auto Data Segment Object number.
/// </summary>
/// <remarks>
/// This is the object number for the Auto Data Segment used by 16-bit modules.
/// This field is supported for 16-bit compatibility only and is not used by
/// 32-bit modules.
/// </remarks>
public uint AutomaticDataObject;
/// <summary>
/// Debug Information offset.
/// </summary>
/// <remarks>
/// This offset is relative to the beginning of the linear EXE header.
/// </remarks>
public uint DebugInformationOffset;
/// <summary>
/// Debug Information length.
/// </summary>
/// <remarks>
/// The length of the debug information in bytes.
/// </remarks>
public uint DebugInformationLength;
/// <summary>
/// Instance pages in preload section.
/// </summary>
/// <remarks>
/// The number of instance data pages found in the preload section.
/// </remarks>
public uint PreloadInstancePagesNumber;
/// <summary>
/// Instance pages in demand section.
/// </summary>
/// <remarks>
/// The number of instance data pages found in the demand section.
/// </remarks>
public uint DemandInstancePagesNumber;
/// <summary>
/// Heap size added to the Auto DS Object.
/// </summary>
/// <remarks>
/// The heap size is the number of bytes added to the Auto Data Segment
/// by the loader. This field is supported for 16-bit compatibility only and
/// is not used by 32-bit modules.
/// </remarks>
public uint ExtraHeapAllocation;
}
}

View File

@@ -0,0 +1,49 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The Module Format Directives Table is an optional table that allows additional
/// options to be specified. It also allows for the extension of the linear EXE
/// format by allowing additional tables of information to be added to the linear
/// EXE module without affecting the format of the linear EXE header. Likewise,
/// module format directives provide a place in the linear EXE module for
/// 'temporary tables' of information, such as incremental linking information
/// and statistic information gathered on the module. When there are no module
/// format directives for a linear EXE module, the fields in the linear EXE header
/// referencing the module format directives table are zero.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class ModuleFormatDirectivesTableEntry
{
/// <summary>
/// Directive number.
/// </summary>
/// <remarks>
/// The directive number specifies the type of directive defined. This can be
/// used to determine the format of the information in the directive data.
/// </remarks>
[MarshalAs(UnmanagedType.U2)]
public DirectiveNumber DirectiveNumber;
/// <summary>
/// Directive data length.
/// </summary>
/// <remarks>
/// This specifies the length in byte of the directive data for this directive number.
/// </remarks>
public ushort DirectiveDataLength;
/// <summary>
/// Directive data offset.
/// </summary>
/// <remarks>
/// This is the offset to the directive data for this directive number. It is relative
/// to beginning of linear EXE header for a resident table, and relative to the
/// beginning of the EXE file for non-resident tables.
/// </remarks>
public uint DirectiveDataOffset;
}
}

View File

@@ -0,0 +1,64 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The resident and non-resident name tables define the ASCII names and ordinal
/// numbers for exported entries in the module. In addition the first entry in
/// the resident name table contains the module name. These tables are used to
/// translate a procedure name string into an ordinal number by searching for a
/// matching name string. The ordinal number is used to locate the entry point
/// 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
/// 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
/// vs memory usage.
///
/// Import references by name require these tables to be searched to obtain the entry
/// point ordinal number.Import references by ordinal number provide the fastest
/// lookup since the search of these tables is not required.
///
/// The strings are CASE SENSITIVE and are NOT NULL TERMINATED.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class NonResidentNamesTableEntry
{
/// <summary>
/// String Length.
/// </summary>
/// <remarks>
/// This defines the length of the string in bytes. A zero length indicates there are
/// no more entries in table. The length of each ascii name string is limited to 127
/// characters.
///
/// The high bit in the LEN field (bit 7) is defined as an Overload bit. This bit
/// signifies that additional information is contained in the linear EXE module and
/// will be used in the future for parameter type checking.
/// </remarks>
public byte Length { get; set; }
/// <summary>
/// ASCII String.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public string? Name { get; set; }
/// <summary>
/// Ordinal number.
/// </summary>
/// <remarks>
/// The ordinal number in an ordered index into the entry table for this entry point.
/// </remarks>
public ushort OrdinalNumber { get; set; }
}
}

View File

@@ -0,0 +1,61 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The Object page table provides information about a logical page in an object.
/// A logical page may be an enumerated page, a pseudo page or an iterated page.
/// The structure of the object page table in conjunction with the structure of
/// the object table allows for efficient access of a page when a page fault occurs,
/// while still allowing the physical page data to be located in the preload page,
/// demand load page or iterated data page sections in the linear EXE module. The
/// logical page entries in the Object Page Table are numbered starting from one.
/// The Object Page Table is parallel to the Fixup Page Table as they are both
/// indexed by the logical page number.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class ObjectPageMapEntry
{
/// <summary>
/// Offset to the page data in the EXE file.
/// </summary>
/// <remarks>
/// This field, when bit shifted left by the PAGE OFFSET SHIFT from the module
/// header, specifies the offset from the beginning of the Preload Page section
/// of the physical page data in the EXE file that corresponds to this logical
/// page entry. The page data may reside in the Preload Pages, Demand Load Pages
/// or the Iterated Data Pages sections.
///
/// If the FLAGS field specifies that this is a zero-Filled page then the PAGE
/// DATA OFFSET field will contain a 0.
///
/// If the logical page is specified as an iterated data page, as indicated by
/// the FLAGS field, then this field specifies the offset into the Iterated Data
/// Pages section.
///
/// The logical page number (Object Page Table index), is used to index the Fixup
/// Page Table to find any fixups associated with the logical page.
/// </remarks>
public uint PageDataOffset;
/// <summary>
/// Number of bytes of data for this page.
/// </summary>
/// <remarks>
/// This field specifies the actual number of bytes that represent the page in the
/// file. If the PAGE SIZE field from the module header is greater than the value
/// of this field and the FLAGS field indicates a Legal Physical Page, the remaining
/// bytes are to be filled with zeros. If the FLAGS field indicates an Iterated Data
/// Page, the iterated data records will completely fill out the remainder.
/// </remarks>
public ushort DataSize;
/// <summary>
/// Attributes specifying characteristics of this logical page.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public ObjectPageFlags Flags;
}
}

View File

@@ -0,0 +1,83 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The object table contains information that describes each segment in
/// an executable file. This information includes segment length, segment
/// type, and segment-relocation data. The following list summarizes the
/// values found in in the segment table (the locations are relative to
/// the beginning of each entry):
/// </summary>
/// <remarks>
/// Entries in the Object Table are numbered starting from one.
/// </remarks>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class ObjectTableEntry
{
/// <summary>
/// Virtual memory size.
/// </summary>
/// <remarks>
/// This is the size of the object that will be allocated when the object
/// is loaded. The object's virtual size (rounded up to the page size value)
/// must be greater than or equal to the total size of the pages in the EXE
/// file for the object. This memory size must also be large enough to
/// contain all of the iterated data and uninitialized data in the EXE file.
/// </remarks>
public uint VirtualSegmentSize;
/// <summary>
/// Relocation Base Address.
/// </summary>
/// <remarks>
/// The relocation base address the object is currently relocated to. If the
/// internal relocation fixups for the module have been removed, this is the
/// address the object will be allocated at by the loader.
/// </remarks>
public uint RelocationBaseAddress;
/// <summary>
/// Flag bits for the object.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public ObjectFlags ObjectFlags;
/// <summary>
/// Object Page Table Index.
/// </summary>
/// <remarks>
/// This specifies the number of the first object page table entry for this object.
/// The object page table specifies where in the EXE file a page can be found for
/// a given object and specifies per-page attributes.
///
/// The object table entries are ordered by logical page in the object table. In
/// other words the object table entries are sorted based on the object page table
/// index value.
/// </remarks>
public uint PageTableIndex;
/// <summary>
/// # of object page table entries for this object.
/// </summary>
/// <remarks>
/// Any logical pages at the end of an object that do not have an entry in the object
/// page table associated with them are handled as zero filled or invalid pages by
/// the loader.
///
/// When the last logical pages of an object are not specified with an object page
/// table entry, they are treated as either zero filled pages or invalid pages based
/// on the last entry in the object page table for that object. If the last entry
/// was neither a zero filled or invalid page, then the additional pages are treated
/// as zero filled pages.
/// </remarks>
public uint PageTableEntries;
/// <summary>
/// Reserved for future use. Must be set to zero.
/// </summary>
public uint Reserved;
}
}

View File

@@ -0,0 +1,24 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The Per-Page Checksum table provides space for a cryptographic checksum for
/// each physical page in the EXE file.
///
/// The checksum table is arranged such that the first entry in the table corresponds
/// to the first logical page of code/data in the EXE file (usually a preload page)
/// and the last entry corresponds to the last logical page in the EXE file (usually
/// a iterated data page).
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class PerPageChecksumTableEntry
{
/// <summary>
/// Cryptographic checksum.
/// </summary>
public uint Checksum;
}
}

View File

@@ -0,0 +1,61 @@
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The resident and non-resident name tables define the ASCII names and ordinal
/// numbers for exported entries in the module. In addition the first entry in
/// the resident name table contains the module name. These tables are used to
/// translate a procedure name string into an ordinal number by searching for a
/// matching name string. The ordinal number is used to locate the entry point
/// 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
/// 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
/// vs memory usage.
///
/// Import references by name require these tables to be searched to obtain the entry
/// point ordinal number.Import references by ordinal number provide the fastest
/// lookup since the search of these tables is not required.
///
/// The strings are CASE SENSITIVE and are NOT NULL TERMINATED.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
public sealed class ResidentNamesTableEntry
{
/// <summary>
/// String Length.
/// </summary>
/// <remarks>
/// This defines the length of the string in bytes. A zero length indicates there are
/// no more entries in table. The length of each ascii name string is limited to 127
/// characters.
///
/// The high bit in the LEN field (bit 7) is defined as an Overload bit. This bit
/// signifies that additional information is contained in the linear EXE module and
/// will be used in the future for parameter type checking.
/// </remarks>
public byte Length { get; set; } // TODO: Remove in lieu of AnsiBStr
/// <summary>
/// ASCII String.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public string? Name { get; set; }
/// <summary>
/// Ordinal number.
/// </summary>
/// <remarks>
/// The ordinal number in an ordered index into the entry table for this entry point.
/// </remarks>
public ushort OrdinalNumber { get; set; }
}
}

View File

@@ -0,0 +1,47 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The resource table is an array of resource table entries. Each resource table
/// entry contains a type ID and name ID. These entries are used to locate resource
/// objects contained in the Object table. The number of entries in the resource
/// table is defined by the Resource Table Count located in the linear EXE header.
/// More than one resource may be contained within a single object. Resource table
/// entries are in a sorted order, (ascending, by Resource Name ID within the
/// Resource Type ID). This allows the DosGetResource API function to use a binary
/// search when looking up a resource in a 32-bit module instead of the linear search
/// being used in the current 16-bit module.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class ResourceTableEntry
{
/// <summary>
/// Resource type ID.
/// </summary>
[MarshalAs(UnmanagedType.U4)]
public ResourceTableEntryType TypeID;
/// <summary>
/// An ID used as a name for the resource when referred to.
/// </summary>
public ushort NameID;
/// <summary>
/// The number of bytes the resource consists of.
/// </summary>
public uint ResourceSize;
/// <summary>
/// The number of the object which contains the resource.
/// </summary>
public ushort ObjectNumber;
/// <summary>
/// The offset within the specified object where the resource begins.
/// </summary>
public uint Offset;
}
}

View File

@@ -0,0 +1,85 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.LinearExecutable
{
/// <summary>
/// The Verify Record Directive Table is an optional table. It maintains a record
/// of the pages in the EXE file that have been fixed up and written back to the
/// original linear EXE module, along with the module dependencies used to perform
/// these fixups. This table provides an efficient means for verifying the virtual
/// addresses required for the fixed up pages when the module is loaded.
/// </summary>
/// <see href="https://faydoc.tripod.com/formats/exe-LE.htm"/>
/// <see href="http://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class VerifyRecordDirectiveTableEntry
{
/// <summary>
/// Number of module dependencies.
/// </summary>
/// <remarks>
/// This field specifies how many entries there are in the verify record
/// directive table. This is equal to the number of modules referenced by
/// this module.
/// </remarks>
public ushort EntryCount;
/// <summary>
/// Ordinal index into the Import Module Name Table.
/// </summary>
/// <remarks>
/// This value is an ordered index in to the Import Module Name Table for
/// the referenced module.
/// </remarks>
public ushort OrdinalIndex;
/// <summary>
/// Module Version.
/// </summary>
/// <remarks>
/// This is the version of the referenced module that the fixups were
/// originally performed.This is used to insure the same version of the
/// referenced module is loaded that was fixed up in this module and
/// therefore the fixups are still correct. This requires the version
/// number in a module to be incremented anytime the entry point offsets
/// change.
/// </remarks>
public ushort Version;
/// <summary>
/// Module # of Object Entries.
/// </summary>
/// <remarks>
/// This field is used to identify the number of object verify entries
/// that follow for the referenced module.
/// </remarks>
public ushort ObjectEntriesCount;
/// <summary>
/// Object # in Module.
/// </summary>
/// <remarks>
/// This field specifies the object number in the referenced module that
/// is being verified.
/// </remarks>
public ushort ObjectNumberInModule;
/// <summary>
/// Object load base address.
/// </summary>
/// <remarks>
/// This is the address that the object was loaded at when the fixups were
/// performed.
/// </remarks>
public ushort ObjectLoadBaseAddress;
/// <summary>
/// Object virtual address size.
/// </summary>
/// <remarks>
/// This field specifies the total amount of virtual memory required for
/// this object.
/// </remarks>
public ushort ObjectVirtualAddressSize;
}
}

View File

@@ -0,0 +1,11 @@
namespace SabreTools.Serialization.Models.MSDOS
{
public static class Constants
{
public static readonly byte[] SignatureBytes = [0x4d, 0x5a];
public const string SignatureString = "MZ";
public const ushort SignatureUInt16 = 0x5a4d;
}
}

View File

@@ -0,0 +1,29 @@
namespace SabreTools.Serialization.Models.MSDOS
{
/// <summary>
/// The MS-DOS EXE format, also known as MZ after its signature (the initials of Microsoft engineer Mark Zbykowski),
/// was introduced with MS-DOS 2.0 (version 1.0 only sported the simple COM format). It is designed as a relocatable
/// executable running under real mode. As such, only DOS and Windows 9x can use this format natively, but there are
/// several free DOS emulators (e.g., DOSBox) that support it and that run under various operating systems (e.g.,
/// Linux, Amiga, Windows NT, etc.). Although they can exist on their own, MZ executables are embedded in all NE, LE,
/// and PE executables, usually as stubs so that when they are ran under DOS, they display a warning.
/// </summary>
/// <see href="https://wiki.osdev.org/MZ"/>
public sealed class Executable
{
/// <summary>
/// MS-DOS executable header
/// </summary>
public ExecutableHeader? Header { get; set; }
/// <summary>
/// After loading the executable into memory, the program loader goes through
/// every entry in relocation table. For each relocation entry, the loader
/// adds the start segment address into word value pointed to by the
/// segment:offset pair. So, for example, a relocation entry 0001:001A will
/// make the loader add start segment address to the value at offset
/// 1*0x10+0x1A=0x2A within the program data.
/// </summary>
public RelocationEntry[]? RelocationTable { get; set; }
}
}

View File

@@ -0,0 +1,133 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.MSDOS
{
/// <summary>
/// MZ executables only consists of 2 structures: the header and the relocation table.
/// The header, which is followed by the program image, looks like this.
/// </summary>
/// <see href="https://wiki.osdev.org/MZ"/>
/// <see href="http://www.pinvoke.net/default.aspx/Structures.IMAGE_DOS_HEADER"/>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public sealed class ExecutableHeader
{
#region Standard Fields
/// <summary>
/// 0x5A4D (ASCII for 'M' and 'Z')
/// </summary>
/// <remarks>15 bytes</remarks>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
public string? Magic;
/// <summary>
/// Number of bytes in the last page.
/// </summary>
public ushort LastPageBytes;
/// <summary>
/// Number of whole/partial pages.
/// </summary>
/// <remarks>A page (or block) is 512 bytes long.</remarks>
public ushort Pages;
/// <summary>
/// Number of entries in the relocation table.
/// </summary>
public ushort RelocationItems;
/// <summary>
/// The number of paragraphs taken up by the header. It can be any value, as the loader
/// just uses it to find where the actual executable data starts. It may be larger than
/// what the "standard" fields take up, and you may use it if you want to include your
/// own header metadata, or put the relocation table there, or use it for any other purpose. [08]
/// </summary>
/// <remarks>A paragraph is 16 bytes in size</remarks>
public ushort HeaderParagraphSize;
/// <summary>
/// The number of paragraphs required by the program, excluding the PSP and program image.
/// If no free block is big enough, the loading stops.
/// </summary>
/// <remarks>A paragraph is 16 bytes in size</remarks>
public ushort MinimumExtraParagraphs;
/// <summary>
/// The number of paragraphs requested by the program.
/// If no free block is big enough, the biggest one possible is allocated.
/// </summary>
/// <remarks>A paragraph is 16 bytes in size</remarks>
public ushort MaximumExtraParagraphs;
/// <summary>
/// Relocatable segment address for SS.
/// </summary>
public ushort InitialSSValue;
/// <summary>
/// Initial value for SP.
/// </summary>
public ushort InitialSPValue;
/// <summary>
/// When added to the sum of all other words in the file, the result should be zero.
/// </summary>
public ushort Checksum;
/// <summary>
/// Initial value for IP. [14]
/// </summary>
public ushort InitialIPValue;
/// <summary>
/// Relocatable segment address for CS.
/// </summary>
public ushort InitialCSValue;
/// <summary>
/// The (absolute) offset to the relocation table.
/// </summary>
public ushort RelocationTableAddr;
/// <summary>
/// Value used for overlay management.
/// If zero, this is the main executable.
/// </summary>
public ushort OverlayNumber;
#endregion
#region PE Extensions
/// <summary>
/// Reserved words
/// </summary>
/// <remarks>4 entries/remarks>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public ushort[]? Reserved1;
/// <summary>
/// Defined by name but no other information is given; typically zeroes
/// </summary>
public ushort OEMIdentifier;
/// <summary>
/// Defined by name but no other information is given; typically zeroes
/// </summary>
public ushort OEMInformation;
/// <summary>
/// Reserved words
/// </summary>
/// <remarks>10 entries/remarks>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public ushort[]? Reserved2;
/// <summary>
/// Starting address of the PE header
/// </summary>
public uint NewExeHeaderAddr;
#endregion
}
}

View File

@@ -0,0 +1,22 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.MSDOS
{
/// <summary>
/// Each pointer in the relocation table looks as such
/// </summary>
/// <see href="https://wiki.osdev.org/MZ"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class RelocationEntry
{
/// <summary>
/// Offset of the relocation within provided segment.
/// </summary>
public ushort Offset;
/// <summary>
/// Segment of the relocation, relative to the load segment address.
/// </summary>
public ushort Segment;
}
}

View File

@@ -0,0 +1,11 @@
namespace SabreTools.Serialization.Models.NewExecutable
{
public static class Constants
{
public static readonly byte[] SignatureBytes = [0x4e, 0x45];
public const string SignatureString = "NE";
public const ushort SignatureUInt16 = 0x454e;
}
}

View File

@@ -0,0 +1,84 @@
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// The entry table follows the imported-name table. This table contains
/// bundles of entry-point definitions. Bundling is done to save space in
/// the entry table. The entry table is accessed by an ordinal value.
/// Ordinal number one is defined to index the first entry in the entry
/// table. To find an entry point, the bundles are scanned searching for a
/// specific entry point using an ordinal number. The ordinal number is
/// adjusted as each bundle is checked. When the bundle that contains the
/// entry point is found, the ordinal number is multiplied by the size of
/// the bundle's entries to index the proper entry.
/// </summary>
/// <remarks>
/// The linker forms bundles in the most dense manner it can, under the
/// restriction that it cannot reorder entry points to improve bundling.
/// The reason for this restriction is that other .EXE files may refer to
/// entry points within this bundle by their ordinal number. The following
/// describes the format of the entry table bundles.
/// </remarks>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
public sealed class EntryTableBundle
{
/// <summary>
/// Number of entries in this bundle. All records in one bundle
/// are either moveable or refer to the same fixed segment. A zero
/// value in this field indicates the end of the entry table.
/// </summary>
public byte EntryCount { get; set; }
/// <summary>
/// Segment indicator for this bundle. This defines the type of
/// entry table entry data within the bundle. There are three
/// types of entries that are defined.
/// - 000h = Unused entries. There is no entry data in an unused
/// bundle. The next bundle follows this field. This is
/// used by the linker to skip ordinal numbers.
/// - 001h-0FEh = Segment number for fixed segment entries. A fixed
/// segment entry is 3 bytes long.
/// - 0FFH = Moveable segment entries. The entry data contains the
/// segment number for the entry points. A moveable segment
/// entry is 6 bytes long.
/// </summary>
public byte SegmentIndicator { get; set; }
#region Fixed Segment Entry
/// <summary>
/// Flag word.
/// </summary>
public FixedSegmentEntryFlag FixedFlagWord { get; set; }
/// <summary>
/// Offset within segment to entry point.
/// </summary>
public ushort FixedOffset { get; set; }
#endregion
#region Moveable Segment Entry
/// <summary>
/// Flag word.
/// </summary>
public MoveableSegmentEntryFlag MoveableFlagWord { get; set; }
/// <summary>
/// INT 3FH.
/// </summary>
public ushort MoveableReserved { get; set; }
/// <summary>
/// Segment number.
/// </summary>
public byte MoveableSegmentNumber { get; set; }
/// <summary>
/// Offset within segment to entry point.
/// </summary>
public ushort MoveableOffset { get; set; }
#endregion
}
}

View File

@@ -0,0 +1,325 @@
using System;
namespace SabreTools.Serialization.Models.NewExecutable
{
[Flags]
public enum FixedSegmentEntryFlag : byte
{
/// <summary>
/// Set if the entry is exported.
/// </summary>
Exported = 0x01,
/// <summary>
/// Set if the entry uses a global (shared) data segments.
/// </summary>
/// <remarks>
/// The first assembly-language instruction in the
/// entry point prologue must be "MOV AX,data
/// segment number". This may be set only for
/// SINGLEDATA library modules.
/// </remarks>
Global = 0x02,
}
[Flags]
public enum HeaderFlag : ushort
{
#region Program Flags
NOAUTODATA = 0x0000,
/// <summary>
/// Shared automatic data segment
/// </summary>
SINGLEDATA = 0x0001,
/// <summary>
/// Instanced automatic data segment
/// </summary>
MULTIPLEDATA = 0x0002,
/// <summary>
/// Global initialization
/// </summary>
GlobalInitialization = 0x0004,
/// <summary>
/// Protected mode only
/// </summary>
ProtectedModeOnly = 0x0008,
/// <summary>
/// 8086 instructions
/// </summary>
Instructions8086 = 0x0010,
/// <summary>
/// 80286 instructions
/// </summary>
Instructions80286 = 0x0020,
/// <summary>
/// 80386 instructions
/// </summary>
Instructions80386 = 0x0040,
/// <summary>
/// 80x87 instructions
/// </summary>
Instructions80x87 = 0x0080,
#endregion
#region Application Flags
/// <summary>
/// Full screen (not aware of Windows/P.M. API)
/// </summary>
FullScreen = 0x0100,
/// <summary>
/// Compatible with Windows/P.M. API
/// </summary>
WindowsPMCompatible = 0x0200,
/// <summary>
/// Uses Windows/P.M. API
/// </summary>
WindowsPM = 0x0400,
/// <summary>
/// OS/2 family application
/// </summary>
OS2FamilyApplication = 0x0800,
/// <summary>
/// Unknown (Reserved?)
/// </summary>
UnknownReserved = 0x1000,
/// <summary>
/// Errors detected at link time, module will not load
/// </summary>
ErrorsDetectedAtLinkTime = 0x2000,
/// <summary>
/// Unknown (non-conforming program)
/// </summary>
UnknownNonConforming = 0x4000,
/// <summary>
/// Library module.
/// The SS:SP information is invalid, CS:IP points
/// to an initialization procedure that is called
/// with AX equal to the module handle. This
/// initialization procedure must perform a far
/// return to the caller, with AX not equal to
/// zero to indicate success, or AX equal to zero
/// to indicate failure to initialize. DS is set
/// to the library's data segment if the
/// SINGLEDATA flag is set. Otherwise, DS is set
/// to the caller's data segment.
/// </summary>
/// <remarks>
/// A program or DLL can only contain dynamic
/// links to executable files that have this
/// library module flag set. One program cannot
/// dynamic-link to another program.
/// </remarks>
LibraryModule = 0x8000,
#endregion
}
[Flags]
public enum MoveableSegmentEntryFlag : byte
{
/// <summary>
/// Set if the entry is exported.
/// </summary>
Exported = 0x01,
/// <summary>
/// Set if the entry uses a global (shared) data segments.
/// </summary>
Global = 0x02,
}
public enum OperatingSystem : byte
{
Unknown = 0x00,
OS2 = 0x01,
WINDOWS = 0x02,
EU_MSDOS4 = 0x03,
WINDOWS_386 = 0x04,
BOSS = 0x05,
}
[Flags]
public enum OS2Flag : byte
{
/// <summary>
/// Long filename support
/// </summary>
LongFilenameSupport = 0x01,
/// <summary>
/// 2.x protected mode
/// </summary>
ProtectedMode = 0x02,
/// <summary>
/// 2.x proportional fonts
/// </summary>
ProportionalFonts = 0x04,
/// <summary>
/// Executable has gangload area
/// </summary>
HasGangload = 0x08,
/// <summary>
/// Unknown
/// </summary>
Unknown = 0xF0,
}
public enum OSFixupType : ushort
{
/// <summary>
/// FIARQQ, FJARQQ
/// </summary>
FIARQQ = 0x0001,
/// <summary>
/// FISRQQ, FJSRQQ
/// </summary>
FISRQQ = 0x0002,
/// <summary>
/// FICRQQ, FJCRQQ
/// </summary>
FICRQQ = 0x0003,
FIERQQ = 0x0004,
FIDRQQ = 0x0005,
FIWRQQ = 0x0006,
}
[Flags]
public enum RelocationRecordFlag : byte
{
TARGET_MASK = 0x03,
INTERNALREF = 0x00,
IMPORTORDINAL = 0x01,
IMPORTNAME = 0x02,
OSFIXUP = 0x03,
ADDITIVE = 0x04,
}
public enum RelocationRecordSourceType : byte
{
SOURCE_MASK = 0x0F,
LOBYTE = 0x00,
SEGMENT = 0x02,
/// <summary>
/// 32-bit pointer
/// </summary>
FAR_ADDR = 0x03,
/// <summary>
/// 16-bit offset
/// </summary>
OFFSET = 0x05,
}
[Flags]
public enum ResourceTypeResourceFlag : ushort
{
/// <summary>
/// Resource is not fixed.
/// </summary>
MOVEABLE = 0x0010,
/// <summary>
/// Resource can be shared.
/// </summary>
PURE = 0x0020,
/// <summary>
/// Resource is preloaded.
/// </summary>
PRELOAD = 0x0040,
}
public enum SegmentEntryType
{
/// <summary>
/// 000h - There is no entry data in an unused bundle. The next bundle
/// follows this field. This is used by the linker to skip ordinal numbers.
/// </summary>
Unused,
/// <summary>
/// 001h-0FEh - Segment number for fixed segment entries. A fixed segment
/// entry is 3 bytes long.
/// </summary>
FixedSegment,
/// <summary>
/// 0FFH - Moveable segment entries. The entry data contains the segment
/// number for the entry points. A moveable segment entry is 6 bytes long.
/// </summary>
MoveableSegment,
}
[Flags]
public enum SegmentTableEntryFlag : ushort
{
/// <summary>
/// Segment-type field.
/// </summary>
TYPE_MASK = 0x0007,
/// <summary>
/// Code-segment type.
/// </summary>
CODE = 0x0000,
/// <summary>
/// Data-segment type.
/// </summary>
DATA = 0x0001,
/// <summary>
/// Segment is not fixed.
/// </summary>
MOVEABLE = 0x0010,
/// <summary>
/// Segment will be preloaded; read-only if this is a data segment.
/// </summary>
PRELOAD = 0x0040,
/// <summary>
/// Set if segment has relocation records.
/// </summary>
RELOCINFO = 0x0100,
/// <summary>
/// Discard priority.
/// </summary>
DISCARD = 0xF000,
}
}

View File

@@ -0,0 +1,67 @@
using System.Collections.Generic;
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// The segmented EXE header contains general information about the EXE
/// file and contains information on the location and size of the other
/// sections. The Windows loader copies this section, along with other
/// data, into the module table in the system data. The module table is
/// internal data used by the loader to manage the loaded executable
/// modules in the system and to support dynamic linking.
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
/// <see href="https://wiki.osdev.org/NE"/>
public sealed class Executable
{
/// <summary>
/// MS-DOS executable stub
/// </summary>
public MSDOS.Executable? Stub { get; set; }
/// <summary>
/// New Executable header
/// </summary>
public ExecutableHeader? Header { get; set; }
/// <summary>
/// Segment table
/// </summary>
public SegmentTableEntry[]? SegmentTable { get; set; }
/// <summary>
/// Resource table
/// </summary>
public ResourceTable? ResourceTable { get; set; }
/// <summary>
/// Resident-Name table
/// </summary>
public ResidentNameTableEntry[]? ResidentNameTable { get; set; }
/// <summary>
/// Module-Reference table
/// </summary>
public ModuleReferenceTableEntry[]? ModuleReferenceTable { get; set; }
/// <summary>
/// Imported-Name table
/// </summary>
public Dictionary<ushort, ImportedNameTableEntry>? ImportedNameTable { get; set; }
/// <summary>
/// Entry table
/// </summary>
public EntryTableBundle[]? EntryTable { get; set; }
/// <summary>
/// Nonresident-Name table
/// </summary>
public NonResidentNameTableEntry[]? NonResidentNameTable { get; set; }
/// <summary>
/// Segment relocation data
/// </summary>
public PerSegmentData[]? SegmentRelocationData { get; set; }
}
}

View File

@@ -0,0 +1,209 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// The NE header is a relatively large structure with multiple characteristics.
/// Because of the age of the format some items are unclear in meaning.
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
/// <see href="https://github.com/libyal/libexe/blob/main/documentation/Executable%20(EXE)%20file%20format.asciidoc#24-ne-extended-header"/>
/// <see href="https://wiki.osdev.org/NE"/>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public sealed class ExecutableHeader
{
/// <summary>
/// "NE"
/// </summary>
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 2)]
public string? Magic;
/// <summary>
/// Version number of the linker.
/// </summary>
/// <remarks>Also known as the major linker version</remarks>
public byte LinkerVersion;
/// <summary>
/// Revision number of the linker.
/// </summary>
/// <remarks>Also known as the minor linker version</remarks>
public byte LinkerRevision;
/// <summary>
/// Entry Table file offset, relative to the beginning of the segmented EXE header.
/// </summary>
public ushort EntryTableOffset;
/// <summary>
/// Length of entry table in bytes
/// </summary>
public ushort EntryTableSize;
/// <summary>
/// 32-bit CRC of entire contents of file.
/// </summary>
/// <remarks>These words are taken as 00 during the calculation.</remarks>
public uint CrcChecksum;
/// <summary>
/// Flag word
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public HeaderFlag FlagWord;
/// <summary>
/// Segment number of automatic data segment.
/// This value is set to zero if SINGLEDATA and
/// MULTIPLEDATA flag bits are clear, NOAUTODATA is
/// indicated in the flags word.
/// </summary>
/// <remarks>
/// A Segment number is an index into the module's segment
/// table. The first entry in the segment table is segment
/// number 1.
/// </remarks>
public ushort AutomaticDataSegmentNumber;
/// <summary>
/// Initial size, in bytes, of dynamic heap added to the
/// data segment. This value is zero if no initial local
/// heap is allocated.
/// </summary>
public ushort InitialHeapAlloc;
/// <summary>
/// Initial size, in bytes, of stack added to the data
/// segment. This value is zero to indicate no initial
/// stack allocation, or when SS is not equal to DS.
/// </summary>
public ushort InitialStackAlloc;
/// <summary>
/// Segment number:offset of CS:IP.
/// </summary>
/// <remarks>CS:IP entry point, CS is index into segment table</remarks>
public uint InitialCSIPSetting;
/// <summary>
/// Segment number:offset of SS:SP.
/// </summary>
/// <remarks>
/// SS:SP initial stack pointer, SS is index into segment table
///
/// If SS equals the automatic data segment and SP equals
/// zero, the stack pointer is set to the top of the
/// automatic data segment just below the additional heap
/// area.
/// </remarks>
public uint InitialSSSPSetting;
/// <summary>
/// Number of entries in the Segment Table.
/// </summary>
public ushort FileSegmentCount;
/// <summary>
/// Number of entries in the Module Reference Table.
/// </summary>
public ushort ModuleReferenceTableSize;
/// <summary>
/// Size of non-resident names table in bytes
/// </summary>
public ushort NonResidentNameTableSize;
/// <summary>
/// Segment Table file offset, relative to the beginning
/// of the segmented EXE header.
/// </summary>
public ushort SegmentTableOffset;
/// <summary>
/// Resource Table file offset, relative to the beginning
/// of the segmented EXE header.
/// </summary>
public ushort ResourceTableOffset;
/// <summary>
/// Resident Name Table file offset, relative to the
/// beginning of the segmented EXE header.
/// </summary>
public ushort ResidentNameTableOffset;
/// <summary>
/// Module Reference Table file offset, relative to the
/// beginning of the segmented EXE header.
/// </summary>
public ushort ModuleReferenceTableOffset;
/// <summary>
/// Imported Names Table file offset, relative to the
/// beginning of the segmented EXE header.
/// </summary>
public ushort ImportedNamesTableOffset;
/// <summary>
/// Non-Resident Name Table offset, relative to the
/// beginning of the file.
/// </summary>
public uint NonResidentNamesTableOffset;
/// <summary>
/// Number of movable entries in the Entry Table.
/// </summary>
public ushort MovableEntriesCount;
/// <summary>
/// Logical sector alignment shift count, log(base 2) of
/// the segment sector size (default 9).
/// </summary>
public ushort SegmentAlignmentShiftCount;
/// <summary>
/// Number of resource entries.
/// </summary>
public ushort ResourceEntriesCount;
/// <summary>
/// Executable type, used by loader.
/// </summary>
[MarshalAs(UnmanagedType.U1)]
public OperatingSystem TargetOperatingSystem;
#region OS/2 Specific
/// <summary>
/// Other OS/2 flags
/// </summary>
[MarshalAs(UnmanagedType.U1)]
public OS2Flag AdditionalFlags;
/// <summary>
/// Offset to return thunks or start of gangload area
/// </summary>
public ushort ReturnThunkOffset;
/// <summary>
/// Offset to segment reference thunks or size of gangload area
/// </summary>
public ushort SegmentReferenceThunkOffset;
/// <summary>
/// Minimum code swap area size
/// </summary>
public ushort MinCodeSwapAreaSize;
/// <summary>
/// Windows SDK revison number
/// </summary>
public byte WindowsSDKRevision;
/// <summary>
/// Windows SDK version number
/// </summary>
public byte WindowsSDKVersion;
#endregion
}
}

View File

@@ -0,0 +1,19 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class ImportNameRelocationRecord
{
/// <summary>
/// Index into module reference table for the imported module.
/// </summary>
public ushort Index;
/// <summary>
/// Offset within Imported Names Table to procedure name string.
/// </summary>
public ushort Offset;
}
}

View File

@@ -0,0 +1,19 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class ImportOrdinalRelocationRecord
{
/// <summary>
/// Index into module reference table for the imported module.
/// </summary>
public ushort Index;
/// <summary>
/// Procedure ordinal number.
/// </summary>
public ushort Ordinal;
}
}

View File

@@ -0,0 +1,25 @@
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// The imported-name table follows the module-reference table. This table
/// contains the names of modules and procedures that are imported by the
/// executable file. Each entry is composed of a 1-byte field that
/// contains the length of the string, followed by any number of
/// characters. The strings are not null-terminated and are case
/// sensitive.
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
public sealed class ImportedNameTableEntry
{
/// <summary>
/// Length of the name string that follows. A zero value indicates
/// the end of the name table.
/// </summary>
public byte Length { get; set; } // TODO: Remove in lieu of AnsiBStr
/// <summary>
/// ASCII text of the name string.
/// </summary>
public byte[]? NameString { get; set; }
}
}

View File

@@ -0,0 +1,26 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class InternalRefRelocationRecord
{
/// <summary>
/// Segment number for a fixed segment, or 0FFh for a
/// movable segment.
/// </summary>
public byte SegmentNumber;
/// <summary>
/// Always 0
/// </summary>
public byte Reserved;
/// <summary>
/// Offset into segment if fixed segment, or ordinal
/// number index into Entry Table if movable segment.
/// </summary>
public ushort Offset;
}
}

View File

@@ -0,0 +1,19 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// The module-reference table follows the resident-name table. Each entry
/// contains an offset for the module-name string within the imported-
/// names table; each entry is 2 bytes long.
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class ModuleReferenceTableEntry
{
/// <summary>
/// Offset within Imported Names Table to referenced module name string.
/// </summary>
public ushort Offset;
}
}

View File

@@ -0,0 +1,31 @@
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// The nonresident-name table follows the entry table, and contains a
/// module description and nonresident exported procedure name strings.
/// The first string in this table is a module description. These name
/// strings are case-sensitive and are not null-terminated. The name
/// strings follow the same format as those defined in the resident name
/// table.
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
public sealed class NonResidentNameTableEntry
{
/// <summary>
/// Length of the name string that follows. A zero value indicates
/// the end of the name table.
/// </summary>
public byte Length { get; set; } // TODO: Remove in lieu of AnsiBStr
/// <summary>
/// ASCII text of the name string.
/// </summary>
public byte[]? NameString { get; set; }
/// <summary>
/// Ordinal number (index into entry table). This value is ignored
/// for the module name.
/// </summary>
public ushort OrdinalNumber { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class OSFixupRelocationRecord
{
/// <summary>
/// Operating system fixup type.
/// Floating-point fixups.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public OSFixupType FixupType;
/// <summary>
/// 0
/// </summary>
public ushort Reserved;
}
}

View File

@@ -0,0 +1,30 @@
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// The location and size of the per-segment data is defined in the
/// segment table entry for the segment. If the segment has relocation
/// fixups, as defined in the segment table entry flags, they directly
/// follow the segment data in the file. The relocation fixup information
/// is defined as follows:
/// </summary>
/// <remarks>
/// To find the relocation data for a segment, seek to:
/// <see cref="SegmentTableEntry.Offset"/>
/// * (1 << <see cref="ExecutableHeader.SegmentAlignmentShiftCount"/>)
/// + <see cref="SegmentTableEntry.Length"/>
/// </remarks>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
/// <see href="https://wiki.osdev.org/NE"/>
public sealed class PerSegmentData
{
/// <summary>
/// Number of relocation records that follow.
/// </summary>
public ushort RelocationRecordCount { get; set; }
/// <summary>
/// A table of relocation records follows.
/// </summary>
public RelocationRecord[]? RelocationRecords { get; set; }
}
}

View File

@@ -0,0 +1,57 @@
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// A table of relocation records follows. The following is the format
/// of each relocation record.
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
public sealed class RelocationRecord
{
/// <summary>
/// Source type.
/// </summary>
public RelocationRecordSourceType SourceType { get; set; }
/// <summary>
/// Flags byte.
///
/// The target value has four types that are defined in the flag
/// byte field.
/// </summary>
public RelocationRecordFlag Flags { get; set; }
/// <summary>
/// Offset within this segment of the source chain.
/// If the ADDITIVE flag is set, then target value is added to
/// the source contents, instead of replacing the source and
/// following the chain. The source chain is an 0FFFFh
/// terminated linked list within this segment of all
/// references to the target.
/// </summary>
public ushort Offset { get; set; }
/// <summary>
/// INTERNALREF
/// </summary>
/// <remarks>Must be null if <see cref="Flags"/> is not set to <see cref="RelocationRecordFlag.INTERNALREF"/></remarks>
public InternalRefRelocationRecord? InternalRefRelocationRecord { get; set; }
/// <summary>
/// IMPORTNAME
/// </summary>
/// <remarks>Must be null if <see cref="Flags"/> is not set to <see cref="RelocationRecordFlag.IMPORTNAME"/></remarks>
public ImportNameRelocationRecord? ImportNameRelocationRecord { get; set; }
/// <summary>
/// IMPORTORDINAL
/// </summary>
/// <remarks>Must be null if <see cref="Flags"/> is not set to <see cref="RelocationRecordFlag.IMPORTORDINAL"/></remarks>
public ImportOrdinalRelocationRecord? ImportOrdinalRelocationRecord { get; set; }
/// <summary>
/// IMPORTORDINAL
/// </summary>
/// <remarks>Must be null if <see cref="Flags"/> is not set to <see cref="RelocationRecordFlag.OSFIXUP"/></remarks>
public OSFixupRelocationRecord? OSFixupRelocationRecord { get; set; }
}
}

View File

@@ -0,0 +1,30 @@
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// The resident-name table follows the resource table, and contains this
/// module's name string and resident exported procedure name strings. The
/// first string in this table is this module's name. These name strings
/// are case-sensitive and are not null-terminated. The following
/// describes the format of the name strings:
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
public sealed class ResidentNameTableEntry
{
/// <summary>
/// Length of the name string that follows. A zero value indicates
/// the end of the name table.
/// </summary>
public byte Length { get; set; } // TODO: Remove in lieu of AnsiBStr
/// <summary>
/// ASCII text of the name string.
/// </summary>
public byte[]? NameString { get; set; }
/// <summary>
/// Ordinal number (index into entry table). This value is ignored
/// for the module name.
/// </summary>
public ushort OrdinalNumber { get; set; }
}
}

View File

@@ -0,0 +1,34 @@
using System.Collections.Generic;
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// The resource table follows the segment table and contains entries for
/// each resource in the executable file. The resource table consists of
/// an alignment shift count, followed by a table of resource records. The
/// resource records define the type ID for a set of resources. Each
/// resource record contains a table of resource entries of the defined
/// type. The resource entry defines the resource ID or name ID for the
/// resource. It also defines the location and size of the resource. The
/// following describes the contents of each of these structures:
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
public sealed class ResourceTable
{
/// <summary>
/// Alignment shift count for resource data.
/// </summary>
public ushort AlignmentShiftCount { get; set; }
/// <summary>
/// A table of resource type information blocks follows.
/// </summary>
public ResourceTypeInformationEntry[]? ResourceTypes { get; set; }
/// <summary>
/// Resource type and name strings are stored at the end of the
/// resource table.
/// </summary>
public Dictionary<ushort, ResourceTypeAndNameString?>? TypeAndNameStrings { get; set; }
}
}

View File

@@ -0,0 +1,23 @@
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// Resource type and name strings are stored at the end of the
/// resource table. Note that these strings are NOT null terminated and
/// are case sensitive.
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
public sealed class ResourceTypeAndNameString
{
/// <summary>
/// Length of the type or name string that follows. A zero value
/// indicates the end of the resource type and name string, also
/// the end of the resource table.
/// </summary>
public byte Length { get; set; } // TODO: Remove in lieu of AnsiBStr
/// <summary>
/// ASCII text of the type or name string.
/// </summary>
public byte[]? Text { get; set; }
}
}

View File

@@ -0,0 +1,34 @@
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// A table of resource type information blocks follows. The following
/// is the format of each type information block:
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
public sealed class ResourceTypeInformationEntry
{
/// <summary>
/// Type ID. This is an integer type if the high-order bit is
/// set (8000h); otherwise, it is an offset to the type string,
/// the offset is relative to the beginning of the resource
/// table. A zero type ID marks the end of the resource type
/// information blocks.
/// </summary>
public ushort TypeID { get; set; }
/// <summary>
/// Number of resources for this type.
/// </summary>
public ushort ResourceCount { get; set; }
/// <summary>
/// Reserved.
/// </summary>
public uint Reserved { get; set; }
/// <summary>
/// A table of resources for this type follows.
/// </summary>
public ResourceTypeResourceEntry[]? Resources { get; set; }
}
}

View File

@@ -0,0 +1,48 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// A table of resources for this type follows. The following is
/// the format of each resource (8 bytes each):
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
/// <see href="https://wiki.osdev.org/NE"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class ResourceTypeResourceEntry
{
/// <summary>
/// File offset to the contents of the resource data,
/// relative to beginning of file. The offset is in terms
/// of the alignment shift count value specified at
/// beginning of the resource table.
/// </summary>
/// <remarks>Byte offset is: Offset * (1 << <see cref="ResourceTable.AlignmentShiftCount"/>)</remarks>
public ushort Offset;
/// <summary>
/// Length of the resource in the file (in bytes).
/// </summary>
/// <remarks>Byte length is: Length * (1 << <see cref="ResourceTable.AlignmentShiftCount"/>)</remarks>
public ushort Length;
/// <summary>
/// Flag word.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public ResourceTypeResourceFlag FlagWord;
/// <summary>
/// Resource ID. This is an integer type if the high-order
/// bit is set (8000h), otherwise it is the offset to the
/// resource string, the offset is relative to the
/// beginning of the resource table.
/// </summary>
public ushort ResourceID;
/// <summary>
/// Reserved.
/// </summary>
public uint Reserved;
}
}

View File

@@ -0,0 +1,60 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.NewExecutable
{
/// <summary>
/// The segment table contains an entry for each segment in the executable
/// file. The number of segment table entries are defined in the segmented
/// EXE header. The first entry in the segment table is segment number 1.
/// The following is the structure of a segment table entry.
/// </summary>
/// <see href="https://web.archive.org/web/20240422070115/http://bytepointer.com/resources/win16_ne_exe_format_win3.0.htm"/>
/// <see href="https://wiki.osdev.org/NE"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class SegmentTableEntry
{
/// <summary>
/// Logical-sector offset (n byte) to the contents of the segment
/// data, relative to the beginning of the file. Zero means no
/// file data.
/// </summary>
/// <remarks>Byte offset is: Offset * (1 << <see cref="ExecutableHeader.SegmentAlignmentShiftCount"/>)</remarks>
public ushort Offset { get; set; }
/// <summary>
/// Length of the segment in the file, in bytes. Zero means 64K.
/// </summary>
public ushort Length { get; set; }
/// <summary>
/// Flag word.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
public SegmentTableEntryFlag FlagWord;
/// <summary>
/// Minimum allocation size of the segment, in bytes. Total size
/// of the segment. Zero means 64K.
/// </summary>
public ushort MinimumAllocationSize { get; set; }
/// <summary>
/// Segment data
/// </summary>
/// <remarks>
/// Data is not sequential to the entry header. It lives at
/// the <see cref="Offset"/> and has a size of <see cref="Length"/>
/// </remarks>
public byte[]? Data { get; set; }
/// <summary>
/// Per-segment data
/// </summary>
/// <remarks>
/// This only exists if <see cref="FlagWord"/> has a flag value
/// of <see cref="SegmentTableEntryFlag.RELOCINFO"/>. It immediately
/// follows <see cref="Data"/>.
/// </remarks>
public PerSegmentData? PerSegmentData { get; set; }
}
}

View File

@@ -0,0 +1,63 @@
namespace SabreTools.Serialization.Models.PortableExecutable.AttributeCertificate
{
/// <summary>
/// Attribute certificates can be associated with an image by adding an attribute
/// certificate table. The attribute certificate table is composed of a set of
/// contiguous, quadword-aligned attribute certificate entries. Zero padding is
/// inserted between the original end of the file and the beginning of the attribute
/// certificate table to achieve this alignment.
///
/// The virtual address value from the Certificate Table entry in the Optional
/// Header Data Directory is a file offset to the first attribute certificate
/// entry. Subsequent entries are accessed by advancing that entry's dwLength
/// bytes, rounded up to an 8-byte multiple, from the start of the current
/// attribute certificate entry. This continues until the sum of the rounded dwLength
/// values equals the Size value from the Certificates Table entry in the Optional
/// Header Data Directory. If the sum of the rounded dwLength values does not equal
/// the Size value, then either the attribute certificate table or the Size field
/// is corrupted.
///
/// The first certificate starts at offset 0x5000 from the start of the file on disk.
/// To advance through all the attribute certificate entries:
///
/// 1. Add the first attribute certificate's dwLength value to the starting offset.
/// 2. Round the value from step 1 up to the nearest 8-byte multiple to find the offset
/// of the second attribute certificate entry.
/// 3. Add the offset value from step 2 to the second attribute certificate entry's
/// dwLength value and round up to the nearest 8-byte multiple to determine the offset
/// of the third attribute certificate entry.
/// 4. Repeat step 3 for each successive certificate until the calculated offset equals
/// 0x6000 (0x5000 start + 0x1000 total size), which indicates that you've walked
/// the entire table.
///
/// Attribute certificate table entries can contain any certificate type, as long as
/// the entry has the correct dwLength value, a unique wRevision value, and a unique
/// wCertificateType value. The most common type of certificate table entry is a
/// WIN_CERTIFICATE structure, which is documented in Wintrust.h and discussed in
/// the remainder of this section.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class Entry
{
/// <summary>
/// Specifies the length of the attribute certificate entry.
/// </summary>
public uint Length { get; set; }
/// <summary>
/// Contains the certificate version number.
/// </summary>
public WindowsCertificateRevision Revision { get; set; }
/// <summary>
/// Specifies the type of content in Certificate.
/// </summary>
public WindowsCertificateType CertificateType { get; set; }
/// <summary>
/// Contains a certificate, such as an Authenticode signature.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#certificate-data"/>
public byte[]? Certificate { get; set; }
}
}

View File

@@ -0,0 +1,51 @@
namespace SabreTools.Serialization.Models.PortableExecutable.BaseRelocation
{
/// <summary>
/// The base relocation table contains entries for all base relocations in
/// the image. The Base Relocation Table field in the optional header data
/// directories gives the number of bytes in the base relocation table. For
/// more information, see Optional Header Data Directories (Image Only).
/// The base relocation table is divided into blocks. Each block represents
/// the base relocations for a 4K page. Each block must start on a 32-bit boundary.
///
/// The loader is not required to process base relocations that are resolved by
/// the linker, unless the load image cannot be loaded at the image base that is
/// specified in the PE header.
///
/// To apply a base relocation, the difference is calculated between the preferred
/// base address and the base where the image is actually loaded. If the image is
/// loaded at its preferred base, the difference is zero and thus the base
/// relocations do not have to be applied.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class Block
{
/// <summary>
/// The image base plus the page RVA is added to each offset to create
/// the VA where the base relocation must be applied.
/// </summary>
public uint PageRVA { get; set; }
/// <summary>
/// The total number of bytes in the base relocation block, including
/// the Page RVA and Block Size fields and the Type/Offset fields that
/// follow.
/// </summary>
public uint BlockSize { get; set; }
/// <summary>
/// The Block Size field is then followed by any number of Type or Offset
/// field entries. Each entry is a WORD (2 bytes) and has the following
/// structure:
///
/// 4 bits - Type - Stored in the high 4 bits of the WORD, a value
/// that indicates the type of base relocation to be
/// applied. For more information, see <see cref="BaseRelocationTypes"/>
/// 12 bits - Offset - Stored in the remaining 12 bits of the WORD, an
/// offset from the starting address that was specified
/// in the Page RVA field for the block. This offset
/// specifies where the base relocation is to be applied.
/// </summary>
public TypeOffsetFieldEntry[]? TypeOffsetFieldEntries { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
namespace SabreTools.Serialization.Models.PortableExecutable.BaseRelocation
{
/// <summary>
/// Type or Offset field entry is a WORD (2 bytes).
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class TypeOffsetFieldEntry
{
/// <summary>
/// Stored in the high 4 bits of the WORD, a value that indicates the type
/// of base relocation to be applied. For more information, see <see cref="BaseRelocationTypes"/>
/// </summary>
public BaseRelocationTypes BaseRelocationType { get; set; }
/// <summary>
/// Stored in the remaining 12 bits of the WORD, an offset from the starting
/// address that was specified in the Page RVA field for the block. This
/// offset specifies where the base relocation is to be applied.
/// </summary>
public ushort Offset { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
namespace SabreTools.Serialization.Models.PortableExecutable
{
// TODO: Add more constants around sizes
// - Debug Directory entry size is a constant value of 28 (0x1C)
public static class Constants
{
public static readonly byte[] SignatureBytes = [0x50, 0x45, 0x00, 0x00];
public const string SignatureString = "PE\0\0";
public const uint SignatureUInt32 = 0x00004550;
}
}

View File

@@ -0,0 +1,29 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.PortableExecutable
{
/// <summary>
/// Each data directory gives the address and size of a table or string that Windows uses.
/// These data directory entries are all loaded into memory so that the system can use them
/// at run time.
///
/// Also, do not assume that the RVAs in this table point to the beginning of a section or
/// that the sections that contain specific tables have specific names.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class DataDirectory
{
/// <summary>
/// The first field, VirtualAddress, is actually the RVA of the table. The RVA
/// is the address of the table relative to the base address of the image when
/// the table is loaded.
/// </summary>
public uint VirtualAddress;
/// <summary>
/// The second field gives the size in bytes.
/// </summary>
public uint Size;
}
}

View File

@@ -0,0 +1,67 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.PortableExecutable.DebugData
{
/// <summary>
/// Image files contain an optional debug directory that indicates what form
/// of debug information is present and where it is. This directory consists
/// of an array of debug directory entries whose location and size are indicated
/// in the image optional header.
///
/// The debug directory can be in a discardable .debug section (if one exists),
/// or it can be included in any other section in the image file, or not be in
/// a section at all.
///
/// Each debug directory entry identifies the location and size of a block of
/// debug information. The specified RVA can be zero if the debug information
/// is not covered by a section header (that is, it resides in the image file
/// and is not mapped into the run-time address space). If it is mapped, the
/// RVA is its address.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class Entry
{
/// <summary>
/// Reserved, must be zero.
/// </summary>
public uint Characteristics;
/// <summary>
/// The time and date that the debug data was created.
/// </summary>
public uint TimeDateStamp;
/// <summary>
/// The major version number of the debug data format.
/// </summary>
public ushort MajorVersion;
/// <summary>
/// The minor version number of the debug data format.
/// </summary>
public ushort MinorVersion;
/// <summary>
/// The format of debugging information. This field enables support
/// of multiple debuggers.
/// </summary>
[MarshalAs(UnmanagedType.U4)]
public DebugType DebugType;
/// <summary>
/// The size of the debug data (not including the debug directory itself).
/// </summary>
public uint SizeOfData;
/// <summary>
/// The address of the debug data when loaded, relative to the image base.
/// </summary>
public uint AddressOfRawData;
/// <summary>
/// The file pointer to the debug data.
/// </summary>
public uint PointerToRawData;
}
}

View File

@@ -0,0 +1,44 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.PortableExecutable.DebugData
{
/// <summary>
/// PDB 2.0 files
/// </summary>
/// <see href="https://www.debuginfo.com/articles/debuginfomatch.html"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class NB10ProgramDatabase
{
/// <summary>
/// "CodeView signature, equal to “NB10”
/// </summary>
public uint Signature;
/// <summary>
/// CodeView offset. Set to 0, because debug information
/// is stored in a separate file.
/// </summary>
public uint Offset;
/// <summary>
/// The time when debug information was created (in seconds
/// since 01.01.1970)
/// </summary>
public uint Timestamp;
/// <summary>
/// Ever-incrementing value, which is initially set to 1 and
/// incremented every time when a part of the PDB file is updated
/// without rewriting the whole file.
/// </summary>
public uint Age;
/// <summary>
/// Null-terminated name of the PDB file. It can also contain full
/// or partial path to the file.
/// </summary>
/// <remarks>Is this Unicode?</remarks>
[MarshalAs(UnmanagedType.LPStr)]
public string? PdbFileName;
}
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.PortableExecutable.DebugData
{
/// <summary>
/// This file describes the format of the pdb (Program Database) files of the "RSDS"
/// or "DS" type which are emitted by Miscrosoft's link.exe from version 7 and above.
/// </summary>
/// <see href="http://www.godevtool.com/Other/pdb.htm"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class RSDSProgramDatabase
{
/// <summary>
/// "RSDS" signature
/// </summary>
public uint Signature;
/// <summary>
/// 16-byte Globally Unique Identifier
/// </summary>
public Guid GUID;
/// <summary>
/// Ever-incrementing value, which is initially set to 1 and
/// incremented every time when a part of the PDB file is updated
/// without rewriting the whole file.
/// </summary>
public uint Age;
/// <summary>
/// zero terminated UTF8 path and file name
/// </summary>
#if NET472_OR_GREATER || NETCOREAPP
[MarshalAs(UnmanagedType.LPUTF8Str)]
#else
[MarshalAs(UnmanagedType.LPStr)]
#endif
public string? PathAndFileName;
}
}

View File

@@ -0,0 +1,38 @@
namespace SabreTools.Serialization.Models.PortableExecutable.DebugData
{
/// <summary>
/// The .debug section is used in object files to contain compiler-generated debug
/// information and in image files to contain all of the debug information that is
/// generated. This section describes the packaging of debug information in object
/// and image files.
///
/// The next section describes the format of the debug directory, which can be
/// anywhere in the image. Subsequent sections describe the "groups" in object
/// files that contain debug information.
///
/// The default for the linker is that debug information is not mapped into the
/// address space of the image. A .debug section exists only when debug information
/// is mapped in the address space.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class Table
{
/// <summary>
/// Image files contain an optional debug directory that indicates what form
/// of debug information is present and where it is. This directory consists
/// of an array of debug directory entries whose location and size are
/// indicated in the image optional header.
///
/// The debug directory can be in a discardable .debug section (if one exists),
/// or it can be included in any other section in the image file, or not be
/// in a section at all.
///
/// Each debug directory entry identifies the location and size of a block of
/// debug information. The specified RVA can be zero if the debug information
/// is not covered by a section header (that is, it resides in the image
/// file and is not mapped into the run-time address space). If it is mapped,
/// the RVA is its address.
/// </summary>
public Entry[]? DebugDirectoryTable { get; set; }
}
}

View File

@@ -0,0 +1,107 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.PortableExecutable.DelayLoad
{
/// <summary>
/// The delay-load directory table is the counterpart to the import directory
/// table. It can be retrieved through the Delay Import Descriptor entry in
/// the optional header data directories list (offset 200).
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class DirectoryTable
{
/// <summary>
/// Must be zero.
/// </summary>
/// <remarks>
/// As yet, no attribute flags are defined. The linker sets this field to
/// zero in the image. This field can be used to extend the record by
/// indicating the presence of new fields, or it can be used to indicate
/// behaviors to the delay or unload helper functions.
/// </remarks>
public uint Attributes;
/// <summary>
/// The RVA of the name of the DLL to be loaded. The name resides in the
/// read-only data section of the image.
/// </summary>
/// <remarks>
/// The name of the DLL to be delay-loaded resides in the read-only data
/// section of the image. It is referenced through the szName field.
/// </remarks>
public uint NameRVA;
/// <summary>
/// The RVA of the module handle (in the data section of the image) of the DLL
/// to be delay-loaded. It is used for storage by the routine that is supplied
/// to manage delay-loading.
/// </summary>
/// <remarks>
/// The handle of the DLL to be delay-loaded is in the data section of the image.
/// The phmod field points to the handle. The supplied delay-load helper uses
/// this location to store the handle to the loaded DLL.
/// </remarks>
public uint ModuleHandle;
/// <summary>
/// The RVA of the delay-load import address table.
/// </summary>
/// <remarks>
/// The delay import address table (IAT) is referenced by the delay import
/// descriptor through the pIAT field. The delay-load helper updates these
/// pointers with the real entry points so that the thunks are no longer in
/// the calling loop. The function pointers are accessed by using the expression
/// pINT->u1.Function.
/// </remarks>
public uint DelayImportAddressTable;
/// <summary>
/// The RVA of the delay-load name table, which contains the names of the imports
/// that might need to be loaded. This matches the layout of the import name table.
/// </summary>
/// <remarks>
/// The delay import name table (INT) contains the names of the imports that might
/// require loading. They are ordered in the same fashion as the function pointers
/// in the IAT. They consist of the same structures as the standard INT and are
/// accessed by using the expression pINT->u1.AddressOfData->Name[0].
/// </remarks>
public uint DelayImportNameTable;
/// <summary>
/// The RVA of the bound delay-load address table, if it exists.
/// </summary>
/// <remarks>
/// The delay bound import address table (BIAT) is an optional table of
/// IMAGE_THUNK_DATA items that is used along with the timestamp field of the
/// delay-load directory table by a post-process binding phase.
/// </remarks>
public uint BoundDelayImportTable;
/// <summary>
/// The RVA of the unload delay-load address table, if it exists. This is an exact
/// copy of the delay import address table. If the caller unloads the DLL, this
/// table should be copied back over the delay import address table so that
/// subsequent calls to the DLL continue to use the thunking mechanism correctly.
/// </summary>
/// <remarks>
/// The delay unload import address table (UIAT) is an optional table of
/// IMAGE_THUNK_DATA items that the unload code uses to handle an explicit unload
/// request. It consists of initialized data in the read-only section that is an
/// exact copy of the original IAT that referred the code to the delay-load thunks.
/// On the unload request, the library can be freed, the *phmod cleared, and the
/// UIAT written over the IAT to restore everything to its preload state.
/// </remarks>
public uint UnloadDelayImportTable;
/// <summary>
/// The timestamp of the DLL to which this image has been bound.
/// </summary>
/// <remarks>
/// The delay bound import address table (BIAT) is an optional table of
/// IMAGE_THUNK_DATA items that is used along with the timestamp field of the
/// delay-load directory table by a post-process binding phase.
/// </remarks>
public uint TimeStamp;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,250 @@
using System.Collections.Generic;
namespace SabreTools.Serialization.Models.PortableExecutable
{
/// <summary>
/// The following list describes the Microsoft PE executable format, with the
/// base of the image header at the top. The section from the MS-DOS 2.0
/// Compatible EXE Header through to the unused section just before the PE header
/// is the MS-DOS 2.0 Section, and is used for MS-DOS compatibility only.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class Executable
{
/// <summary>
/// MS-DOS executable stub
/// </summary>
public MSDOS.Executable? Stub { get; set; }
/// <summary>
/// After the MS-DOS stub, at the file offset specified at offset 0x3c, is a 4-byte
/// signature that identifies the file as a PE format image file. This signature is "PE\0\0"
/// (the letters "P" and "E" followed by two null bytes).
/// </summary>
public string? Signature { get; set; }
/// <summary>
/// File header
/// </summary>
public COFF.FileHeader? FileHeader { get; set; }
/// <summary>
/// Microsoft extended optional header
/// </summary>
public OptionalHeader? OptionalHeader { get; set; }
/// <summary>
/// Section table
/// </summary>
public COFF.SectionHeader[]? SectionTable { get; set; }
/// <summary>
/// Symbol table
/// </summary>
public COFF.SymbolTableEntries.BaseEntry[]? SymbolTable { get; set; }
/// <summary>
/// String table
/// </summary>
public COFF.StringTable? StringTable { get; set; }
#region Data Directories
/// <summary>
/// The export data section, named .edata, contains information about symbols that other images
/// can access through dynamic linking. Exported symbols are generally found in DLLs, but DLLs
/// can also import symbols.
///
/// An overview of the general structure of the export section is described below. The tables
/// described are usually contiguous in the file in the order shown (though this is not
/// required). Only the export directory table and export address table are required to export
/// symbols as ordinals. (An ordinal is an export that is accessed directly by its export
/// address table index.) The name pointer table, ordinal table, and export name table all
/// exist to support use of export names.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
#region Export Table (.edata)
/// <summary>
/// A table with just one row (unlike the debug directory). This table indicates the
/// locations and sizes of the other export tables.
/// </summary>
public Export.DirectoryTable? ExportDirectoryTable { get; set; }
/// <summary>
/// An array of RVAs of exported symbols. These are the actual addresses of the exported
/// functions and data within the executable code and data sections. Other image files
/// can import a symbol by using an index to this table (an ordinal) or, optionally, by
/// using the public name that corresponds to the ordinal if a public name is defined.
/// </summary>
public Export.AddressTableEntry[]? ExportAddressTable { get; set; }
/// <summary>
/// An array of pointers to the public export names, sorted in ascending order.
/// </summary>
public Export.NamePointerTable? NamePointerTable { get; set; }
/// <summary>
/// An array of the ordinals that correspond to members of the name pointer table. The
/// correspondence is by position; therefore, the name pointer table and the ordinal table
/// must have the same number of members. Each ordinal is an index into the export address
/// table.
/// </summary>
public Export.OrdinalTable? OrdinalTable { get; set; }
/// <summary>
/// A series of null-terminated ASCII strings. Members of the name pointer table point into
/// this area. These names are the public names through which the symbols are imported and
/// exported; they are not necessarily the same as the private names that are used within
/// the image file.
/// </summary>
public Export.NameTable? ExportNameTable { get; set; }
#endregion
/// <summary>
/// All image files that import symbols, including virtually all executable (EXE) files,
/// have an .idata section. A typical file layout for the import information follows:
///
/// - Directory Table
/// Null Directory Entry
/// - DLL1 Import Lookup Table
/// Null
/// - DLL2 Import Lookup Table
/// Null
/// - DLL3 Import Lookup Table
/// Null
/// - Hint-Name Table
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
#region Import Table (.idata) and Import Address Table
/// <summary>
/// The import information begins with the import directory table, which describes the
/// remainder of the import information.
/// </summary>
public Import.DirectoryTableEntry[]? ImportDirectoryTable { get; set; }
/// <summary>
/// An import lookup table is an array of 32-bit numbers for PE32 or an array of 64-bit
/// numbers for PE32+.
/// </summary>
public Dictionary<int, Import.LookupTableEntry[]?>? ImportLookupTables { get; set; }
/// <summary>
/// These addresses are the actual memory addresses of the symbols, although technically
/// they are still called "virtual addresses".
/// </summary>
public Dictionary<int, Import.AddressTableEntry[]?>? ImportAddressTables { get; set; }
/// <summary>
/// One hint/name table suffices for the entire import section.
/// </summary>
public Import.HintNameTableEntry[]? HintNameTable { get; set; }
#endregion
#region Resource Table (.rsrc)
/// <summary>
/// Resource directory table (.rsrc)
/// </summary>
public Resource.DirectoryTable? ResourceDirectoryTable { get; set; }
#endregion
// TODO: Handle Exception Table
#region Certificate Table
/// <summary>
/// Attribute certificate table
/// </summary>
public AttributeCertificate.Entry[]? AttributeCertificateTable { get; set; }
#endregion
#region Base Relocation Table (.reloc)
/// <summary>
/// Base relocation table
/// </summary>
public BaseRelocation.Block[]? BaseRelocationTable { get; set; }
#endregion
#region Debug Data (.debug*)
/// <summary>
/// Debug table
/// </summary>
public DebugData.Table? DebugTable { get; set; }
#endregion
// TODO: Handle Architecture
// TODO: Handle Global Ptr
// TODO: Thread Local Storage (.tls)
// TODO: Load Configuration Table
// TODO: Bound Import Table
#region Delay Load Table
/// <summary>
/// Delay-load directory table
/// </summary>
public DelayLoad.DirectoryTable? DelayLoadDirectoryTable { get; set; }
#endregion
// TODO: CLR Runtime Header (.cormeta)
// TODO: Reserved
#endregion
#region Named Sections
// TODO: Support grouped sections in section reading and parsing
// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#grouped-sections-object-only
// Grouped sections are ordered and mean that the data in the sections contributes
// to the "base" section (the one without the "$X" suffix). This may negatively impact
// the use of some of the different types of executables.
// .cormeta - CLR metadata is stored in this section. It is used to indicate that
// the object file contains managed code. The format of the metadata is not
// documented, but can be handed to the CLR interfaces for handling metadata.
// .drectve - A section is a directive section if it has the IMAGE_SCN_LNK_INFO
// flag set in the section header and has the .drectve section name. The linker
// removes a .drectve section after processing the information, so the section
// does not appear in the image file that is being linked.
//
// A .drectve section consists of a string of text that can be encoded as ANSI
// or UTF-8. If the UTF-8 byte order marker (BOM, a three-byte prefix that
// consists of 0xEF, 0xBB, and 0xBF) is not present, the directive string is
// interpreted as ANSI. The directive string is a series of linker options that
// are separated by spaces. Each option contains a hyphen, the option name, and
// any appropriate attribute. If an option contains spaces, the option must be
// enclosed in quotes. The .drectve section must not have relocations or line
// numbers.
//
// TODO: Can we implement reading/parsing the .drectve section?
// .pdata Section - Multiple formats per entry
// .sxdata - The valid exception handlers of an object are listed in the .sxdata
// section of that object. The section is marked IMAGE_SCN_LNK_INFO. It contains
// the COFF symbol index of each valid handler, using 4 bytes per index.
//
// Additionally, the compiler marks a COFF object as registered SEH by emitting
// the absolute symbol "@feat.00" with the LSB of the value field set to 1. A
// COFF object with no registered SEH handlers would have the "@feat.00" symbol,
// but no .sxdata section.
//
// TODO: Can we implement reading/parsing the .sxdata section?
#endregion
// TODO: Determine if "Archive (Library) File Format" is worth modelling
}
}

View File

@@ -0,0 +1,37 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.PortableExecutable.Export
{
/// <summary>
/// The export address table contains the address of exported entry points
/// and exported data and absolutes. An ordinal number is used as an index
/// into the export address table.
///
/// Each entry in the export address table is a field that uses one of two
/// formats in the following table. If the address specified is not within
/// the export section (as defined by the address and length that are
/// indicated in the optional header), the field is an export RVA, which is
/// an actual address in code or data. Otherwise, the field is a forwarder RVA,
/// which names a symbol in another DLL.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Explicit)]
public sealed class AddressTableEntry
{
/// <summary>
/// The address of the exported symbol when loaded into memory, relative to
/// the image base. For example, the address of an exported function.
/// </summary>
[FieldOffset(0)] public uint ExportRVA;
/// <summary>
/// The pointer to a null-terminated ASCII string in the export section. This
/// string must be within the range that is given by the export table data
/// directory entry. See Optional Header Data Directories (Image Only). This
/// string gives the DLL name and the name of the export (for example,
/// "MYDLL.expfunc") or the DLL name and the ordinal number of the export
/// (for example, "MYDLL.#27").
/// </summary>
[FieldOffset(0)] public uint ForwarderRVA;
}
}

View File

@@ -0,0 +1,78 @@
namespace SabreTools.Serialization.Models.PortableExecutable.Export
{
/// <summary>
/// The export symbol information begins with the export directory table,
/// which describes the remainder of the export symbol information. The
/// export directory table contains address information that is used to resolve
/// imports to the entry points within this image.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class DirectoryTable
{
/// <summary>
/// Reserved, must be 0.
/// </summary>
public uint ExportFlags { get; set; }
/// <summary>
/// The time and date that the export data was created.
/// </summary>
public uint TimeDateStamp { get; set; }
/// <summary>
/// The major version number. The major and minor version numbers can be set
/// by the user.
/// </summary>
public ushort MajorVersion { get; set; }
/// <summary>
/// The minor version number.
/// </summary>
public ushort MinorVersion { get; set; }
/// <summary>
/// The address of the ASCII string that contains the name of the DLL. This
/// address is relative to the image base.
/// </summary>
public uint NameRVA { get; set; }
/// <summary>
/// ASCII string that contains the name of the DLL.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// The starting ordinal number for exports in this image. This field specifies
/// the starting ordinal number for the export address table. It is usually set
/// to 1.
/// </summary>
public uint OrdinalBase { get; set; }
/// <summary>
/// The number of entries in the export address table.
/// </summary>
public uint AddressTableEntries { get; set; }
/// <summary>
/// The number of entries in the name pointer table. This is also the number of
/// entries in the ordinal table.
/// </summary>
public uint NumberOfNamePointers { get; set; }
/// <summary>
/// The address of the export address table, relative to the image base.
/// </summary>
public uint ExportAddressTableRVA { get; set; }
/// <summary>
/// The address of the export name pointer table, relative to the image base.
/// The table size is given by the Number of Name Pointers field.
/// </summary>
public uint NamePointerRVA { get; set; }
/// <summary>
/// The address of the ordinal table, relative to the image base.
/// </summary>
public uint OrdinalTableRVA { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
namespace SabreTools.Serialization.Models.PortableExecutable.Export
{
/// <summary>
/// The export name pointer table is an array of addresses (RVAs) into the export name table.
/// The pointers are 32 bits each and are relative to the image base. The pointers are ordered
/// lexically to allow binary searches.
///
/// An export name is defined only if the export name pointer table contains a pointer to it.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class NamePointerTable
{
/// <summary>
/// The pointers are 32 bits each and are relative to the image base.
/// </summary>
public uint[]? Pointers { get; set; }
}
}

View File

@@ -0,0 +1,27 @@
namespace SabreTools.Serialization.Models.PortableExecutable.Export
{
/// <summary>
/// The export name table contains the actual string data that was pointed to by the export
/// name pointer table. The strings in this table are public names that other images can use
/// to import the symbols. These public export names are not necessarily the same as the
/// private symbol names that the symbols have in their own image file and source code,
/// although they can be.
///
/// Every exported symbol has an ordinal value, which is just the index into the export
/// address table. Use of export names, however, is optional. Some, all, or none of the
/// exported symbols can have export names. For exported symbols that do have export names,
/// corresponding entries in the export name pointer table and export ordinal table work
/// together to associate each name with an ordinal.
///
/// The structure of the export name table is a series of null-terminated ASCII strings
/// of variable length.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class NameTable
{
/// <summary>
/// A series of null-terminated ASCII strings of variable length.
/// </summary>
public string[]? Strings { get; set; }
}
}

View File

@@ -0,0 +1,42 @@
namespace SabreTools.Serialization.Models.PortableExecutable.Export
{
/// <summary>
/// The export ordinal table is an array of 16-bit unbiased indexes into the export address table.
/// Ordinals are biased by the Ordinal Base field of the export directory table. In other words,
/// the ordinal base must be subtracted from the ordinals to obtain true indexes into the export
/// address table.
///
/// The export name pointer table and the export ordinal table form two parallel arrays that are
/// separated to allow natural field alignment. These two tables, in effect, operate as one table,
/// in which the Export Name Pointer column points to a public (exported) name and the Export
/// Ordinal column gives the corresponding ordinal for that public name. A member of the export
/// name pointer table and a member of the export ordinal table are associated by having the same
/// position (index) in their respective arrays.
///
/// Thus, when the export name pointer table is searched and a matching string is found at position
/// i, the algorithm for finding the symbol's RVA and biased ordinal is:
///
/// i = Search_ExportNamePointerTable(name);
/// ordinal = ExportOrdinalTable[i];
///
/// rva = ExportAddressTable[ordinal];
/// biased_ordinal = ordinal + OrdinalBase;
///
/// When searching for a symbol by(biased) ordinal, the algorithm for finding the symbol's RVA
/// and name is:
///
/// ordinal = biased_ordinal - OrdinalBase;
/// i = Search_ExportOrdinalTable(ordinal);
///
/// rva = ExportAddressTable[ordinal];
/// name = ExportNameTable[i];
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class OrdinalTable
{
/// <summary>
/// An array of 16-bit unbiased indexes into the export address table
/// </summary>
public ushort[]? Indexes { get; set; }
}
}

View File

@@ -0,0 +1,36 @@
namespace SabreTools.Serialization.Models.PortableExecutable.Import
{
/// <summary>
/// The structure and content of the import address table are identical to those of
/// the import lookup table, until the file is bound. During binding, the entries in
/// the import address table are overwritten with the 32-bit (for PE32) or 64-bit
/// (for PE32+) addresses of the symbols that are being imported. These addresses are
/// the actual memory addresses of the symbols, although technically they are still
/// called "virtual addresses." The loader typically processes the binding.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class AddressTableEntry
{
/// <summary>
/// If this bit is set, import by ordinal. Otherwise, import by name. Bit is
/// masked as 0x80000000 for PE32, 0x8000000000000000 for PE32+.
/// </summary>
/// <remarks>Bit 31/63</remarks>
public bool OrdinalNameFlag { get; set; }
/// <summary>
/// A 16-bit ordinal number. This field is used only if the Ordinal/Name Flag
/// bit field is 1 (import by ordinal). Bits 30-15 or 62-15 must be 0.
/// </summary>
/// <remarks>Bits 15-0</remarks>
public ushort OrdinalNumber { get; set; }
/// <summary>
/// A 31-bit RVA of a hint/name table entry. This field is used only if the
/// Ordinal/Name Flag bit field is 0 (import by name). For PE32+ bits 62-31
/// must be zero.
/// </summary>
/// <remarks>Bits 30-0</remarks>
public uint HintNameTableRVA { get; set; }
}
}

View File

@@ -0,0 +1,50 @@
namespace SabreTools.Serialization.Models.PortableExecutable.Import
{
/// <summary>
/// The import information begins with the import directory table, which
/// describes the remainder of the import information. The import directory
/// table contains address information that is used to resolve fixup references
/// to the entry points within a DLL image. The import directory table consists
/// of an array of import directory entries, one entry for each DLL to which
/// the image refers. The last directory entry is empty (filled with null values),
/// which indicates the end of the directory table.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class DirectoryTableEntry
{
/// <summary>
/// The RVA of the import lookup table. This table contains a name or ordinal
/// for each import. (The name "Characteristics" is used in Winnt.h, but no
/// longer describes this field.)
/// </summary>
public uint ImportLookupTableRVA { get; set; }
/// <summary>
/// The stamp that is set to zero until the image is bound. After the image is
/// bound, this field is set to the time/data stamp of the DLL.
/// </summary>
public uint TimeDateStamp { get; set; }
/// <summary>
/// The index of the first forwarder reference.
/// </summary>
public uint ForwarderChain { get; set; }
/// <summary>
/// The address of an ASCII string that contains the name of the DLL. This address
/// is relative to the image base.
/// </summary>
public uint NameRVA { get; set; }
/// <summary>
/// ASCII string that contains the name of the DLL.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// The RVA of the import address table. The contents of this table are identical
/// to the contents of the import lookup table until the image is bound.
/// </summary>
public uint ImportAddressTableRVA { get; set; }
}
}

View File

@@ -0,0 +1,27 @@
using System.Runtime.InteropServices;
namespace SabreTools.Serialization.Models.PortableExecutable.Import
{
/// <summary>
/// One hint/name table suffices for the entire import section.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
[StructLayout(LayoutKind.Sequential)]
public sealed class HintNameTableEntry
{
/// <summary>
/// An index into the export name pointer table. A match is attempted first
/// with this value. If it fails, a binary search is performed on the DLL's
/// export name pointer table.
/// </summary>
public ushort Hint;
/// <summary>
/// An ASCII string that contains the name to import. This is the string that
/// must be matched to the public name in the DLL. This string is case sensitive
/// and terminated by a null byte.
/// </summary>
[MarshalAs(UnmanagedType.LPStr)]
public string? Name;
}
}

View File

@@ -0,0 +1,36 @@
namespace SabreTools.Serialization.Models.PortableExecutable.Import
{
/// <summary>
/// An import lookup table is an array of 32-bit numbers for PE32 or an array of
/// 64-bit numbers for PE32+. Each entry uses the bit-field format that is described
/// in the following table. In this format, bit 31 is the most significant bit for
/// PE32 and bit 63 is the most significant bit for PE32+. The collection of these
/// entries describes all imports from a given DLL. The last entry is set to zero
/// (NULL) to indicate the end of the table.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/windows/win32/debug/pe-format"/>
public sealed class LookupTableEntry
{
/// <summary>
/// If this bit is set, import by ordinal. Otherwise, import by name. Bit is
/// masked as 0x80000000 for PE32, 0x8000000000000000 for PE32+.
/// </summary>
/// <remarks>Bit 31/63</remarks>
public bool OrdinalNameFlag { get; set; }
/// <summary>
/// A 16-bit ordinal number. This field is used only if the Ordinal/Name Flag
/// bit field is 1 (import by ordinal). Bits 30-15 or 62-15 must be 0.
/// </summary>
/// <remarks>Bits 15-0</remarks>
public ushort OrdinalNumber { get; set; }
/// <summary>
/// A 31-bit RVA of a hint/name table entry. This field is used only if the
/// Ordinal/Name Flag bit field is 0 (import by name). For PE32+ bits 62-31
/// must be zero.
/// </summary>
/// <remarks>Bits 30-0</remarks>
public uint HintNameTableRVA { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More