diff --git a/SabreTools.Serialization/Wrappers/NewExecutable.cs b/SabreTools.Serialization/Wrappers/NewExecutable.cs
index f7ec8f7a..57985b7f 100644
--- a/SabreTools.Serialization/Wrappers/NewExecutable.cs
+++ b/SabreTools.Serialization/Wrappers/NewExecutable.cs
@@ -24,12 +24,278 @@ namespace SabreTools.Serialization.Wrappers
///
public Models.NewExecutable.NonResidentNameTableEntry[]? NonResidentNameTable => Model.NonResidentNameTable;
+ ///
+ /// Address of the overlay, if it exists
+ ///
+ ///
+ public int OverlayAddress
+ {
+ get
+ {
+ lock (_sourceDataLock)
+ {
+ // Use the cached data if possible
+ if (_overlayAddress != null)
+ return _overlayAddress.Value;
+
+ // Get the end of the file, if possible
+ int endOfFile = GetEndOfFile();
+ if (endOfFile == -1)
+ return -1;
+
+ // If a required property is missing
+ if (Header == null || SegmentTable == null || ResourceTable?.ResourceTypes == null)
+ return -1;
+
+ // Search through the segments table to find the furthest
+ int endOfSectionData = -1;
+ foreach (var entry in SegmentTable)
+ {
+ int offset = (entry.Offset << Header.SegmentAlignmentShiftCount) + entry.Length;
+ if (offset > endOfSectionData)
+ endOfSectionData = offset;
+ }
+
+ // Search through the resources table to find the furthest
+ foreach (var entry in ResourceTable.ResourceTypes)
+ {
+ // Skip invalid entries
+ if (entry.ResourceCount == 0 || entry.Resources == null || entry.Resources.Length == 0)
+ continue;
+
+ foreach (var resource in entry.Resources)
+ {
+ int offset = (resource.Offset << ResourceTable.AlignmentShiftCount) + resource.Length;
+ if (offset > endOfSectionData)
+ endOfSectionData = offset;
+ }
+ }
+
+ // If we didn't find the end of section data
+ if (endOfSectionData <= 0)
+ endOfSectionData = -1;
+
+ // Adjust the position of the data by 705 bytes
+ // TODO: Investigate what the byte data is
+ endOfSectionData += 705;
+
+ // Cache and return the position
+ _overlayAddress = endOfSectionData;
+ return _overlayAddress.Value;
+ }
+ }
+ }
+
+ ///
+ /// Overlay data, if it exists
+ ///
+ ///
+ public byte[]? OverlayData
+ {
+ get
+ {
+ lock (_sourceDataLock)
+ {
+ // Use the cached data if possible
+ if (_overlayData != null)
+ return _overlayData;
+
+ // Get the end of the file, if possible
+ int endOfFile = GetEndOfFile();
+ if (endOfFile == -1)
+ return null;
+
+ // If a required property is missing
+ if (Header == null || SegmentTable == null || ResourceTable?.ResourceTypes == null)
+ return null;
+
+ // Search through the segments table to find the furthest
+ int endOfSectionData = -1;
+ foreach (var entry in SegmentTable)
+ {
+ int offset = (entry.Offset << Header.SegmentAlignmentShiftCount) + entry.Length;
+ if (offset > endOfSectionData)
+ endOfSectionData = offset;
+ }
+
+ // Search through the resources table to find the furthest
+ foreach (var entry in ResourceTable.ResourceTypes)
+ {
+ // Skip invalid entries
+ if (entry.ResourceCount == 0 || entry.Resources == null || entry.Resources.Length == 0)
+ continue;
+
+ foreach (var resource in entry.Resources)
+ {
+ int offset = (resource.Offset << ResourceTable.AlignmentShiftCount) + resource.Length;
+ if (offset > endOfSectionData)
+ endOfSectionData = offset;
+ }
+ }
+
+ // If we didn't find the end of section data
+ if (endOfSectionData <= 0)
+ return null;
+
+ // Adjust the position of the data by 705 bytes
+ // TODO: Investigate what the byte data is
+ endOfSectionData += 705;
+
+ // If we're at the end of the file, cache an empty byte array
+ if (endOfSectionData >= endOfFile)
+ {
+ _overlayData = [];
+ return _overlayData;
+ }
+
+ // Otherwise, cache and return the data
+ int overlayLength = endOfFile - endOfSectionData;
+ _overlayData = ReadFromDataSource(endOfSectionData, overlayLength);
+ return _overlayData;
+ }
+ }
+ }
+
+ ///
+ /// Overlay strings, if they exist
+ ///
+ public List? OverlayStrings
+ {
+ get
+ {
+ lock (_sourceDataLock)
+ {
+ // Use the cached data if possible
+ if (_overlayStrings != null)
+ return _overlayStrings;
+
+ // Get the end of the file, if possible
+ int endOfFile = GetEndOfFile();
+ if (endOfFile == -1)
+ return null;
+
+ // If a required property is missing
+ if (Header == null || SegmentTable == null || ResourceTable?.ResourceTypes == null)
+ return null;
+
+ // Search through the segments table to find the furthest
+ int endOfSectionData = -1;
+ foreach (var entry in SegmentTable)
+ {
+ int offset = (entry.Offset << Header.SegmentAlignmentShiftCount) + entry.Length;
+ if (offset > endOfSectionData)
+ endOfSectionData = offset;
+ }
+
+ // Search through the resources table to find the furthest
+ foreach (var entry in ResourceTable.ResourceTypes)
+ {
+ // Skip invalid entries
+ if (entry.ResourceCount == 0 || entry.Resources == null || entry.Resources.Length == 0)
+ continue;
+
+ foreach (var resource in entry.Resources)
+ {
+ int offset = (resource.Offset << ResourceTable.AlignmentShiftCount) + resource.Length;
+ if (offset > endOfSectionData)
+ endOfSectionData = offset;
+ }
+ }
+
+ // If we didn't find the end of section data
+ if (endOfSectionData <= 0)
+ return null;
+
+ // Adjust the position of the data by 705 bytes
+ // TODO: Investigate what the byte data is
+ endOfSectionData += 705;
+
+ // If we're at the end of the file, cache an empty list
+ if (endOfSectionData >= endOfFile)
+ {
+ _overlayStrings = [];
+ return _overlayStrings;
+ }
+
+ // TODO: Revisit the 16 MiB limit
+ // Cap the check for overlay strings to 16 MiB (arbitrary)
+ int overlayLength = Math.Min(endOfFile - endOfSectionData, 16 * 1024 * 1024);
+
+ // Otherwise, cache and return the strings
+ _overlayStrings = ReadStringsFromDataSource(endOfSectionData, overlayLength, charLimit: 3);
+ return _overlayStrings;
+ }
+ }
+ }
+
///
public Models.NewExecutable.ResidentNameTableEntry[]? ResidentNameTable => Model.ResidentNameTable;
+ ///
+ public Models.NewExecutable.ResourceTable? ResourceTable => Model.ResourceTable;
+
+ ///
+ public Models.NewExecutable.SegmentTableEntry[]? SegmentTable => Model.SegmentTable;
+
///
public Models.MSDOS.Executable? Stub => Model.Stub;
+ ///
+ /// Stub executable data, if it exists
+ ///
+ public byte[]? StubExecutableData
+ {
+ get
+ {
+ lock (_sourceDataLock)
+ {
+ // If we already have cached data, just use that immediately
+ if (_stubExecutableData != null)
+ return _stubExecutableData;
+
+ if (Stub?.Header?.NewExeHeaderAddr == null)
+ return null;
+
+ // Populate the raw stub executable data based on the source
+ int endOfStubHeader = 0x40;
+ int lengthOfStubExecutableData = (int)Stub.Header.NewExeHeaderAddr - endOfStubHeader;
+ _stubExecutableData = ReadFromDataSource(endOfStubHeader, lengthOfStubExecutableData);
+
+ // Cache and return the stub executable data, even if null
+ return _stubExecutableData;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Instance Variables
+
+ ///
+ /// Address of the overlay, if it exists
+ ///
+ private int? _overlayAddress = null;
+
+ ///
+ /// Overlay data, if it exists
+ ///
+ private byte[]? _overlayData = null;
+
+ ///
+ /// Overlay strings, if they exist
+ ///
+ private List? _overlayStrings = null;
+
+ ///
+ /// Stub executable data, if it exists
+ ///
+ private byte[]? _stubExecutableData = null;
+
+ ///
+ /// Lock object for reading from the source
+ ///
+ private readonly object _sourceDataLock = new();
+
#endregion
#region Constructors
diff --git a/SabreTools.Serialization/Wrappers/PortableExecutable.cs b/SabreTools.Serialization/Wrappers/PortableExecutable.cs
index 00355a20..3bc400cc 100644
--- a/SabreTools.Serialization/Wrappers/PortableExecutable.cs
+++ b/SabreTools.Serialization/Wrappers/PortableExecutable.cs
@@ -58,21 +58,21 @@ namespace SabreTools.Serialization.Wrappers
lock (_sourceDataLock)
{
// If the section table is missing
- if (Model.SectionTable == null)
+ if (SectionTable == null)
return null;
// If the address is missing
- if (Model.OptionalHeader?.AddressOfEntryPoint == null)
+ if (OptionalHeader?.AddressOfEntryPoint == null)
return null;
// If we have no entry point
- int entryPointAddress = (int)Model.OptionalHeader.AddressOfEntryPoint.ConvertVirtualAddress(Model.SectionTable);
+ int entryPointAddress = (int)OptionalHeader.AddressOfEntryPoint.ConvertVirtualAddress(SectionTable);
if (entryPointAddress == 0)
return null;
// If the entry point matches with the start of a section, use that
int entryPointSection = FindEntryPointSectionIndex();
- if (entryPointSection >= 0 && Model.OptionalHeader.AddressOfEntryPoint == Model.SectionTable[entryPointSection]?.VirtualAddress)
+ if (entryPointSection >= 0 && OptionalHeader.AddressOfEntryPoint == SectionTable[entryPointSection]?.VirtualAddress)
return GetSectionData(entryPointSection);
// If we already have cached data, just use that immediately
@@ -213,27 +213,27 @@ namespace SabreTools.Serialization.Wrappers
return -1;
// If the section table is missing
- if (Model.SectionTable == null)
+ if (SectionTable == null)
return -1;
// If we have certificate data, use that as the end
- if (Model.OptionalHeader?.CertificateTable != null)
+ if (OptionalHeader?.CertificateTable != null)
{
- int certificateTableAddress = (int)Model.OptionalHeader.CertificateTable.VirtualAddress.ConvertVirtualAddress(Model.SectionTable);
+ int certificateTableAddress = (int)OptionalHeader.CertificateTable.VirtualAddress.ConvertVirtualAddress(SectionTable);
if (certificateTableAddress != 0 && certificateTableAddress < endOfFile)
endOfFile = certificateTableAddress;
}
// Search through all sections and find the furthest a section goes
int endOfSectionData = -1;
- foreach (var section in Model.SectionTable)
+ foreach (var section in SectionTable)
{
// If we have an invalid section
if (section == null)
continue;
// If we have an invalid section address
- int sectionAddress = (int)section.VirtualAddress.ConvertVirtualAddress(Model.SectionTable);
+ int sectionAddress = (int)section.VirtualAddress.ConvertVirtualAddress(SectionTable);
if (sectionAddress == 0)
continue;
@@ -284,27 +284,27 @@ namespace SabreTools.Serialization.Wrappers
return null;
// If the section table is missing
- if (Model.SectionTable == null)
+ if (SectionTable == null)
return null;
// If we have certificate data, use that as the end
- if (Model.OptionalHeader?.CertificateTable != null)
+ if (OptionalHeader?.CertificateTable != null)
{
- int certificateTableAddress = (int)Model.OptionalHeader.CertificateTable.VirtualAddress.ConvertVirtualAddress(Model.SectionTable);
+ int certificateTableAddress = (int)OptionalHeader.CertificateTable.VirtualAddress.ConvertVirtualAddress(SectionTable);
if (certificateTableAddress != 0 && certificateTableAddress < endOfFile)
endOfFile = certificateTableAddress;
}
// Search through all sections and find the furthest a section goes
int endOfSectionData = -1;
- foreach (var section in Model.SectionTable)
+ foreach (var section in SectionTable)
{
// If we have an invalid section
if (section == null)
continue;
// If we have an invalid section address
- int sectionAddress = (int)section.VirtualAddress.ConvertVirtualAddress(Model.SectionTable);
+ int sectionAddress = (int)section.VirtualAddress.ConvertVirtualAddress(SectionTable);
if (sectionAddress == 0)
continue;
@@ -362,27 +362,27 @@ namespace SabreTools.Serialization.Wrappers
return null;
// If the section table is missing
- if (Model.SectionTable == null)
+ if (SectionTable == null)
return null;
// If we have certificate data, use that as the end
- if (Model.OptionalHeader?.CertificateTable != null)
+ if (OptionalHeader?.CertificateTable != null)
{
- int certificateTableAddress = (int)Model.OptionalHeader.CertificateTable.VirtualAddress.ConvertVirtualAddress(Model.SectionTable);
+ int certificateTableAddress = (int)OptionalHeader.CertificateTable.VirtualAddress.ConvertVirtualAddress(SectionTable);
if (certificateTableAddress != 0 && certificateTableAddress < endOfFile)
endOfFile = certificateTableAddress;
}
// Search through all sections and find the furthest a section goes
int endOfSectionData = -1;
- foreach (var section in Model.SectionTable)
+ foreach (var section in SectionTable)
{
// If we have an invalid section
if (section == null)
continue;
// If we have an invalid section address
- int sectionAddress = (int)section.VirtualAddress.ConvertVirtualAddress(Model.SectionTable);
+ int sectionAddress = (int)section.VirtualAddress.ConvertVirtualAddress(SectionTable);
if (sectionAddress == 0)
continue;
@@ -424,6 +424,9 @@ namespace SabreTools.Serialization.Wrappers
}
}
+ ///
+ public Models.PortableExecutable.ResourceDirectoryTable? ResourceDirectoryTable => Model.ResourceDirectoryTable;
+
///
/// Sanitized section names
///
@@ -438,14 +441,14 @@ namespace SabreTools.Serialization.Wrappers
return _sectionNames;
// If there are no sections
- if (Model.SectionTable == null)
+ if (SectionTable == null)
return null;
// Otherwise, build and return the cached array
- _sectionNames = new string[Model.SectionTable.Length];
+ _sectionNames = new string[SectionTable.Length];
for (int i = 0; i < _sectionNames.Length; i++)
{
- var section = Model.SectionTable[i];
+ var section = SectionTable[i];
if (section == null)
continue;
@@ -510,13 +513,13 @@ namespace SabreTools.Serialization.Wrappers
return _resourceData;
// If we have no resource table, just return
- if (Model.OptionalHeader?.ResourceTable == null
- || Model.OptionalHeader.ResourceTable.VirtualAddress == 0
- || Model.ResourceDirectoryTable == null)
+ if (OptionalHeader?.ResourceTable == null
+ || OptionalHeader.ResourceTable.VirtualAddress == 0
+ || ResourceDirectoryTable == null)
return null;
// Otherwise, build and return the cached dictionary
- ParseResourceDirectoryTable(Model.ResourceDirectoryTable, types: []);
+ ParseResourceDirectoryTable(ResourceDirectoryTable, types: []);
return _resourceData;
}
}