Use MemoryStream in builders

This commit is contained in:
Matt Nadareski
2022-12-15 13:37:34 -08:00
parent bbe234b459
commit 16e71c910e
7 changed files with 28 additions and 2660 deletions

View File

@@ -5,7 +5,6 @@ using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builder
{
// TODO: Make Stream Data rely on Byte Data
public class BFPK
{
#region Byte Data
@@ -26,95 +25,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
// Cache the current offset
int initialOffset = offset;
// Create a new archive to fill
var archive = new Archive();
#region Header
// Try to parse the header
var header = ParseHeader(data, ref offset);
if (header == null)
return null;
// Set the archive header
archive.Header = header;
#endregion
#region Files
// If we have any files
if (header.Files > 0)
{
var files = new FileEntry[header.Files];
// Read all entries in turn
for (int i = 0; i < header.Files; i++)
{
var file = ParseFileEntry(data, ref offset);
if (file == null)
return null;
files[i] = file;
}
// Set the files
archive.Files = files;
}
#endregion
return archive;
}
/// <summary>
/// Parse a byte array into a header
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled header on success, null on error</returns>
private static Header ParseHeader(byte[] data, ref int offset)
{
// TODO: Use marshalling here instead of building
Header header = new Header();
header.Magic = data.ReadUInt32(ref offset);
if (header.Magic != 0x4b504642)
return null;
header.Version = data.ReadInt32(ref offset);
header.Files = data.ReadInt32(ref offset);
return header;
}
/// <summary>
/// Parse a byte array into a file entry
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled file entry on success, null on error</returns>
private static FileEntry ParseFileEntry(byte[] data, ref int offset)
{
// TODO: Use marshalling here instead of building
FileEntry fileEntry = new FileEntry();
fileEntry.NameSize = data.ReadInt32(ref offset);
if (fileEntry.NameSize > 0)
fileEntry.Name = new string(data.ReadBytes(ref offset, fileEntry.NameSize).Select(b => (char)b).ToArray());
fileEntry.UncompressedSize = data.ReadInt32(ref offset);
fileEntry.Offset = data.ReadInt32(ref offset);
if (fileEntry.Offset > 0)
{
int entryOffset = fileEntry.Offset;
fileEntry.CompressedSize = data.ReadInt32(ref entryOffset);
}
return fileEntry;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data);
dataStream.Position = offset;
return ParseArchive(dataStream);
}
#endregion

View File

@@ -3,7 +3,6 @@ using BurnOutSharp.Models.LinearExecutable;
namespace BurnOutSharp.Builder
{
// TODO: Make Stream Data rely on Byte Data
public static class LinearExecutable
{
#region Byte Data
@@ -24,34 +23,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
// Cache the current offset
int initialOffset = offset;
// Create a new executable to fill
var executable = new Executable();
// Parse the MS-DOS stub
var stub = MSDOS.ParseExecutable(data, offset);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the MS-DOS stub
executable.Stub = stub;
// TODO: Implement LE/LX parsing
return null;
}
/// <summary>
/// Parse a byte array into a Linear Executable information block
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled information block on success, null on error</returns>
private static InformationBlock ParseInformationBlock(byte[] data, int offset)
{
// TODO: Implement LE/LX information block parsing
return null;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data);
dataStream.Position = offset;
return ParseExecutable(dataStream);
}
#endregion

View File

@@ -4,7 +4,6 @@ using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builder
{
// TODO: Make Stream Data rely on Byte Data
public static class MSDOS
{
#region Byte Data
@@ -25,128 +24,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
// Cache the current offset
int initialOffset = offset;
// Create a new executable to fill
var executable = new Executable();
#region Executable Header
// Try to parse the executable header
var executableHeader = ParseExecutableHeader(data, offset);
if (executableHeader == null)
return null;
// Set the executable header
executable.Header = executableHeader;
#endregion
#region Relocation Table
// If the offset for the relocation table doesn't exist
int tableAddress = initialOffset + executableHeader.RelocationTableAddr;
if (tableAddress >= data.Length)
return executable;
// Try to parse the relocation table
var relocationTable = ParseRelocationTable(data, tableAddress, executableHeader.RelocationItems);
if (relocationTable == null)
return null;
// Set the relocation table
executable.RelocationTable = relocationTable;
#endregion
// Return the executable
return executable;
}
/// <summary>
/// Parse a byte array into an MS-DOS executable header
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled executable header on success, null on error</returns>
private static ExecutableHeader ParseExecutableHeader(byte[] data, int offset)
{
// TODO: Use marshalling here instead of building
var header = new ExecutableHeader();
#region Standard Fields
header.Magic = new byte[2];
for (int i = 0; i < header.Magic.Length; i++)
{
header.Magic[i] = data.ReadByte(ref offset);
}
if (header.Magic[0] != 'M' || header.Magic[1] != 'Z')
return null;
header.LastPageBytes = data.ReadUInt16(ref offset);
header.Pages = data.ReadUInt16(ref offset);
header.RelocationItems = data.ReadUInt16(ref offset);
header.HeaderParagraphSize = data.ReadUInt16(ref offset);
header.MinimumExtraParagraphs = data.ReadUInt16(ref offset);
header.MaximumExtraParagraphs = data.ReadUInt16(ref offset);
header.InitialSSValue = data.ReadUInt16(ref offset);
header.InitialSPValue = data.ReadUInt16(ref offset);
header.Checksum = data.ReadUInt16(ref offset);
header.InitialIPValue = data.ReadUInt16(ref offset);
header.InitialCSValue = data.ReadUInt16(ref offset);
header.RelocationTableAddr = data.ReadUInt16(ref offset);
header.OverlayNumber = data.ReadUInt16(ref offset);
#endregion
// If we don't have enough data for PE extensions
if (offset >= data.Length || data.Length - offset < 36)
return header;
#region PE Extensions
header.Reserved1 = new ushort[4];
for (int i = 0; i < header.Reserved1.Length; i++)
{
header.Reserved1[i] = data.ReadUInt16(ref offset);
}
header.OEMIdentifier = data.ReadUInt16(ref offset);
header.OEMInformation = data.ReadUInt16(ref offset);
header.Reserved2 = new ushort[10];
for (int i = 0; i < header.Reserved2.Length; i++)
{
header.Reserved2[i] = data.ReadUInt16(ref offset);
}
header.NewExeHeaderAddr = data.ReadUInt32(ref offset);
#endregion
return header;
}
/// <summary>
/// Parse a byte array into a relocation table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="count">Number of relocation table entries to read</param>
/// <returns>Filled relocation table on success, null on error</returns>
private static RelocationEntry[] ParseRelocationTable(byte[] data, int offset, int count)
{
// TODO: Use marshalling here instead of building
var relocationTable = new RelocationEntry[count];
for (int i = 0; i < count; i++)
{
var entry = new RelocationEntry();
entry.Offset = data.ReadUInt16(ref offset);
entry.Segment = data.ReadUInt16(ref offset);
relocationTable[i] = entry;
}
return relocationTable;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data);
dataStream.Position = offset;
return ParseExecutable(dataStream);
}
#endregion

