mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-02-16 21:37:05 +00:00
Use MemoryStream in builders
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user