View File

@@ -7,7 +7,6 @@ using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builder
{
// TODO: Add multi-cabinet reading
// TODO: Make Stream Data rely on Byte Data
public class MicrosoftCabinet
{
#region Constants
@@ -72,234 +71,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
// Cache the current offset
int initialOffset = offset;
// Create a new cabinet to fill
var cabinet = new Cabinet();
#region Cabinet Header
// Try to parse the cabinet header
var cabinetHeader = ParseCabinetHeader(data, ref offset);
if (cabinetHeader == null)
return null;
// Set the cabinet header
cabinet.Header = cabinetHeader;
#endregion
#region Folders
// Set the folder array
cabinet.Folders = new CFFOLDER[cabinetHeader.FolderCount];
// Try to parse each folder, if we have any
for (int i = 0; i < cabinetHeader.FolderCount; i++)
{
var folder = ParseFolder(data, ref offset, cabinetHeader, initialOffset);
if (folder == null)
return null;
// Set the folder
cabinet.Folders[i] = folder;
}
#endregion
#region Files
// Get the files offset
int filesOffset = (int)cabinetHeader.FilesOffset + initialOffset;
if (filesOffset > data.Length)
return null;
// Set the file array
cabinet.Files = new CFFILE[cabinetHeader.FileCount];
// Try to parse each file, if we have any
for (int i = 0; i < cabinetHeader.FileCount; i++)
{
var file = ParseFile(data, ref filesOffset);
if (file == null)
return null;
// Set the file
cabinet.Files[i] = file;
}
#endregion
return cabinet;
}
/// <summary>
/// Parse a byte array into a cabinet header
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled cabinet header on success, null on error</returns>
private static CFHEADER ParseCabinetHeader(byte[] data, ref int offset)
{
// TODO: Use marshalling here instead of building
CFHEADER header = new CFHEADER();
header.Signature = data.ReadUInt32(ref offset);
if (header.Signature != SignatureValue)
return null;
header.Reserved1 = data.ReadUInt32(ref offset);
if (header.Reserved1 != 0x00000000)
return null;
header.CabinetSize = data.ReadUInt32(ref offset);
if (header.CabinetSize > MaximumCabSize)
return null;
header.Reserved2 = data.ReadUInt32(ref offset);
if (header.Reserved2 != 0x00000000)
return null;
header.FilesOffset = data.ReadUInt32(ref offset);
header.Reserved3 = data.ReadUInt32(ref offset);
if (header.Reserved3 != 0x00000000)
return null;
header.VersionMinor = data.ReadByte(ref offset);
header.VersionMajor = data.ReadByte(ref offset);
if (header.VersionMajor != 0x00000001 || header.VersionMinor != 0x00000003)
return null;
header.FolderCount = data.ReadUInt16(ref offset);
if (header.FolderCount > MaximumFolderCount)
return null;
header.FileCount = data.ReadUInt16(ref offset);
if (header.FileCount > MaximumFileCount)
return null;
header.Flags = (HeaderFlags)data.ReadUInt16(ref offset);
header.SetID = data.ReadUInt16(ref offset);
header.CabinetIndex = data.ReadUInt16(ref offset);
if (header.Flags.HasFlag(HeaderFlags.RESERVE_PRESENT))
{
header.HeaderReservedSize = data.ReadUInt16(ref offset);
if (header.HeaderReservedSize > 60_000)
return null;
header.FolderReservedSize = data.ReadByte(ref offset);
header.DataReservedSize = data.ReadByte(ref offset);
if (header.HeaderReservedSize > 0)
header.ReservedData = data.ReadBytes(ref offset, header.HeaderReservedSize);
}
if (header.Flags.HasFlag(HeaderFlags.PREV_CABINET))
{
header.CabinetPrev = data.ReadString(ref offset, Encoding.ASCII);
header.DiskPrev = data.ReadString(ref offset, Encoding.ASCII);
}
if (header.Flags.HasFlag(HeaderFlags.NEXT_CABINET))
{
header.CabinetNext = data.ReadString(ref offset, Encoding.ASCII);
header.DiskNext = data.ReadString(ref offset, Encoding.ASCII);
}
return header;
}
/// <summary>
/// Parse a byte array into a folder
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="header">Cabinet header to get flags and sizes from</param>
/// <param name="initialOffset">Initial offset for calculations</param>
/// <returns>Filled folder on success, null on error</returns>
private static CFFOLDER ParseFolder(byte[] data, ref int offset, CFHEADER header, int initialOffset)
{
// TODO: Use marshalling here instead of building
CFFOLDER folder = new CFFOLDER();
folder.CabStartOffset = data.ReadUInt32(ref offset);
folder.DataCount = data.ReadUInt16(ref offset);
folder.CompressionType = (CompressionType)data.ReadUInt16(ref offset);
if (header.FolderReservedSize > 0)
folder.ReservedData = data.ReadBytes(ref offset, header.FolderReservedSize);
if (folder.CabStartOffset > 0)
{
int blockPtr = initialOffset + (int)folder.CabStartOffset;
folder.DataBlocks = new Dictionary<int, CFDATA>();
for (int i = 0; i < folder.DataCount; i++)
{
int dataStart = blockPtr;
CFDATA dataBlock = ParseDataBlock(data, ref blockPtr, header.DataReservedSize);
folder.DataBlocks[dataStart] = dataBlock;
}
}
return folder;
}
/// <summary>
/// Parse a byte array into a data block
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="dataReservedSize">Reserved byte size for data blocks</param>
/// <returns>Filled folder on success, null on error</returns>
private static CFDATA ParseDataBlock(byte[] data, ref int offset, byte dataReservedSize)
{
// TODO: Use marshalling here instead of building
CFDATA dataBlock = new CFDATA();
dataBlock.Checksum = data.ReadUInt32(ref offset);
dataBlock.CompressedSize = data.ReadUInt16(ref offset);
dataBlock.UncompressedSize = data.ReadUInt16(ref offset);
if (dataBlock.UncompressedSize != 0 && dataBlock.CompressedSize > dataBlock.UncompressedSize)
return null;
if (dataReservedSize > 0)
dataBlock.ReservedData = data.ReadBytes(ref offset, dataReservedSize);
if (dataBlock.CompressedSize > 0)
dataBlock.CompressedData = data.ReadBytes(ref offset, dataBlock.CompressedSize);
return dataBlock;
}
/// <summary>
/// Parse a byte array into a file
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled file on success, null on error</returns>
private static CFFILE ParseFile(byte[] data, ref int offset)
{
// TODO: Use marshalling here instead of building
CFFILE file = new CFFILE();
file.FileSize = data.ReadUInt32(ref offset);
file.FolderStartOffset = data.ReadUInt32(ref offset);
file.FolderIndex = (FolderIndex)data.ReadUInt16(ref offset);
file.Date = data.ReadUInt16(ref offset);
file.Time = data.ReadUInt16(ref offset);
file.Attributes = (Models.MicrosoftCabinet.FileAttributes)data.ReadUInt16(ref offset);
if (file.Attributes.HasFlag(Models.MicrosoftCabinet.FileAttributes.NAME_IS_UTF))
file.Name = data.ReadString(ref offset, Encoding.Unicode);
else
file.Name = data.ReadString(ref offset, Encoding.ASCII);
return file;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data);
dataStream.Position = offset;
return ParseCabinet(dataStream);
}
#endregion

View File

@@ -6,7 +6,6 @@ using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builder
{
// TODO: Make Stream Data rely on Byte Data
public class MoPaQ
{
#region Constants
@@ -225,519 +224,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
// Cache the current offset
int initialOffset = offset;
// Create a new archive to fill
var archive = new Archive();
#region User Data
// Check for User Data
uint possibleSignature = BitConverter.ToUInt32(data, offset);
if (possibleSignature == UserDataSignatureValue)
{
// Save the current position for offset correction
int basePtr = offset;
// Deserialize the user data, returning null if invalid
var userData = ParseUserData(data, ref offset);
if (userData == null)
return null;
// Set the user data
archive.UserData = userData;
// Set the starting position according to the header offset
offset = basePtr + (int)archive.UserData.HeaderOffset;
}
#endregion
#region Archive Header
// Check for the Header
possibleSignature = BitConverter.ToUInt32(data, offset);
if (possibleSignature == ArchiveHeaderSignatureValue)
{
// Try to parse the archive header
var archiveHeader = ParseArchiveHeader(data, ref offset);
if (archiveHeader == null)
return null;
// Set the archive header
archive.ArchiveHeader = archiveHeader;
}
#endregion
#region Hash Table
// Version 1
if (archive.ArchiveHeader.FormatVersion == 0)
{
// If we have a hash table
long hashTableOffset = archive.ArchiveHeader.HashTablePosition;
if (hashTableOffset != 0)
{
// Find the ending offset based on size
long hashTableEnd = hashTableOffset + archive.ArchiveHeader.HashTableSize;
// Read in the hash table
var hashTable = new List<HashEntry>();
while (hashTableOffset < hashTableEnd)
{
var hashEntry = ParseHashEntry(data, ref hashTableOffset);
if (hashEntry == null)
return null;
hashTable.Add(hashEntry);
}
archive.HashTable = hashTable.ToArray();
}
}
// Version 2 and 3
else if (archive.ArchiveHeader.FormatVersion == 1 || archive.ArchiveHeader.FormatVersion == 2)
{
// If we have a hash table
long hashTableOffset = ((uint)archive.ArchiveHeader.HashTablePositionHi << 23) | archive.ArchiveHeader.HashTablePosition;
if (hashTableOffset != 0)
{
// Find the ending offset based on size
long hashTableEnd = hashTableOffset + archive.ArchiveHeader.HashTableSize;
// Read in the hash table
var hashTable = new List<HashEntry>();
while (hashTableOffset < hashTableEnd)
{
var hashEntry = ParseHashEntry(data, ref hashTableOffset);
if (hashEntry == null)
return null;
hashTable.Add(hashEntry);
}
archive.HashTable = hashTable.ToArray();
}
}
// Version 4
else if (archive.ArchiveHeader.FormatVersion == 3)
{
// If we have a hash table
long hashTableOffset = ((uint)archive.ArchiveHeader.HashTablePositionHi << 23) | archive.ArchiveHeader.HashTablePosition;
if (hashTableOffset != 0)
{
// Find the ending offset based on size
long hashTableEnd = hashTableOffset + (long)archive.ArchiveHeader.HashTableSizeLong;
// Read in the hash table
var hashTable = new List<HashEntry>();
while (hashTableOffset < hashTableEnd)
{
var hashEntry = ParseHashEntry(data, ref hashTableOffset);
if (hashEntry == null)
return null;
hashTable.Add(hashEntry);
}
archive.HashTable = hashTable.ToArray();
}
}
#endregion
#region Block Table
// Version 1
if (archive.ArchiveHeader.FormatVersion == 0)
{
// If we have a block table
long blockTableOffset = archive.ArchiveHeader.BlockTablePosition;
if (blockTableOffset != 0)
{
// Find the ending offset based on size
long blockTableEnd = blockTableOffset + archive.ArchiveHeader.BlockTableSize;
// Read in the block table
var blockTable = new List<BlockEntry>();
while (blockTableOffset < blockTableEnd)
{
var blockEntry = ParseBlockEntry(data, ref blockTableOffset);
if (blockEntry == null)
return null;
blockTable.Add(blockEntry);
}
archive.BlockTable = blockTable.ToArray();
}
}
// Version 2 and 3
else if (archive.ArchiveHeader.FormatVersion == 1 || archive.ArchiveHeader.FormatVersion == 2)
{
// If we have a block table
long blockTableOffset = ((uint)archive.ArchiveHeader.BlockTablePositionHi << 23) | archive.ArchiveHeader.BlockTablePosition;
if (blockTableOffset != 0)
{
// Find the ending offset based on size
long blockTableEnd = blockTableOffset + archive.ArchiveHeader.BlockTableSize;
// Read in the block table
var blockTable = new List<BlockEntry>();
while (blockTableOffset < blockTableEnd)
{
var blockEntry = ParseBlockEntry(data, ref blockTableOffset);
if (blockEntry == null)
return null;
blockTable.Add(blockEntry);
}
archive.BlockTable = blockTable.ToArray();
}
}
// Version 4
else if (archive.ArchiveHeader.FormatVersion == 3)
{
// If we have a block table
long blockTableOffset = ((uint)archive.ArchiveHeader.BlockTablePositionHi << 23) | archive.ArchiveHeader.BlockTablePosition;
if (blockTableOffset != 0)
{
// Find the ending offset based on size
long blockTableEnd = blockTableOffset + (long)archive.ArchiveHeader.BlockTableSizeLong;
// Read in the block table
var blockTable = new List<BlockEntry>();
while (blockTableOffset < blockTableEnd)
{
var blockEntry = ParseBlockEntry(data, ref blockTableOffset);
if (blockEntry == null)
return null;
blockTable.Add(blockEntry);
}
archive.BlockTable = blockTable.ToArray();
}
}
#endregion
#region Hi-Block Table
// Version 2, 3, and 4
if (archive.ArchiveHeader.FormatVersion == 1
|| archive.ArchiveHeader.FormatVersion == 2
|| archive.ArchiveHeader.FormatVersion == 3)
{
// If we have a hi-block table
int hiBlockTableOffset = (int)archive.ArchiveHeader.HiBlockTablePosition;
if (hiBlockTableOffset != 0)
{
// Read in the hi-block table
var hiBlockTable = new List<short>();
for (int i = 0; i < archive.BlockTable.Length; i++)
{
short hiBlockEntry = data.ReadInt16(ref hiBlockTableOffset);
hiBlockTable.Add(hiBlockEntry);
}
archive.HiBlockTable = hiBlockTable.ToArray();
}
}
#endregion
#region BET Table
// Version 3 and 4
if (archive.ArchiveHeader.FormatVersion == 2 || archive.ArchiveHeader.FormatVersion == 3)
{
// If we have a BET table
int betTableOffset = (int)archive.ArchiveHeader.BetTablePosition;
if (betTableOffset != 0)
{
// Read in the BET table
var betTable = ParseBetTable(data, ref betTableOffset);
if (betTable == null)
return null;
archive.BetTable = betTable;
}
}
#endregion
#region HET Table
// Version 3 and 4
if (archive.ArchiveHeader.FormatVersion == 2 || archive.ArchiveHeader.FormatVersion == 3)
{
// If we have a HET table
int hetTableOffset = (int)archive.ArchiveHeader.HetTablePosition;
if (hetTableOffset != 0)
{
// Read in the HET table
var hetTable = ParseHetTable(data, ref hetTableOffset);
if (hetTable == null)
return null;
archive.HetTable = hetTable;
}
}
#endregion
return archive;
}
/// <summary>
/// Parse a byte array into a archive header
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled archive header on success, null on error</returns>
private static ArchiveHeader ParseArchiveHeader(byte[] data, ref int offset)
{
// TODO: Use marshalling here instead of building
ArchiveHeader archiveHeader = new ArchiveHeader();
// V1 - Common
archiveHeader.Signature = data.ReadUInt32(ref offset);
if (archiveHeader.Signature != ArchiveHeaderSignatureValue)
return null;
archiveHeader.HeaderSize = data.ReadUInt32(ref offset);
archiveHeader.ArchiveSize = data.ReadUInt32(ref offset);
archiveHeader.FormatVersion = data.ReadUInt16(ref offset);
archiveHeader.BlockSize = data.ReadUInt16(ref offset);
archiveHeader.HashTablePosition = data.ReadUInt32(ref offset);
archiveHeader.BlockTablePosition = data.ReadUInt32(ref offset);
archiveHeader.HashTableSize = data.ReadUInt32(ref offset);
archiveHeader.BlockTableSize = data.ReadUInt32(ref offset);
// V2
if (archiveHeader.FormatVersion >= 2 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion2Size)
{
archiveHeader.HiBlockTablePosition = data.ReadUInt64(ref offset);
archiveHeader.HashTablePositionHi = data.ReadUInt16(ref offset);
archiveHeader.BlockTablePositionHi = data.ReadUInt16(ref offset);
}
// V3
if (archiveHeader.FormatVersion >= 3 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion3Size)
{
archiveHeader.ArchiveSizeLong = data.ReadUInt64(ref offset);
archiveHeader.BetTablePosition = data.ReadUInt64(ref offset);
archiveHeader.HetTablePosition = data.ReadUInt64(ref offset);
}
// V4
if (archiveHeader.FormatVersion >= 4 && archiveHeader.HeaderSize >= ArchiveHeaderHeaderVersion4Size)
{
archiveHeader.HashTableSizeLong = data.ReadUInt64(ref offset);
archiveHeader.BlockTableSizeLong = data.ReadUInt64(ref offset);
archiveHeader.HiBlockTableSize = data.ReadUInt64(ref offset);
archiveHeader.HetTableSize = data.ReadUInt64(ref offset);
archiveHeader.BetTablesize = data.ReadUInt64(ref offset);
archiveHeader.RawChunkSize = data.ReadUInt32(ref offset);
archiveHeader.BlockTableMD5 = data.ReadBytes(ref offset, 0x10);
archiveHeader.HashTableMD5 = data.ReadBytes(ref offset, 0x10);
archiveHeader.HiBlockTableMD5 = data.ReadBytes(ref offset, 0x10);
archiveHeader.BetTableMD5 = data.ReadBytes(ref offset, 0x10);
archiveHeader.HetTableMD5 = data.ReadBytes(ref offset, 0x10);
archiveHeader.HetTableMD5 = data.ReadBytes(ref offset, 0x10);
}
return archiveHeader;
}
/// <summary>
/// Parse a byte array into a user data object
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled user data on success, null on error</returns>
private static UserData ParseUserData(byte[] data, ref int offset)
{
// TODO: Use marshalling here instead of building
UserData userData = new UserData();
userData.Signature = data.ReadUInt32(ref offset);
if (userData.Signature != UserDataSignatureValue)
return null;
userData.UserDataSize = data.ReadUInt32(ref offset);
userData.HeaderOffset = data.ReadUInt32(ref offset);
userData.UserDataHeaderSize = data.ReadUInt32(ref offset);
return userData;
}
/// <summary>
/// Parse a byte array into a HET table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled HET table on success, null on error</returns>
private static HetTable ParseHetTable(byte[] data, ref int offset)
{
// TODO: Use marshalling here instead of building
HetTable hetTable = new HetTable();
// Common Headers
hetTable.Signature = data.ReadUInt32(ref offset);
if (hetTable.Signature != HetTableSignatureValue)
return null;
hetTable.Version = data.ReadUInt32(ref offset);
hetTable.DataSize = data.ReadUInt32(ref offset);
// HET-Specific
hetTable.TableSize = data.ReadUInt32(ref offset);
hetTable.MaxFileCount = data.ReadUInt32(ref offset);
hetTable.HashTableSize = data.ReadUInt32(ref offset);
hetTable.TotalIndexSize = data.ReadUInt32(ref offset);
hetTable.IndexSizeExtra = data.ReadUInt32(ref offset);
hetTable.IndexSize = data.ReadUInt32(ref offset);
hetTable.BlockTableSize = data.ReadUInt32(ref offset);
hetTable.HashTable = data.ReadBytes(ref offset, (int)hetTable.HashTableSize);
// TODO: Populate the file indexes array
hetTable.FileIndexes = new byte[(int)hetTable.HashTableSize][];
return hetTable;
}
/// <summary>
/// Parse a byte array into a BET table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled BET table on success, null on error</returns>
private static BetTable ParseBetTable(byte[] data, ref int offset)
{
// TODO: Use marshalling here instead of building
BetTable betTable = new BetTable();
// Common Headers
betTable.Signature = data.ReadUInt32(ref offset);
if (betTable.Signature != BetTableSignatureValue)
return null;
betTable.Version = data.ReadUInt32(ref offset);
betTable.DataSize = data.ReadUInt32(ref offset);
// BET-Specific
betTable.TableSize = data.ReadUInt32(ref offset);
betTable.FileCount = data.ReadUInt32(ref offset);
betTable.Unknown = data.ReadUInt32(ref offset);
betTable.TableEntrySize = data.ReadUInt32(ref offset);
betTable.FilePositionBitIndex = data.ReadUInt32(ref offset);
betTable.FileSizeBitIndex = data.ReadUInt32(ref offset);
betTable.CompressedSizeBitIndex = data.ReadUInt32(ref offset);
betTable.FlagIndexBitIndex = data.ReadUInt32(ref offset);
betTable.UnknownBitIndex = data.ReadUInt32(ref offset);
betTable.FilePositionBitCount = data.ReadUInt32(ref offset);
betTable.FileSizeBitCount = data.ReadUInt32(ref offset);
betTable.CompressedSizeBitCount = data.ReadUInt32(ref offset);
betTable.FlagIndexBitCount = data.ReadUInt32(ref offset);
betTable.UnknownBitCount = data.ReadUInt32(ref offset);
betTable.TotalBetHashSize = data.ReadUInt32(ref offset);
betTable.BetHashSizeExtra = data.ReadUInt32(ref offset);
betTable.BetHashSize = data.ReadUInt32(ref offset);
betTable.BetHashArraySize = data.ReadUInt32(ref offset);
betTable.FlagCount = data.ReadUInt32(ref offset);
betTable.FlagsArray = new uint[betTable.FlagCount];
Buffer.BlockCopy(data, offset, betTable.FlagsArray, 0, (int)betTable.FlagCount * 4);
offset += (int)betTable.FlagCount * 4;
// TODO: Populate the file table
// TODO: Populate the hash table
return betTable;
}
/// <summary>
/// Parse a byte array into a hash entry
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled hash entry on success, null on error</returns>
private static HashEntry ParseHashEntry(byte[] data, ref long offset)
{
// TODO: Use marshalling here instead of building
HashEntry hashEntry = new HashEntry();
int intOffset = (int)offset;
hashEntry.NameHashPartA = data.ReadUInt32(ref intOffset);
hashEntry.NameHashPartB = data.ReadUInt32(ref intOffset);
hashEntry.Locale = (Locale)data.ReadUInt16(ref intOffset);
hashEntry.Platform = data.ReadUInt16(ref intOffset);
hashEntry.BlockIndex = data.ReadUInt32(ref intOffset);
offset = intOffset;
return hashEntry;
}
/// <summary>
/// Parse a byte array into a block entry
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled block entry on success, null on error</returns>
private static BlockEntry ParseBlockEntry(byte[] data, ref long offset)
{
// TODO: Use marshalling here instead of building
BlockEntry blockEntry = new BlockEntry();
int intOffset = (int)offset;
blockEntry.FilePosition = data.ReadUInt32(ref intOffset);
blockEntry.CompressedSize = data.ReadUInt32(ref intOffset);
blockEntry.UncompressedSize = data.ReadUInt32(ref intOffset);
blockEntry.Flags = (FileFlags)data.ReadUInt32(ref intOffset);
offset = intOffset;
return blockEntry;
}
/// <summary>
/// Parse a byte array into a patch info
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled patch info on success, null on error</returns>
private static PatchInfo ParsePatchInfo(byte[] data, ref int offset)
{
// TODO: Use marshalling here instead of building
PatchInfo patchInfo = new PatchInfo();
patchInfo.Length = data.ReadUInt32(ref offset);
patchInfo.Flags = data.ReadUInt32(ref offset);
patchInfo.DataSize = data.ReadUInt32(ref offset);
patchInfo.MD5 = data.ReadBytes(ref offset, 0x10);
// TODO: Fill the sector offset table
return patchInfo;
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data);
dataStream.Position = offset;
return ParseArchive(dataStream);
}
#endregion

View File

@@ -6,7 +6,6 @@ using BurnOutSharp.Utilities;
namespace BurnOutSharp.Builder
{
// TODO: Make Stream Data rely on Byte Data
public static class NewExecutable
{
#region Byte Data
@@ -27,458 +26,10 @@ namespace BurnOutSharp.Builder
if (offset < 0 || offset >= data.Length)
return null;
// Cache the current offset
int initialOffset = offset;
// Create a new executable to fill
var executable = new Executable();
#region MS-DOS Stub
// Parse the MS-DOS stub
var stub = MSDOS.ParseExecutable(data, offset);
if (stub?.Header == null || stub.Header.NewExeHeaderAddr == 0)
return null;
// Set the MS-DOS stub
executable.Stub = stub;
#endregion
#region Executable Header
// Try to parse the executable header
offset = (int)(initialOffset + stub.Header.NewExeHeaderAddr);
var executableHeader = ParseExecutableHeader(data, offset);
if (executableHeader == null)
return null;
// Set the executable header
executable.Header = executableHeader;
#endregion
#region Segment Table
// If the offset for the segment table doesn't exist
int tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.SegmentTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the segment table
var segmentTable = ParseSegmentTable(data, tableAddress, executableHeader.FileSegmentCount);
if (segmentTable == null)
return null;
// Set the segment table
executable.SegmentTable = segmentTable;
#endregion
#region Resource Table
// If the offset for the segment table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.SegmentTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the resource table
var resourceTable = ParseResourceTable(data, tableAddress, executableHeader.ResourceEntriesCount);
if (resourceTable == null)
return null;
// Set the resource table
executable.ResourceTable = resourceTable;
#endregion
#region Resident-Name Table
// If the offset for the resident-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ResidentNameTableOffset;
int endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ModuleReferenceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the resident-name table
var residentNameTable = ParseResidentNameTable(data, tableAddress, endOffset);
if (residentNameTable == null)
return null;
// Set the resident-name table
executable.ResidentNameTable = residentNameTable;
#endregion
#region Module-Reference Table
// If the offset for the module-reference table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ModuleReferenceTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the module-reference table
var moduleReferenceTable = ParseModuleReferenceTable(data, tableAddress, executableHeader.ModuleReferenceTableSize);
if (moduleReferenceTable == null)
return null;
// Set the module-reference table
executable.ModuleReferenceTable = moduleReferenceTable;
#endregion
#region Imported-Name Table
// If the offset for the imported-name table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.ImportedNamesTableOffset;
endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.EntryTableOffset;
if (tableAddress >= data.Length)
return executable;
// Try to parse the imported-name table
var importedNameTable = ParseImportedNameTable(data, tableAddress, endOffset);
if (importedNameTable == null)
return null;
// Set the imported-name table
executable.ImportedNameTable = importedNameTable;
#endregion
#region Entry Table
// If the offset for the entry table doesn't exist
tableAddress = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.EntryTableOffset;
endOffset = initialOffset
+ (int)stub.Header.NewExeHeaderAddr
+ executableHeader.EntryTableOffset
+ executableHeader.EntryTableSize;
if (tableAddress >= data.Length)
return executable;
// Try to parse the entry table
var entryTable = ParseEntryTable(data, tableAddress, endOffset);
if (entryTable == null)
return null;
// Set the entry table
executable.EntryTable = entryTable;
#endregion
#region Nonresident-Name Table
// If the offset for the nonresident-name table doesn't exist
tableAddress = initialOffset
+ (int)executableHeader.NonResidentNamesTableOffset;
endOffset = initialOffset
+ (int)executableHeader.NonResidentNamesTableOffset
+ executableHeader.NonResidentNameTableSize;
if (tableAddress >= data.Length)
return executable;
// Try to parse the nonresident-name table
var nonResidentNameTable = ParseNonResidentNameTable(data, tableAddress, endOffset);
if (nonResidentNameTable == null)
return null;
// Set the nonresident-name table
executable.NonResidentNameTable = nonResidentNameTable;
#endregion
return executable;
}
/// <summary>
/// Parse a byte array into a New Executable header
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <returns>Filled executable header on success, null on error</returns>
private static ExecutableHeader ParseExecutableHeader(byte[] data, int offset)
{
// TODO: Use marshalling here instead of building
var header = new ExecutableHeader();
header.Magic = new byte[2];
for (int i = 0; i < header.Magic.Length; i++)
{
header.Magic[i] = data.ReadByte(ref offset);
}
if (header.Magic[0] != 'N' || header.Magic[1] != 'E')
return null;
header.LinkerVersion = data.ReadByte(ref offset);
header.LinkerRevision = data.ReadByte(ref offset);
header.EntryTableOffset = data.ReadUInt16(ref offset);
header.EntryTableSize = data.ReadUInt16(ref offset);
header.CrcChecksum = data.ReadUInt32(ref offset);
header.FlagWord = (HeaderFlag)data.ReadUInt16(ref offset);
header.AutomaticDataSegmentNumber = data.ReadUInt16(ref offset);
header.InitialHeapAlloc = data.ReadUInt16(ref offset);
header.InitialStackAlloc = data.ReadUInt16(ref offset);
header.InitialCSIPSetting = data.ReadUInt32(ref offset);
header.InitialSSSPSetting = data.ReadUInt32(ref offset);
header.FileSegmentCount = data.ReadUInt16(ref offset);
header.ModuleReferenceTableSize = data.ReadUInt16(ref offset);
header.NonResidentNameTableSize = data.ReadUInt16(ref offset);
header.SegmentTableOffset = data.ReadUInt16(ref offset);
header.ResourceTableOffset = data.ReadUInt16(ref offset);
header.ResidentNameTableOffset = data.ReadUInt16(ref offset);
header.ModuleReferenceTableOffset = data.ReadUInt16(ref offset);
header.ImportedNamesTableOffset = data.ReadUInt16(ref offset);
header.NonResidentNamesTableOffset = data.ReadUInt32(ref offset);
header.MovableEntriesCount = data.ReadUInt16(ref offset);
header.SegmentAlignmentShiftCount = data.ReadUInt16(ref offset);
header.ResourceEntriesCount = data.ReadUInt16(ref offset);
header.TargetOperatingSystem = (OperatingSystem)data.ReadByte(ref offset);
header.AdditionalFlags = (OS2Flag)data.ReadByte(ref offset);
header.ReturnThunkOffset = data.ReadUInt16(ref offset);
header.SegmentReferenceThunkOffset = data.ReadUInt16(ref offset);
header.MinCodeSwapAreaSize = data.ReadUInt16(ref offset);
header.WindowsSDKRevision = data.ReadByte(ref offset);
header.WindowsSDKVersion = data.ReadByte(ref offset);
return header;
}
/// <summary>
/// Parse a byte array into a segment table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="count">Number of segment table entries to read</param>
/// <returns>Filled segment table on success, null on error</returns>
private static SegmentTableEntry[] ParseSegmentTable(byte[] data, int offset, int count)
{
// TODO: Use marshalling here instead of building
var segmentTable = new SegmentTableEntry[count];
for (int i = 0; i < count; i++)
{
var entry = new SegmentTableEntry();
entry.Offset = data.ReadUInt16(ref offset);
entry.Length = data.ReadUInt16(ref offset);
entry.FlagWord = (SegmentTableEntryFlag)data.ReadUInt16(ref offset);
entry.MinimumAllocationSize = data.ReadUInt16(ref offset);
segmentTable[i] = entry;
}
return segmentTable;
}
/// <summary>
/// Parse a byte array into a resource table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="count">Number of resource table entries to read</param>
/// <returns>Filled resource table on success, null on error</returns>
private static ResourceTable ParseResourceTable(byte[] data, int offset, int count)
{
int initialOffset = offset;
// TODO: Use marshalling here instead of building
var resourceTable = new ResourceTable();
resourceTable.AlignmentShiftCount = data.ReadUInt16(ref offset);
resourceTable.ResourceTypes = new ResourceTypeInformationEntry[count];
for (int i = 0; i < resourceTable.ResourceTypes.Length; i++)
{
var entry = new ResourceTypeInformationEntry();
entry.TypeID = data.ReadUInt16(ref offset);
entry.ResourceCount = data.ReadUInt16(ref offset);
entry.Reserved = data.ReadUInt32(ref offset);
entry.Resources = new ResourceTypeResourceEntry[entry.ResourceCount];
for (int j = 0; j < entry.ResourceCount; j++)
{
// TODO: Should we read and store the resource data?
var resource = new ResourceTypeResourceEntry();
resource.Offset = data.ReadUInt16(ref offset);
resource.Length = data.ReadUInt16(ref offset);
resource.FlagWord = (ResourceTypeResourceFlag)data.ReadUInt16(ref offset);
resource.ResourceID = data.ReadUInt16(ref offset);
resource.Reserved = data.ReadUInt32(ref offset);
entry.Resources[j] = resource;
}
resourceTable.ResourceTypes[i] = entry;
}
// Get the full list of unique string offsets
var stringOffsets = resourceTable.ResourceTypes
.Where(rt => rt.IsIntegerType() == false)
.Select(rt => rt.TypeID)
.Union(resourceTable.ResourceTypes
.SelectMany(rt => rt.Resources)
.Where(r => r.IsIntegerType() == false)
.Select(r => r.ResourceID))
.Distinct()
.OrderBy(o => o)
.ToList();
// Populate the type and name string dictionary
resourceTable.TypeAndNameStrings = new Dictionary<ushort, ResourceTypeAndNameString>();
for (int i = 0; i < stringOffsets.Count; i++)
{
int stringOffset = stringOffsets[i] + initialOffset;
var str = new ResourceTypeAndNameString();
str.Length = data.ReadByte(ref stringOffset);
str.Text = data.ReadBytes(ref stringOffset, str.Length);
resourceTable.TypeAndNameStrings[stringOffsets[i]] = str;
}
return resourceTable;
}
/// <summary>
/// Parse a byte array into a resident-name table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="endOffset">First address not part of the resident-name table</param>
/// <returns>Filled resident-name table on success, null on error</returns>
private static ResidentNameTableEntry[] ParseResidentNameTable(byte[] data, int offset, int endOffset)
{
// TODO: Use marshalling here instead of building
var residentNameTable = new List<ResidentNameTableEntry>();
while (offset < endOffset)
{
var entry = new ResidentNameTableEntry();
entry.Length = data.ReadByte(ref offset);
entry.NameString = data.ReadBytes(ref offset, entry.Length);
entry.OrdinalNumber = data.ReadUInt16(ref offset);
residentNameTable.Add(entry);
}
return residentNameTable.ToArray();
}
/// <summary>
/// Parse a byte array into a module-reference table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="count">Number of module-reference table entries to read</param>
/// <returns>Filled module-reference table on success, null on error</returns>
private static ModuleReferenceTableEntry[] ParseModuleReferenceTable(byte[] data, int offset, int count)
{
// TODO: Use marshalling here instead of building
var moduleReferenceTable = new ModuleReferenceTableEntry[count];
for (int i = 0; i < count; i++)
{
var entry = new ModuleReferenceTableEntry();
entry.Offset = data.ReadUInt16(ref offset);
moduleReferenceTable[i] = entry;
}
return moduleReferenceTable;
}
/// <summary>
/// Parse a byte array into an imported-name table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="endOffset">First address not part of the imported-name table</param>
/// <returns>Filled imported-name table on success, null on error</returns>
private static Dictionary<ushort, ImportedNameTableEntry> ParseImportedNameTable(byte[] data, int offset, int endOffset)
{
// TODO: Use marshalling here instead of building
var importedNameTable = new Dictionary<ushort, ImportedNameTableEntry>();
while (offset < endOffset)
{
ushort currentOffset = (ushort)offset;
var entry = new ImportedNameTableEntry();
entry.Length = data.ReadByte(ref offset);
entry.NameString = data.ReadBytes(ref offset, entry.Length);
importedNameTable[currentOffset] = entry;
}
return importedNameTable;
}
/// <summary>
/// Parse a byte array into an entry table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="endOffset">First address not part of the entry table</param>
/// <returns>Filled entry table on success, null on error</returns>
private static EntryTableBundle[] ParseEntryTable(byte[] data, int offset, int endOffset)
{
// TODO: Use marshalling here instead of building
var entryTable = new List<EntryTableBundle>();
while (offset < endOffset)
{
var entry = new EntryTableBundle();
entry.EntryCount = data.ReadByte(ref offset);
entry.SegmentIndicator = data.ReadByte(ref offset);
switch (entry.GetEntryType())
{
case SegmentEntryType.Unused:
break;
case SegmentEntryType.FixedSegment:
entry.FixedFlagWord = (FixedSegmentEntryFlag)data.ReadByte(ref offset);
entry.FixedOffset = data.ReadUInt16(ref offset);
break;
case SegmentEntryType.MoveableSegment:
entry.MoveableFlagWord = (MoveableSegmentEntryFlag)data.ReadByte(ref offset);
entry.MoveableReserved = data.ReadUInt16(ref offset);
entry.MoveableSegmentNumber = data.ReadByte(ref offset);
entry.MoveableOffset = data.ReadUInt16(ref offset);
break;
}
entryTable.Add(entry);
}
return entryTable.ToArray();
}
/// <summary>
/// Parse a byte array into a nonresident-name table
/// </summary>
/// <param name="data">Byte array to parse</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="endOffset">First address not part of the nonresident-name table</param>
/// <returns>Filled nonresident-name table on success, null on error</returns>
private static NonResidentNameTableEntry[] ParseNonResidentNameTable(byte[] data, int offset, int endOffset)
{
// TODO: Use marshalling here instead of building
var residentNameTable = new List<NonResidentNameTableEntry>();
while (offset < endOffset)
{
var entry = new NonResidentNameTableEntry();
entry.Length = data.ReadByte(ref offset);
entry.NameString = data.ReadBytes(ref offset, entry.Length);
entry.OrdinalNumber = data.ReadUInt16(ref offset);
residentNameTable.Add(entry);
}
return residentNameTable.ToArray();
// Create a memory stream and parse that
MemoryStream dataStream = new MemoryStream(data);
dataStream.Position = offset;
return ParseExecutable(dataStream);
}
#endregion

File diff suppressed because it is too large Load Diff