mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-07-08 18:06:41 +00:00
Update packages and upstream changes
This commit is contained in:
@@ -66,7 +66,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.IO" Version="1.7.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.7.1" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.8" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.IO" Version="1.7.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.7.1" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.6.1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.7.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -71,25 +71,25 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (obj.ID1 != ID1 || obj.ID2 != ID2)
|
||||
return null;
|
||||
|
||||
obj.CM = (CompressionMethod)data.ReadByteValue();
|
||||
obj.FLG = (Flags)data.ReadByteValue();
|
||||
obj.MTIME = data.ReadUInt32LittleEndian();
|
||||
obj.XFL = (ExtraFlags)data.ReadByteValue();
|
||||
obj.OS = (OperatingSystem)data.ReadByteValue();
|
||||
obj.CompressionMethod = (CompressionMethod)data.ReadByteValue();
|
||||
obj.Flags = (Flags)data.ReadByteValue();
|
||||
obj.LastModifiedTime = data.ReadUInt32LittleEndian();
|
||||
obj.ExtraFlags = (ExtraFlags)data.ReadByteValue();
|
||||
obj.OperatingSystem = (OperatingSystem)data.ReadByteValue();
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((obj.FLG & Flags.FEXTRA) != 0)
|
||||
if ((obj.Flags & Flags.FEXTRA) != 0)
|
||||
#else
|
||||
if (obj.FLG.HasFlag(Flags.FEXTRA))
|
||||
if (obj.Flags.HasFlag(Flags.FEXTRA))
|
||||
#endif
|
||||
{
|
||||
obj.XLEN = data.ReadUInt16LittleEndian();
|
||||
obj.ExtraLength = data.ReadUInt16LittleEndian();
|
||||
|
||||
// Cache the current position
|
||||
long currentPosition = data.Position;
|
||||
|
||||
List<ExtraFieldData> extraFields = [];
|
||||
while (data.Position < currentPosition + obj.XLEN)
|
||||
while (data.Position < currentPosition + obj.ExtraLength)
|
||||
{
|
||||
var extraField = ParseExtraFieldData(data);
|
||||
if (extraField == null)
|
||||
@@ -102,24 +102,24 @@ namespace SabreTools.Serialization.Deserializers
|
||||
}
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((obj.FLG & Flags.FNAME) != 0)
|
||||
if ((obj.Flags & Flags.FNAME) != 0)
|
||||
#else
|
||||
if (obj.FLG.HasFlag(Flags.FNAME))
|
||||
if (obj.Flags.HasFlag(Flags.FNAME))
|
||||
#endif
|
||||
obj.OriginalFileName = data.ReadNullTerminatedAnsiString();
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((obj.FLG & Flags.FCOMMENT) != 0)
|
||||
if ((obj.Flags & Flags.FCOMMENT) != 0)
|
||||
#else
|
||||
if (obj.FLG.HasFlag(Flags.FCOMMENT))
|
||||
if (obj.Flags.HasFlag(Flags.FCOMMENT))
|
||||
#endif
|
||||
obj.FileComment = data.ReadNullTerminatedAnsiString();
|
||||
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((obj.FLG & Flags.FHCRC) != 0)
|
||||
if ((obj.Flags & Flags.FHCRC) != 0)
|
||||
#else
|
||||
if (obj.FLG.HasFlag(Flags.FHCRC))
|
||||
if (obj.Flags.HasFlag(Flags.FHCRC))
|
||||
#endif
|
||||
obj.CRC16 = data.ReadUInt16LittleEndian();
|
||||
|
||||
@@ -135,10 +135,10 @@ namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
var obj = new ExtraFieldData();
|
||||
|
||||
obj.SI1 = data.ReadByteValue();
|
||||
obj.SI2 = data.ReadByteValue();
|
||||
obj.LEN = data.ReadUInt16LittleEndian();
|
||||
obj.Data = data.ReadBytes(obj.LEN);
|
||||
obj.SubfieldID1 = data.ReadByteValue();
|
||||
obj.SubfieldID2 = data.ReadByteValue();
|
||||
obj.Length = data.ReadUInt16LittleEndian();
|
||||
obj.Data = data.ReadBytes(obj.Length);
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -153,7 +153,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var obj = new Trailer();
|
||||
|
||||
obj.CRC32 = data.ReadUInt32LittleEndian();
|
||||
obj.ISIZE = data.ReadUInt32LittleEndian();
|
||||
obj.InputSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
obj.BlockTableSizeLong = data.ReadUInt64LittleEndian();
|
||||
obj.HiBlockTableSize = data.ReadUInt64LittleEndian();
|
||||
obj.HetTableSize = data.ReadUInt64LittleEndian();
|
||||
obj.BetTablesize = data.ReadUInt64LittleEndian();
|
||||
obj.BetTableSize = data.ReadUInt64LittleEndian();
|
||||
obj.RawChunkSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
obj.BlockTableMD5 = data.ReadBytes(0x10);
|
||||
@@ -194,7 +194,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
byte[]? tableBytes = decrypter.LoadTable(data,
|
||||
offset,
|
||||
header.BetTableMD5,
|
||||
(uint)header.BetTablesize,
|
||||
(uint)header.BetTableSize,
|
||||
92,
|
||||
MPQ_KEY_BLOCK_TABLE,
|
||||
out _);
|
||||
|
||||
@@ -28,11 +28,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var archive = new Archive();
|
||||
|
||||
// Setup all of the collections
|
||||
var localFileHeaders = new List<LocalFileHeader>();
|
||||
var encryptionHeaders = new List<byte[]>();
|
||||
var fileData = new List<byte[]>();
|
||||
var dataDescriptors = new List<DataDescriptor>();
|
||||
var zip64DataDescriptors = new List<DataDescriptor64>();
|
||||
var localFiles = new List<LocalFile>();
|
||||
var cdrs = new List<CentralDirectoryFileHeader>();
|
||||
|
||||
// Read all blocks
|
||||
@@ -60,23 +56,13 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Local File
|
||||
case LocalFileHeaderSignature:
|
||||
if (!ParseLocalFile(data,
|
||||
out LocalFileHeader? localFileHeader,
|
||||
out byte[]? encryptionHeader,
|
||||
out byte[]? fileDatum,
|
||||
out DataDescriptor? dataDescriptor,
|
||||
out DataDescriptor64? dataDescriptor64))
|
||||
{
|
||||
break;
|
||||
}
|
||||
var lf = ParseLocalFile(data);
|
||||
if (lf == null)
|
||||
return null;
|
||||
|
||||
// Add the local file
|
||||
validBlock = true;
|
||||
localFileHeaders.Add(localFileHeader!);
|
||||
encryptionHeaders.Add(encryptionHeader!);
|
||||
fileData.Add(fileDatum!);
|
||||
dataDescriptors.Add(dataDescriptor!);
|
||||
zip64DataDescriptors.Add(dataDescriptor64!);
|
||||
localFiles.Add(lf);
|
||||
break;
|
||||
|
||||
// TODO: Implement
|
||||
@@ -135,22 +121,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
} while (data.Position < data.Length);
|
||||
|
||||
// If no blocks were read
|
||||
if (localFileHeaders.Count == 0
|
||||
&& encryptionHeaders.Count == 0
|
||||
&& fileData.Count == 0
|
||||
&& dataDescriptors.Count == 0
|
||||
&& zip64DataDescriptors.Count == 0
|
||||
&& cdrs.Count == 0)
|
||||
{
|
||||
if (localFiles.Count == 0 && cdrs.Count == 0)
|
||||
return null;
|
||||
}
|
||||
|
||||
// Assign the local files
|
||||
archive.LocalFileHeaders = [.. localFileHeaders];
|
||||
archive.EncryptionHeaders = [.. encryptionHeaders];
|
||||
archive.FileData = [.. fileData];
|
||||
archive.DataDescriptors = [.. dataDescriptors];
|
||||
archive.ZIP64DataDescriptors = [.. zip64DataDescriptors];
|
||||
archive.LocalFiles = [.. localFiles];
|
||||
|
||||
// Assign the central directory records
|
||||
archive.CentralDirectoryHeaders = [.. cdrs];
|
||||
@@ -245,10 +220,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (extraBytes.Length != obj.ExtraFieldLength)
|
||||
return null;
|
||||
|
||||
obj.ExtraField = extraBytes;
|
||||
|
||||
// TODO: This should live on the model instead of the byte representation
|
||||
extraFields = ParseExtraFields(obj, obj.ExtraField);
|
||||
extraFields = ParseExtraFields(obj, extraBytes);
|
||||
}
|
||||
if (obj.FileCommentLength > 0 && data.Position + obj.FileCommentLength <= data.Length)
|
||||
{
|
||||
@@ -436,25 +409,19 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled local file on success, null on error</returns>
|
||||
public static bool ParseLocalFile(Stream data,
|
||||
out LocalFileHeader? localFileHeader,
|
||||
out byte[]? encryptionHeader,
|
||||
out byte[]? fileData,
|
||||
out DataDescriptor? dataDescriptor,
|
||||
out DataDescriptor64? dataDescriptor64)
|
||||
public static LocalFile? ParseLocalFile(Stream data)
|
||||
{
|
||||
// Set default values
|
||||
localFileHeader = null;
|
||||
encryptionHeader = null;
|
||||
fileData = null;
|
||||
dataDescriptor = null;
|
||||
dataDescriptor64 = null;
|
||||
var obj = new LocalFile();
|
||||
|
||||
#region Local File Header
|
||||
|
||||
localFileHeader = ParseLocalFileHeader(data, out var extraFields);
|
||||
// Try to read the header
|
||||
var localFileHeader = ParseLocalFileHeader(data, out var extraFields);
|
||||
if (localFileHeader == null)
|
||||
return false;
|
||||
return null;
|
||||
|
||||
// Assign the header
|
||||
obj.LocalFileHeader = localFileHeader;
|
||||
|
||||
ulong compressedSize = localFileHeader.CompressedSize;
|
||||
if (extraFields != null)
|
||||
@@ -482,14 +449,17 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#endif
|
||||
{
|
||||
// Try to read the encryption header data -- TODO: Verify amount to read
|
||||
encryptionHeader = data.ReadBytes(12);
|
||||
if (encryptionHeader.Length != 12)
|
||||
return false;
|
||||
byte[] encryptionHeaders = data.ReadBytes(12);
|
||||
if (encryptionHeaders.Length != 12)
|
||||
return null;
|
||||
|
||||
// Set the encryption headers
|
||||
obj.EncryptionHeaders = encryptionHeaders;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the empty encryption header
|
||||
encryptionHeader = [];
|
||||
obj.EncryptionHeaders = [];
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -497,9 +467,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
#region File Data
|
||||
|
||||
// Try to read the file data
|
||||
fileData = data.ReadBytes((int)compressedSize);
|
||||
var fileData = data.ReadBytes((int)compressedSize);
|
||||
if (fileData.Length < (long)compressedSize)
|
||||
return false;
|
||||
return null;
|
||||
|
||||
// Set the file data
|
||||
obj.FileData = fileData;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -512,9 +485,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (!localFileHeader.Flags.HasFlag(GeneralPurposeBitFlags.NoCRC))
|
||||
#endif
|
||||
{
|
||||
dataDescriptor = new DataDescriptor();
|
||||
dataDescriptor64 = new DataDescriptor64();
|
||||
return true;
|
||||
obj.DataDescriptor = new DataDescriptor();
|
||||
obj.ZIP64DataDescriptor = new DataDescriptor64();
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Read the signature
|
||||
@@ -525,9 +498,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Don't fail if descriptor is missing
|
||||
if (signature != DataDescriptorSignature)
|
||||
{
|
||||
dataDescriptor = new DataDescriptor();
|
||||
dataDescriptor64 = new DataDescriptor64();
|
||||
return true;
|
||||
obj.DataDescriptor = new DataDescriptor();
|
||||
obj.ZIP64DataDescriptor = new DataDescriptor64();
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Determine if the entry is ZIP64
|
||||
@@ -536,22 +509,22 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Try to parse the correct data descriptor
|
||||
if (zip64)
|
||||
{
|
||||
dataDescriptor = new DataDescriptor();
|
||||
dataDescriptor64 = ParseDataDescriptor64(data);
|
||||
if (dataDescriptor64 == null)
|
||||
return false;
|
||||
obj.DataDescriptor = new DataDescriptor();
|
||||
obj.ZIP64DataDescriptor = ParseDataDescriptor64(data);
|
||||
if (obj.ZIP64DataDescriptor == null)
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataDescriptor = ParseDataDescriptor(data);
|
||||
dataDescriptor64 = new DataDescriptor64();
|
||||
if (dataDescriptor == null)
|
||||
return false;
|
||||
obj.DataDescriptor = ParseDataDescriptor(data);
|
||||
obj.ZIP64DataDescriptor = new DataDescriptor64();
|
||||
if (obj.DataDescriptor == null)
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return true;
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -602,10 +575,8 @@ namespace SabreTools.Serialization.Deserializers
|
||||
if (extraBytes.Length != obj.ExtraFieldLength)
|
||||
return null;
|
||||
|
||||
obj.ExtraField = extraBytes;
|
||||
|
||||
// TODO: This should live on the model instead of the byte representation
|
||||
extraFields = ParseExtraFields(obj, obj.ExtraField);
|
||||
extraFields = ParseExtraFields(obj, extraBytes);
|
||||
}
|
||||
|
||||
return obj;
|
||||
@@ -633,7 +604,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
offset -= 2;
|
||||
|
||||
// Read based on the ID
|
||||
var field = id switch
|
||||
ExtensibleDataField? field = id switch
|
||||
{
|
||||
HeaderID.Zip64ExtendedInformation => ParseZip64ExtendedInformationExtraField(data, ref offset, header),
|
||||
HeaderID.AVInfo => ParseUnknownExtraField(data, ref offset), // TODO: Implement model
|
||||
@@ -724,7 +695,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
offset -= 2;
|
||||
|
||||
// Read based on the ID
|
||||
var field = id switch
|
||||
ExtensibleDataField? field = id switch
|
||||
{
|
||||
HeaderID.Zip64ExtendedInformation => ParseZip64ExtendedInformationExtraField(data, ref offset, header),
|
||||
HeaderID.AVInfo => ParseUnknownExtraField(data, ref offset), // TODO: Implement model
|
||||
@@ -801,13 +772,15 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled unknown extras field on success, null on error</returns>
|
||||
private static ExtensibleDataField? ParseUnknownExtraField(byte[] data, ref int offset)
|
||||
private static UnknownExtraField? ParseUnknownExtraField(byte[] data, ref int offset)
|
||||
{
|
||||
_ = (HeaderID)data.ReadUInt16LittleEndian(ref offset);
|
||||
ushort dataSize = data.ReadUInt16LittleEndian(ref offset);
|
||||
_ = data.ReadBytes(ref offset, dataSize);
|
||||
var obj = new UnknownExtraField();
|
||||
|
||||
return null;
|
||||
obj.HeaderID = (HeaderID)data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.DataSize = data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.Data = data.ReadBytes(ref offset, obj.DataSize);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -911,10 +884,10 @@ namespace SabreTools.Serialization.Deserializers
|
||||
return null;
|
||||
|
||||
obj.DataSize = data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.BSize = data.ReadUInt32LittleEndian(ref offset);
|
||||
obj.CType = data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.EACRC = data.ReadUInt32LittleEndian(ref offset);
|
||||
obj.Var = data.ReadBytes(ref offset, obj.DataSize - 10);
|
||||
obj.UncompressedBlockSize = data.ReadUInt32LittleEndian(ref offset);
|
||||
obj.CompressionType = data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.CRC32 = data.ReadUInt32LittleEndian(ref offset);
|
||||
obj.Data = data.ReadBytes(ref offset, obj.DataSize - 10);
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -936,27 +909,23 @@ namespace SabreTools.Serialization.Deserializers
|
||||
obj.DataSize = data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.Reserved = data.ReadUInt32LittleEndian(ref offset);
|
||||
|
||||
List<ushort> tags = [];
|
||||
List<ushort> sizes = [];
|
||||
List<byte[]> datas = [];
|
||||
List<TagSizeVar> entries = [];
|
||||
|
||||
int bytesRemaining = obj.DataSize - 4;
|
||||
while (bytesRemaining > 0)
|
||||
{
|
||||
ushort tag = data.ReadUInt16LittleEndian(ref offset);
|
||||
ushort size = data.ReadUInt16LittleEndian(ref offset);
|
||||
byte[] datum = data.ReadBytes(ref offset, size);
|
||||
var entry = new TagSizeVar();
|
||||
|
||||
tags.Add(tag);
|
||||
sizes.Add(size);
|
||||
datas.Add(datum);
|
||||
entry.Tag = data.ReadUInt16LittleEndian(ref offset);
|
||||
entry.Size = data.ReadUInt16LittleEndian(ref offset);
|
||||
entry.Var = data.ReadBytes(ref offset, entry.Size);
|
||||
|
||||
bytesRemaining -= 4 + size;
|
||||
entries.Add(entry);
|
||||
|
||||
bytesRemaining -= 4 + entry.Size;
|
||||
}
|
||||
|
||||
obj.Tags = [.. tags];
|
||||
obj.Size = [.. sizes];
|
||||
obj.Vars = null; // TODO: Fix once this becomes a proper array of arrays
|
||||
obj.TagSizeVars = [.. entries];
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -978,27 +947,23 @@ namespace SabreTools.Serialization.Deserializers
|
||||
obj.DataSize = data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.CRC = data.ReadUInt32LittleEndian(ref offset);
|
||||
|
||||
List<ushort> tags = [];
|
||||
List<ushort> sizes = [];
|
||||
List<byte[]> datas = [];
|
||||
List<TagSizeVar> entries = [];
|
||||
|
||||
int bytesRemaining = obj.DataSize - 4;
|
||||
while (bytesRemaining > 0)
|
||||
{
|
||||
ushort tag = data.ReadUInt16LittleEndian(ref offset);
|
||||
ushort size = data.ReadUInt16LittleEndian(ref offset);
|
||||
byte[] datum = data.ReadBytes(ref offset, size);
|
||||
var entry = new TagSizeVar();
|
||||
|
||||
tags.Add(tag);
|
||||
sizes.Add(size);
|
||||
datas.Add(datum);
|
||||
entry.Tag = data.ReadUInt16LittleEndian(ref offset);
|
||||
entry.Size = data.ReadUInt16LittleEndian(ref offset);
|
||||
entry.Var = data.ReadBytes(ref offset, entry.Size);
|
||||
|
||||
bytesRemaining -= 4 + size;
|
||||
entries.Add(entry);
|
||||
|
||||
bytesRemaining -= 4 + entry.Size;
|
||||
}
|
||||
|
||||
obj.Tags = [.. tags];
|
||||
obj.Size = [.. sizes];
|
||||
obj.Vars = null; // TODO: Fix once this becomes a proper array of arrays
|
||||
obj.TagSizeVars = [.. entries];
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -1018,11 +983,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
return null;
|
||||
|
||||
obj.DataSize = data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.Atime = data.ReadUInt32LittleEndian(ref offset);
|
||||
obj.Mtime = data.ReadUInt32LittleEndian(ref offset);
|
||||
obj.Uid = data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.Gid = data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.Var = data.ReadBytes(ref offset, obj.DataSize - 12);
|
||||
obj.FileLastAccessTime = data.ReadUInt32LittleEndian(ref offset);
|
||||
obj.FileLastModificationTime = data.ReadUInt32LittleEndian(ref offset);
|
||||
obj.FileUserID = data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.FileGroupID = data.ReadUInt16LittleEndian(ref offset);
|
||||
obj.Data = data.ReadBytes(ref offset, obj.DataSize - 12);
|
||||
|
||||
return obj;
|
||||
}
|
||||
@@ -1152,27 +1117,23 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
obj.DataSize = data.ReadUInt16LittleEndian(ref offset);
|
||||
|
||||
List<ushort> tags = [];
|
||||
List<ushort> sizes = [];
|
||||
List<byte[]> datas = [];
|
||||
List<TagSizeVar> entries = [];
|
||||
|
||||
int bytesRemaining = obj.DataSize - 4;
|
||||
while (bytesRemaining > 0)
|
||||
{
|
||||
ushort tag = data.ReadUInt16LittleEndian(ref offset);
|
||||
ushort size = data.ReadUInt16LittleEndian(ref offset);
|
||||
byte[] datum = data.ReadBytes(ref offset, size);
|
||||
var entry = new TagSizeVar();
|
||||
|
||||
tags.Add(tag);
|
||||
sizes.Add(size);
|
||||
datas.Add(datum);
|
||||
entry.Tag = data.ReadUInt16LittleEndian(ref offset);
|
||||
entry.Size = data.ReadUInt16LittleEndian(ref offset);
|
||||
entry.Var = data.ReadBytes(ref offset, entry.Size);
|
||||
|
||||
bytesRemaining -= 4 + size;
|
||||
entries.Add(entry);
|
||||
|
||||
bytesRemaining -= 4 + entry.Size;
|
||||
}
|
||||
|
||||
obj.Tags = [.. tags];
|
||||
obj.Size = [.. sizes];
|
||||
obj.Vars = null; // TODO: Fix once this becomes a proper array of arrays
|
||||
obj.TagSizeVars = [.. entries];
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.PortableExecutable;
|
||||
using SabreTools.Models.PortableExecutable.COFFSymbolTableEntries;
|
||||
using static SabreTools.Models.PortableExecutable.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
@@ -371,6 +372,23 @@ namespace SabreTools.Serialization.Deserializers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a CLRTokenDefinition
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled CLRTokenDefinition on success, null on error</returns>
|
||||
public static CLRTokenDefinition ParseCLRTokenDefinition(Stream data)
|
||||
{
|
||||
var obj = new CLRTokenDefinition();
|
||||
|
||||
obj.AuxFormat6AuxType = data.ReadByteValue();
|
||||
obj.AuxFormat6Reserved1 = data.ReadByteValue();
|
||||
obj.AuxFormat6SymbolTableIndex = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat6Reserved2 = data.ReadBytes(12);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a COFFFileHeader
|
||||
/// </summary>
|
||||
@@ -420,192 +438,15 @@ namespace SabreTools.Serialization.Deserializers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a COFFSymbolTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled COFFSymbolTableEntry on success, null on error</returns>
|
||||
/// <remarks>Standard COFF Symbol Table Entry</remarks>
|
||||
public static COFFSymbolTableEntry? ParseCOFFSymbolTableEntryType0(Stream data, out int currentSymbolType)
|
||||
{
|
||||
var entry = new COFFSymbolTableEntry();
|
||||
entry.ShortName = data.ReadBytes(8);
|
||||
if (entry.ShortName != null)
|
||||
entry.Zeroes = BitConverter.ToUInt32(entry.ShortName, 0);
|
||||
|
||||
if (entry.Zeroes == 0)
|
||||
{
|
||||
if (entry.ShortName != null)
|
||||
entry.Offset = BitConverter.ToUInt32(entry.ShortName, 4);
|
||||
|
||||
entry.ShortName = null;
|
||||
}
|
||||
entry.Value = data.ReadUInt32LittleEndian();
|
||||
entry.SectionNumber = data.ReadUInt16LittleEndian();
|
||||
entry.SymbolType = (SymbolType)data.ReadUInt16LittleEndian();
|
||||
entry.StorageClass = (StorageClass)data.ReadByteValue();
|
||||
entry.NumberOfAuxSymbols = data.ReadByteValue();
|
||||
|
||||
if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL
|
||||
&& entry.SymbolType == SymbolType.IMAGE_SYM_TYPE_FUNC
|
||||
&& entry.SectionNumber > 0)
|
||||
{
|
||||
currentSymbolType = 1;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FUNCTION
|
||||
&& entry.ShortName != null
|
||||
&& ((entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x62 && entry.ShortName[2] == 0x66) // .bf
|
||||
|| (entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x65 && entry.ShortName[2] == 0x66))) // .ef
|
||||
{
|
||||
currentSymbolType = 2;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL
|
||||
&& entry.SectionNumber == (ushort)SectionNumber.IMAGE_SYM_UNDEFINED
|
||||
&& entry.Value == 0)
|
||||
{
|
||||
currentSymbolType = 3;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FILE)
|
||||
{
|
||||
// TODO: Symbol name should be ".file"
|
||||
currentSymbolType = 4;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_STATIC)
|
||||
{
|
||||
// TODO: Should have the name of a section (like ".text")
|
||||
currentSymbolType = 5;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_CLR_TOKEN)
|
||||
{
|
||||
currentSymbolType = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSymbolType = -1;
|
||||
return null;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a COFFSymbolTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled COFFSymbolTableEntry on success, null on error</returns>
|
||||
/// <remarks>Auxiliary Format 1: Function Definitions</remarks>
|
||||
public static COFFSymbolTableEntry ParseCOFFSymbolTableEntryType1(Stream data)
|
||||
{
|
||||
var obj = new COFFSymbolTableEntry();
|
||||
|
||||
obj.AuxFormat1TagIndex = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat1TotalSize = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat1PointerToLinenumber = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat1PointerToNextFunction = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat1Unused = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a COFFSymbolTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled COFFSymbolTableEntry on success, null on error</returns>
|
||||
/// <remarks>Auxiliary Format 2: .bf and .ef Symbols</remarks>
|
||||
public static COFFSymbolTableEntry ParseCOFFSymbolTableEntryType2(Stream data)
|
||||
{
|
||||
var obj = new COFFSymbolTableEntry();
|
||||
|
||||
obj.AuxFormat2Unused1 = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat2Linenumber = data.ReadUInt16LittleEndian();
|
||||
obj.AuxFormat2Unused2 = data.ReadBytes(6);
|
||||
obj.AuxFormat2PointerToNextFunction = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat2Unused3 = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a COFFSymbolTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled COFFSymbolTableEntry on success, null on error</returns>
|
||||
/// <remarks>Auxiliary Format 3: Weak Externals</remarks>
|
||||
public static COFFSymbolTableEntry ParseCOFFSymbolTableEntryType3(Stream data)
|
||||
{
|
||||
var obj = new COFFSymbolTableEntry();
|
||||
|
||||
obj.AuxFormat3TagIndex = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat3Characteristics = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat3Unused = data.ReadBytes(10);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a COFFSymbolTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled COFFSymbolTableEntry on success, null on error</returns>
|
||||
/// <remarks>Auxiliary Format 4: Files</remarks>
|
||||
public static COFFSymbolTableEntry ParseCOFFSymbolTableEntryType4(Stream data)
|
||||
{
|
||||
var obj = new COFFSymbolTableEntry();
|
||||
|
||||
obj.AuxFormat4FileName = data.ReadBytes(18);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a COFFSymbolTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled COFFSymbolTableEntry on success, null on error</returns>
|
||||
/// <remarks>Auxiliary Format 5: Section Definitions</remarks>
|
||||
public static COFFSymbolTableEntry ParseCOFFSymbolTableEntryType5(Stream data)
|
||||
{
|
||||
var obj = new COFFSymbolTableEntry();
|
||||
|
||||
obj.AuxFormat5Length = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat5NumberOfRelocations = data.ReadUInt16LittleEndian();
|
||||
obj.AuxFormat5NumberOfLinenumbers = data.ReadUInt16LittleEndian();
|
||||
obj.AuxFormat5CheckSum = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat5Number = data.ReadUInt16LittleEndian();
|
||||
obj.AuxFormat5Selection = data.ReadByteValue();
|
||||
obj.AuxFormat5Unused = data.ReadBytes(3);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a COFFSymbolTableEntry
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled COFFSymbolTableEntry on success, null on error</returns>
|
||||
/// <remarks>Auxiliary Format 6: CLR Token Definition</remarks>
|
||||
public static COFFSymbolTableEntry ParseCOFFSymbolTableEntryType6(Stream data)
|
||||
{
|
||||
var obj = new COFFSymbolTableEntry();
|
||||
|
||||
obj.AuxFormat6AuxType = data.ReadByteValue();
|
||||
obj.AuxFormat6Reserved1 = data.ReadByteValue();
|
||||
obj.AuxFormat6SymbolTableIndex = data.ReadUInt32LittleEndian();
|
||||
obj.AuxFormat6Reserved2 = data.ReadBytes(12);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a COFF symbol table
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="count">Number of COFF symbol table entries to read</param>
|
||||
/// <returns>Filled COFF symbol table on success, null on error</returns>
|
||||
public static COFFSymbolTableEntry[]? ParseCOFFSymbolTable(Stream data, uint count)
|
||||
public static BaseEntry[]? ParseCOFFSymbolTable(Stream data, uint count)
|
||||
{
|
||||
var obj = new COFFSymbolTableEntry[count];
|
||||
var obj = new BaseEntry[count];
|
||||
|
||||
int auxSymbolsRemaining = 0;
|
||||
int currentSymbolType = 0;
|
||||
@@ -615,7 +456,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Standard COFF Symbol Table Entry
|
||||
if (currentSymbolType == 0)
|
||||
{
|
||||
var entry = ParseCOFFSymbolTableEntryType0(data, out currentSymbolType);
|
||||
var entry = ParseStandardRecord(data, out currentSymbolType);
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
@@ -632,42 +473,42 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// Auxiliary Format 1: Function Definitions
|
||||
else if (currentSymbolType == 1)
|
||||
{
|
||||
obj[i] = ParseCOFFSymbolTableEntryType1(data); ;
|
||||
obj[i] = ParseFunctionDefinition(data);
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
|
||||
// Auxiliary Format 2: .bf and .ef Symbols
|
||||
else if (currentSymbolType == 2)
|
||||
{
|
||||
obj[i] = ParseCOFFSymbolTableEntryType2(data); ;
|
||||
obj[i] = ParseDescriptor(data);
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
|
||||
// Auxiliary Format 3: Weak Externals
|
||||
else if (currentSymbolType == 3)
|
||||
{
|
||||
obj[i] = ParseCOFFSymbolTableEntryType3(data); ;
|
||||
obj[i] = ParseWeakExternal(data);
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
|
||||
// Auxiliary Format 4: Files
|
||||
else if (currentSymbolType == 4)
|
||||
{
|
||||
obj[i] = ParseCOFFSymbolTableEntryType4(data); ;
|
||||
obj[i] = ParseFileRecord(data);
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
|
||||
// Auxiliary Format 5: Section Definitions
|
||||
else if (currentSymbolType == 5)
|
||||
{
|
||||
obj[i] = ParseCOFFSymbolTableEntryType5(data); ;
|
||||
obj[i] = ParseSectionDefinition(data);
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
|
||||
// Auxiliary Format 6: CLR Token Definition
|
||||
else if (currentSymbolType == 6)
|
||||
{
|
||||
obj[i] = ParseCOFFSymbolTableEntryType6(data); ;
|
||||
obj[i] = ParseCLRTokenDefinition(data);
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
|
||||
@@ -768,6 +609,24 @@ namespace SabreTools.Serialization.Deserializers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a Descriptor
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled Descriptor on success, null on error</returns>
|
||||
public static Descriptor ParseDescriptor(Stream data)
|
||||
{
|
||||
var obj = new Descriptor();
|
||||
|
||||
obj.Unused1 = data.ReadUInt32LittleEndian();
|
||||
obj.Linenumber = data.ReadUInt16LittleEndian();
|
||||
obj.Unused2 = data.ReadBytes(6);
|
||||
obj.PointerToNextFunction = data.ReadUInt32LittleEndian();
|
||||
obj.Unused3 = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ExportAddressTableEntry
|
||||
/// </summary>
|
||||
@@ -885,7 +744,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
for (int i = 0; i < directoryTable.NumberOfNamePointers; i++)
|
||||
{
|
||||
long nameAddress = initialOffset
|
||||
+ obj.NamePointerTable.Pointers[i].ConvertVirtualAddress(sections); ;
|
||||
+ obj.NamePointerTable.Pointers[i].ConvertVirtualAddress(sections);
|
||||
data.Seek(nameAddress, SeekOrigin.Begin);
|
||||
|
||||
string? str = data.ReadNullTerminatedAnsiString();
|
||||
@@ -898,6 +757,38 @@ namespace SabreTools.Serialization.Deserializers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a FileRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled FileRecord on success, null on error</returns>
|
||||
public static FileRecord ParseFileRecord(Stream data)
|
||||
{
|
||||
var obj = new FileRecord();
|
||||
|
||||
obj.FileName = data.ReadBytes(18);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a FunctionDefinition
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled FunctionDefinition on success, null on error</returns>
|
||||
public static FunctionDefinition ParseFunctionDefinition(Stream data)
|
||||
{
|
||||
var obj = new FunctionDefinition();
|
||||
|
||||
obj.TagIndex = data.ReadUInt32LittleEndian();
|
||||
obj.TotalSize = data.ReadUInt32LittleEndian();
|
||||
obj.PointerToLinenumber = data.ReadUInt32LittleEndian();
|
||||
obj.PointerToNextFunction = data.ReadUInt32LittleEndian();
|
||||
obj.Unused = data.ReadUInt16LittleEndian();
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a HintNameTableEntry
|
||||
/// </summary>
|
||||
@@ -1499,6 +1390,26 @@ namespace SabreTools.Serialization.Deserializers
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a SectionDefinition
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled SectionDefinition on success, null on error</returns>
|
||||
public static SectionDefinition ParseSectionDefinition(Stream data)
|
||||
{
|
||||
var obj = new SectionDefinition();
|
||||
|
||||
obj.Length = data.ReadUInt32LittleEndian();
|
||||
obj.NumberOfRelocations = data.ReadUInt16LittleEndian();
|
||||
obj.NumberOfLinenumbers = data.ReadUInt16LittleEndian();
|
||||
obj.CheckSum = data.ReadUInt32LittleEndian();
|
||||
obj.Number = data.ReadUInt16LittleEndian();
|
||||
obj.Selection = data.ReadByteValue();
|
||||
obj.Unused = data.ReadBytes(3);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a SectionHeader
|
||||
/// </summary>
|
||||
@@ -1531,5 +1442,88 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a StandardRecord
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled StandardRecord on success, null on error</returns>
|
||||
public static StandardRecord? ParseStandardRecord(Stream data, out int currentSymbolType)
|
||||
{
|
||||
var entry = new StandardRecord();
|
||||
entry.ShortName = data.ReadBytes(8);
|
||||
if (entry.ShortName != null)
|
||||
entry.Zeroes = BitConverter.ToUInt32(entry.ShortName, 0);
|
||||
|
||||
if (entry.Zeroes == 0)
|
||||
{
|
||||
if (entry.ShortName != null)
|
||||
entry.Offset = BitConverter.ToUInt32(entry.ShortName, 4);
|
||||
|
||||
entry.ShortName = null;
|
||||
}
|
||||
entry.Value = data.ReadUInt32LittleEndian();
|
||||
entry.SectionNumber = data.ReadUInt16LittleEndian();
|
||||
entry.SymbolType = (SymbolType)data.ReadUInt16LittleEndian();
|
||||
entry.StorageClass = (StorageClass)data.ReadByteValue();
|
||||
entry.NumberOfAuxSymbols = data.ReadByteValue();
|
||||
|
||||
if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL
|
||||
&& entry.SymbolType == SymbolType.IMAGE_SYM_TYPE_FUNC
|
||||
&& entry.SectionNumber > 0)
|
||||
{
|
||||
currentSymbolType = 1;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FUNCTION
|
||||
&& entry.ShortName != null
|
||||
&& ((entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x62 && entry.ShortName[2] == 0x66) // .bf
|
||||
|| (entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x65 && entry.ShortName[2] == 0x66))) // .ef
|
||||
{
|
||||
currentSymbolType = 2;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL
|
||||
&& entry.SectionNumber == (ushort)SectionNumber.IMAGE_SYM_UNDEFINED
|
||||
&& entry.Value == 0)
|
||||
{
|
||||
currentSymbolType = 3;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FILE)
|
||||
{
|
||||
// TODO: Symbol name should be ".file"
|
||||
currentSymbolType = 4;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_STATIC)
|
||||
{
|
||||
// TODO: Should have the name of a section (like ".text")
|
||||
currentSymbolType = 5;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_CLR_TOKEN)
|
||||
{
|
||||
currentSymbolType = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSymbolType = -1;
|
||||
return null;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a WeakExternal
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled WeakExternal on success, null on error</returns>
|
||||
public static WeakExternal ParseWeakExternal(Stream data)
|
||||
{
|
||||
var obj = new WeakExternal();
|
||||
|
||||
obj.TagIndex = data.ReadUInt32LittleEndian();
|
||||
obj.Characteristics = data.ReadUInt32LittleEndian();
|
||||
obj.Unused = data.ReadBytes(10);
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,19 +70,15 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
obj.Header = header;
|
||||
|
||||
// TODO: Replace this with AlignToBoundary when IO is updated
|
||||
// Align to the block size
|
||||
while (data.Position % 512 != 0 && data.Position < data.Length)
|
||||
{
|
||||
_ = data.ReadByteValue();
|
||||
}
|
||||
data.AlignToBoundary(512);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Blocks
|
||||
|
||||
// Exit if the size is invalid
|
||||
string sizeOctalString = Encoding.ASCII.GetString(header.Size!).TrimEnd('\0');
|
||||
string sizeOctalString = header.Size!.TrimEnd('\0');
|
||||
if (sizeOctalString.Length == 0)
|
||||
return obj;
|
||||
|
||||
@@ -120,12 +116,18 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
byte[] filenameBytes = data.ReadBytes(100);
|
||||
obj.FileName = Encoding.ASCII.GetString(filenameBytes);
|
||||
obj.Mode = data.ReadBytes(8);
|
||||
obj.UID = data.ReadBytes(8);
|
||||
obj.GID = data.ReadBytes(8);
|
||||
obj.Size = data.ReadBytes(12);
|
||||
obj.ModifiedTime = data.ReadBytes(12);
|
||||
obj.Checksum = data.ReadBytes(8);
|
||||
byte[] modeBytes = data.ReadBytes(8);
|
||||
obj.Mode = Encoding.ASCII.GetString(modeBytes);
|
||||
byte[] uidBytes = data.ReadBytes(8);
|
||||
obj.UID = Encoding.ASCII.GetString(uidBytes);
|
||||
byte[] gidBytes = data.ReadBytes(8);
|
||||
obj.GID = Encoding.ASCII.GetString(gidBytes);
|
||||
byte[] sizeBytes = data.ReadBytes(12);
|
||||
obj.Size = Encoding.ASCII.GetString(sizeBytes);
|
||||
byte[] modifiedBytes = data.ReadBytes(12);
|
||||
obj.ModifiedTime = Encoding.ASCII.GetString(modifiedBytes);
|
||||
byte[] checksumBytes = data.ReadBytes(8);
|
||||
obj.Checksum = Encoding.ASCII.GetString(checksumBytes);
|
||||
obj.TypeFlag = (TypeFlag)data.ReadByteValue();
|
||||
byte[] linkNameBytes = data.ReadBytes(100);
|
||||
obj.LinkName = Encoding.ASCII.GetString(linkNameBytes);
|
||||
@@ -172,9 +174,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
var obj = new Block();
|
||||
|
||||
// TODO: Assign this to obj.Data directly when Models is updated
|
||||
byte[] temp = data.ReadBytes(512);
|
||||
Array.Copy(temp, 0, obj.Data, 0, 512);
|
||||
obj.Data = data.ReadBytes(512);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
150
SabreTools.Serialization/Deserializers/WiseOverlayHeader.cs
Normal file
150
SabreTools.Serialization/Deserializers/WiseOverlayHeader.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.WiseInstaller;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class WiseOverlayHeader : BaseBinaryDeserializer<OverlayHeader>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override OverlayHeader? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position;
|
||||
|
||||
var overlayHeader = ParseOverlayHeader(data);
|
||||
|
||||
// WiseColors.dib
|
||||
if (overlayHeader.DibDeflatedSize >= data.Length)
|
||||
return null;
|
||||
else if (overlayHeader.DibDeflatedSize > overlayHeader.DibInflatedSize)
|
||||
return null;
|
||||
|
||||
// WiseScript.bin
|
||||
if (overlayHeader.WiseScriptDeflatedSize == 0)
|
||||
return null;
|
||||
else if (overlayHeader.WiseScriptDeflatedSize >= data.Length)
|
||||
return null;
|
||||
else if (overlayHeader.WiseScriptDeflatedSize > overlayHeader.WiseScriptInflatedSize)
|
||||
return null;
|
||||
|
||||
// WISE0001.DLL
|
||||
if (overlayHeader.WiseDllDeflatedSize >= data.Length)
|
||||
return null;
|
||||
|
||||
// FILE00XX.DAT
|
||||
if (overlayHeader.FinalFileDeflatedSize == 0)
|
||||
return null;
|
||||
else if (overlayHeader.FinalFileDeflatedSize >= data.Length)
|
||||
return null;
|
||||
else if (overlayHeader.FinalFileDeflatedSize > overlayHeader.FinalFileInflatedSize)
|
||||
return null;
|
||||
|
||||
// Valid for older overlay headers
|
||||
if (overlayHeader.Endianness == 0x0000 && overlayHeader.InitTextLen == 0)
|
||||
return overlayHeader;
|
||||
if (overlayHeader.Endianness != Endianness.LittleEndian && overlayHeader.Endianness != Endianness.BigEndian)
|
||||
return null;
|
||||
|
||||
return overlayHeader;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore the actual error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an OverlayHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled OverlayHeader on success, null on error</returns>
|
||||
private static OverlayHeader ParseOverlayHeader(Stream data)
|
||||
{
|
||||
var header = new OverlayHeader();
|
||||
|
||||
header.DllNameLen = data.ReadByteValue();
|
||||
if (header.DllNameLen > 0)
|
||||
{
|
||||
byte[] dllName = data.ReadBytes(header.DllNameLen);
|
||||
header.DllName = Encoding.ASCII.GetString(dllName);
|
||||
header.DllSize = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
|
||||
// Read as a single block
|
||||
header.Flags = (OverlayHeaderFlags)data.ReadUInt32LittleEndian();
|
||||
|
||||
// Read as a single block
|
||||
header.GraphicsData = data.ReadBytes(12);
|
||||
|
||||
// Read as a single block
|
||||
header.WiseScriptExitEventOffset = data.ReadUInt32LittleEndian();
|
||||
header.WiseScriptCancelEventOffset = data.ReadUInt32LittleEndian();
|
||||
|
||||
// Read as a single block
|
||||
header.WiseScriptInflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.WiseScriptDeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.WiseDllDeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.Ctl3d32DeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.SomeData4DeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.RegToolDeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.ProgressDllDeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.SomeData7DeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.SomeData8DeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.SomeData9DeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.SomeData10DeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.FinalFileDeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.FinalFileInflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.EOF = data.ReadUInt32LittleEndian();
|
||||
|
||||
// Newer installers read this and DibInflatedSize in the above block
|
||||
header.DibDeflatedSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
// Handle older overlay data
|
||||
if (header.DibDeflatedSize > data.Length)
|
||||
{
|
||||
header.DibDeflatedSize = 0;
|
||||
data.Seek(-4, SeekOrigin.Current);
|
||||
return header;
|
||||
}
|
||||
|
||||
header.DibInflatedSize = data.ReadUInt32LittleEndian();
|
||||
|
||||
// Peek at the next 2 bytes
|
||||
ushort peek = data.ReadUInt16LittleEndian();
|
||||
data.Seek(-2, SeekOrigin.Current);
|
||||
|
||||
// If the next value is a known Endianness
|
||||
if (Enum.IsDefined(typeof(Endianness), peek))
|
||||
{
|
||||
header.Endianness = (Endianness)data.ReadUInt16LittleEndian();
|
||||
}
|
||||
else
|
||||
{
|
||||
// The first two values are part of the sizes block above
|
||||
header.InstallScriptDeflatedSize = data.ReadUInt32LittleEndian();
|
||||
header.CharacterSet = (CharacterSet)data.ReadUInt32LittleEndian();
|
||||
header.Endianness = (Endianness)data.ReadUInt16LittleEndian();
|
||||
}
|
||||
|
||||
// Endianness and init text len are read in a single block
|
||||
header.InitTextLen = data.ReadByteValue();
|
||||
if (header.InitTextLen > 0)
|
||||
{
|
||||
byte[] initText = data.ReadBytes(header.InitTextLen);
|
||||
header.InitText = Encoding.ASCII.GetString(initText);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
}
|
||||
}
|
||||
1786
SabreTools.Serialization/Deserializers/WiseScript.cs
Normal file
1786
SabreTools.Serialization/Deserializers/WiseScript.cs
Normal file
File diff suppressed because it is too large
Load Diff
332
SabreTools.Serialization/Deserializers/WiseSectionHeader.cs
Normal file
332
SabreTools.Serialization/Deserializers/WiseSectionHeader.cs
Normal file
@@ -0,0 +1,332 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Models.WiseInstaller;
|
||||
using static SabreTools.Models.WiseInstaller.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class WiseSectionHeader : BaseBinaryDeserializer<SectionHeader>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public override SectionHeader? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position;
|
||||
|
||||
var header = ParseSectionHeader(data, initialOffset);
|
||||
if (header == null)
|
||||
return null;
|
||||
|
||||
// Main MSI file
|
||||
// If these go wrong, then there actually is a major issue, and the fallback won't work.
|
||||
if (header.MsiFileEntryLength == 0)
|
||||
return null;
|
||||
else if (header.MsiFileEntryLength >= data.Length)
|
||||
return null;
|
||||
|
||||
// First executable file
|
||||
if (header.FirstExecutableFileEntryLength >= data.Length)
|
||||
return header;
|
||||
|
||||
// Second executable file
|
||||
if (header.SecondExecutableFileEntryLength >= data.Length)
|
||||
return header;
|
||||
|
||||
return header;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Could header somehow be returned here too?
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a WiseSectionHeader
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="initialOffset">Initial offset to use in address comparisons</param>
|
||||
/// <returns>Filled WiseSectionHeader on success, null on error</returns>
|
||||
public static SectionHeader? ParseSectionHeader(Stream data, long initialOffset)
|
||||
{
|
||||
var header = new SectionHeader();
|
||||
|
||||
// Setup required variables
|
||||
int wisOffset = -1;
|
||||
int headerLength = -1;
|
||||
|
||||
// Find offset of "WIS", determine header length, read presumed version value
|
||||
foreach (int offset in WisOffsets)
|
||||
{
|
||||
data.Seek(initialOffset + offset, 0);
|
||||
byte[] checkBytes = data.ReadBytes(3);
|
||||
if (!checkBytes.EqualsExactly(WisString))
|
||||
continue;
|
||||
|
||||
headerLength = WiseSectionHeaderLengthDictionary[offset];
|
||||
int versionOffset = WiseSectionVersionOffsetDictionary[offset];
|
||||
|
||||
data.Seek(initialOffset + offset - versionOffset, 0);
|
||||
header.Version = data.ReadBytes(versionOffset);
|
||||
wisOffset = offset;
|
||||
}
|
||||
bool earlyReturn = false;
|
||||
|
||||
// If the header is invalid
|
||||
if (header.Version == null)
|
||||
earlyReturn = true;
|
||||
if (wisOffset < 0)
|
||||
earlyReturn = true;
|
||||
if (headerLength < 0)
|
||||
earlyReturn = true;
|
||||
|
||||
//Seek back to the beginning of the section
|
||||
data.Seek(initialOffset, 0);
|
||||
|
||||
// Read common values
|
||||
header.UnknownDataSize = data.ReadUInt32LittleEndian();
|
||||
header.SecondExecutableFileEntryLength = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue2 = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue3 = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue4 = data.ReadUInt32LittleEndian();
|
||||
header.FirstExecutableFileEntryLength = data.ReadUInt32LittleEndian();
|
||||
header.MsiFileEntryLength = data.ReadUInt32LittleEndian();
|
||||
|
||||
if (earlyReturn)
|
||||
{
|
||||
return header;
|
||||
}
|
||||
|
||||
if (headerLength > 6)
|
||||
{
|
||||
header.UnknownValue7 = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue8 = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
|
||||
if (headerLength > 8)
|
||||
{
|
||||
header.ThirdExecutableFileEntryLength = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue10 = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue11 = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue12 = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue13 = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue14 = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue15 = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue16 = data.ReadUInt32LittleEndian();
|
||||
header.UnknownValue17 = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
|
||||
if (headerLength > 17)
|
||||
{
|
||||
header.UnknownValue18 = data.ReadUInt32LittleEndian();
|
||||
}
|
||||
|
||||
// Seek to the WIS string offset
|
||||
data.Seek(initialOffset + wisOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the consistent strings
|
||||
header.TmpString = data.ReadNullTerminatedAnsiString();
|
||||
header.GuidString = data.ReadNullTerminatedAnsiString();
|
||||
|
||||
// Parse the pre-string section
|
||||
int preStringBytesSize = GetPreStringBytesSize(data, header, wisOffset);
|
||||
if (preStringBytesSize <= 0)
|
||||
return header;
|
||||
|
||||
// Read the pre-string bytes
|
||||
header.PreStringValues = data.ReadBytes(preStringBytesSize);
|
||||
|
||||
// Try to read the string arrays
|
||||
// TODO: Count size of string section for later size verification
|
||||
byte[][]? stringArrays = ParseStringTable(data, header.PreStringValues);
|
||||
if (stringArrays == null)
|
||||
return header;
|
||||
|
||||
// Set the string arrays
|
||||
header.Strings = stringArrays;
|
||||
|
||||
// Not sure what this data is. Might be a wisescript?
|
||||
// TODO: Should really be done in the wrapper, but almost everything there is static so there's no good place\
|
||||
if (header.UnknownDataSize != 0)
|
||||
data.Seek(header.UnknownDataSize, SeekOrigin.Current);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the pre-string bytes size, if possible
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="header">Section header to get information from</param>
|
||||
/// <param name="wisOffset">Offset to the WIS string relative to the start of the header</param>
|
||||
/// <returns>The size of the pre-string section</returns>
|
||||
/// <remarks>
|
||||
/// This method also sets <see cref="SectionHeader.NonWiseVersion"/> and
|
||||
/// <see cref="SectionHeader.PreFontValue"/>, if possible.
|
||||
/// </remarks>
|
||||
private static int GetPreStringBytesSize(Stream data, SectionHeader header, int wisOffset)
|
||||
{
|
||||
// Handle a case that shouldn't happen
|
||||
if (header.Version == null)
|
||||
return 0;
|
||||
|
||||
// TODO: better way to figure out how far it's needed to advance?
|
||||
int versionSize;
|
||||
if (header.Version[header.Version.Length - 1] == 0x02)
|
||||
versionSize = header.Version[header.Version.Length - 3];
|
||||
else
|
||||
versionSize = header.Version[header.Version.Length - 2];
|
||||
|
||||
// Third byte seems to indicate size of NonWiseVer
|
||||
if (versionSize <= 1)
|
||||
{
|
||||
byte[] stringBytes = data.ReadBytes(versionSize);
|
||||
header.NonWiseVersion = Encoding.ASCII.GetString(stringBytes);
|
||||
if (wisOffset <= 77)
|
||||
header.PreFontValue = data.ReadBytes(2);
|
||||
else
|
||||
header.PreFontValue = data.ReadBytes(4);
|
||||
}
|
||||
|
||||
// If that third byte is 0x01, no NonWiseVersion string is present
|
||||
else
|
||||
{
|
||||
header.PreFontValue = data.ReadBytes(3);
|
||||
}
|
||||
|
||||
header.FontSize = data.ReadByte();
|
||||
int preStringBytesSize = WiseSectionPreStringBytesSize[wisOffset];
|
||||
|
||||
// Hack for Codesited5.exe , very early and very strange.
|
||||
if (header.Version[1] == 0x01)
|
||||
preStringBytesSize = 2;
|
||||
|
||||
return preStringBytesSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse the string table, if possible
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
/// <param name="header"></param>
|
||||
/// <param name="preStringBytesSize"></param>
|
||||
/// <returns>The filled string table on success, false otherwise</returns>
|
||||
private static byte[][]? ParseStringTable(Stream data, byte[] preStringValues)
|
||||
{
|
||||
// Setup the loop variables
|
||||
List<byte[]> stringList = [];
|
||||
int counter = 0;
|
||||
bool endNow = false;
|
||||
bool languageSection = false;
|
||||
int languageSectionCounter = 0;
|
||||
|
||||
// Iterate pre-string byte array
|
||||
while (counter < preStringValues.Length)
|
||||
{
|
||||
// Read the next byte value
|
||||
byte currentByte = preStringValues[counter];
|
||||
if (currentByte == 0x00)
|
||||
break;
|
||||
|
||||
// Now doing third byte after language section begins
|
||||
if (currentByte == 0x01 && languageSectionCounter == 2)
|
||||
{
|
||||
int extraLanguages = preStringValues[counter + 1];
|
||||
for (int i = 0; i < extraLanguages; i++)
|
||||
{
|
||||
byte[]? incrementBytes = data.ReadBytes(2);
|
||||
string? extraLanguageString = data.ReadNullTerminatedAnsiString();
|
||||
if (extraLanguageString == null)
|
||||
return null;
|
||||
|
||||
byte[]? extraLanguageStringArray = Encoding.ASCII.GetBytes(extraLanguageString);
|
||||
stringList.Add(incrementBytes);
|
||||
stringList.Add(extraLanguageStringArray);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Prepends non-string-size indicators
|
||||
else if (currentByte == 0x01)
|
||||
{
|
||||
// 01 01 01 01: entering font section
|
||||
// 01 5D 5C 01: link section; 5D and 5C are string sizes
|
||||
int oneCount = 1;
|
||||
counter++;
|
||||
for (int i = counter; i <= preStringValues.Length; i++)
|
||||
{
|
||||
if (i == preStringValues.Length)
|
||||
{
|
||||
byte checkForZero;
|
||||
do
|
||||
{
|
||||
checkForZero = data.ReadByteValue();
|
||||
} while (checkForZero == 0x00);
|
||||
|
||||
data.Seek(-1, SeekOrigin.Current);
|
||||
endNow = true;
|
||||
break;
|
||||
}
|
||||
|
||||
currentByte = preStringValues[counter];
|
||||
|
||||
// 0x01 followed by one more 0x01 seems to indicate to skip 2 null bytes, but 0x01 followed by
|
||||
// three more 0x01 seems to indicate an unspecified length of null bytes that must be skipped.
|
||||
// It has already been observed it mean 22 or 27 between 2 samples.
|
||||
|
||||
// If you encounter a null byte in the actual pre-string byte array, it seems to always be
|
||||
// after you've read all the strings successfully.
|
||||
if (currentByte == 0x00)
|
||||
{
|
||||
endNow = true;
|
||||
break;
|
||||
}
|
||||
else if (currentByte > 0x01)
|
||||
{
|
||||
byte checkForZero;
|
||||
do
|
||||
{
|
||||
checkForZero = data.ReadByteValue();
|
||||
} while (checkForZero == 0x00);
|
||||
|
||||
data.Seek(-1, SeekOrigin.Current);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
oneCount++;
|
||||
}
|
||||
counter++;
|
||||
}
|
||||
|
||||
if (oneCount == 4)
|
||||
languageSection = true;
|
||||
}
|
||||
|
||||
// If there was an issue
|
||||
if (endNow)
|
||||
break;
|
||||
|
||||
// Read and add the string as a byte array
|
||||
byte[] currentString = data.ReadBytes(currentByte);
|
||||
stringList.Add(currentString);
|
||||
|
||||
counter++;
|
||||
if (languageSection)
|
||||
languageSectionCounter++;
|
||||
}
|
||||
|
||||
return [.. stringList];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Text;
|
||||
using System.Xml.Serialization;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.PortableExecutable;
|
||||
using SabreTools.Models.PortableExecutable.ResourceEntries;
|
||||
|
||||
namespace SabreTools.Serialization
|
||||
{
|
||||
|
||||
64
SabreTools.Serialization/Extensions.WiseScript.cs
Normal file
64
SabreTools.Serialization/Extensions.WiseScript.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SabreTools.Serialization
|
||||
{
|
||||
public static partial class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert a Wise function ID to the formal action name
|
||||
/// </summary>
|
||||
/// <param name="functionId">Function ID to convert</param>
|
||||
/// <returns>The formal action name on success, null otherwise</returns>
|
||||
public static string? FromWiseFunctionId(this string? functionId)
|
||||
{
|
||||
return functionId switch
|
||||
{
|
||||
"f0" => "Add Directory to PATH",
|
||||
"f1" => "Add to AUTOEXEC.BAT",
|
||||
"f2" => "Add to CONFIG.SYS",
|
||||
"f3" => "Add to SYSTEM.INI",
|
||||
"f8" => "Read INI Value",
|
||||
"f9" => "Get Registry Key Value",
|
||||
"f10" => "Register Font",
|
||||
"f11" => "Win32 System Directory",
|
||||
"f12" => "Check Configuration",
|
||||
"f13" => "Search for File",
|
||||
"f15" => "Read/Write Binary File",
|
||||
"f16" => "Set Variable",
|
||||
"f17" => "Get Environment Variable",
|
||||
"f19" => "Check if File/Dir Exists",
|
||||
"f20" => "Set File Attributes",
|
||||
"f21" => "Set Files/Buffers",
|
||||
"f22" => "Find File in Path",
|
||||
"f23" => "Check Disk Space",
|
||||
"f25" => "Insert Line Into Text File",
|
||||
"f27" => "Parse String",
|
||||
"f28" => "Exit Installation",
|
||||
"f29" => "Self-Register OCXs/DLLs",
|
||||
"f30" => "Install DirectX Components",
|
||||
"f31" => "Wizard Block",
|
||||
"f33" => "Read/Update Text File",
|
||||
"f34" => "Post to HTTP Server",
|
||||
"f35" => "Prompt for Filename",
|
||||
"f36" => "Start/Stop Service",
|
||||
"f38" => "Check HTTP Connection",
|
||||
|
||||
// Undefined function IDs
|
||||
"f4" => $"UNDEFINED {functionId}",
|
||||
"f5" => $"UNDEFINED {functionId}",
|
||||
"f6" => $"UNDEFINED {functionId}",
|
||||
"f7" => $"UNDEFINED {functionId}",
|
||||
"f14" => $"UNDEFINED {functionId}",
|
||||
"f18" => $"UNDEFINED {functionId}",
|
||||
"f24" => $"UNDEFINED {functionId}",
|
||||
"f26" => $"UNDEFINED {functionId}",
|
||||
"f32" => $"UNDEFINED {functionId}",
|
||||
"f37" => $"UNDEFINED {functionId}",
|
||||
|
||||
// External DLL
|
||||
null => null,
|
||||
_ => Regex.IsMatch(functionId, @"^f[0-9]{1,2}$") ? $"UNDEFINED {functionId}" : $"External: {functionId}",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,9 @@ namespace SabreTools.Serialization
|
||||
Wrapper.VBSP item => item.PrettyPrint(),
|
||||
Wrapper.VPK item => item.PrettyPrint(),
|
||||
Wrapper.WAD3 item => item.PrettyPrint(),
|
||||
Wrapper.WiseOverlayHeader item => item.PrettyPrint(),
|
||||
Wrapper.WiseScript item => item.PrettyPrint(),
|
||||
// TODO: Add implementation for WiseSectionHeader
|
||||
Wrapper.XeMID item => item.PrettyPrint(),
|
||||
Wrapper.XMID item => item.PrettyPrint(),
|
||||
Wrapper.XZP item => item.PrettyPrint(),
|
||||
@@ -123,6 +126,9 @@ namespace SabreTools.Serialization
|
||||
Wrapper.VBSP item => item.ExportJSON(),
|
||||
Wrapper.VPK item => item.ExportJSON(),
|
||||
Wrapper.WAD3 item => item.ExportJSON(),
|
||||
Wrapper.WiseOverlayHeader item => item.ExportJSON(),
|
||||
Wrapper.WiseScript item => item.ExportJSON(),
|
||||
// TODO: Add implementation for WiseSectionHeader
|
||||
Wrapper.XeMID item => item.ExportJSON(),
|
||||
Wrapper.XMID item => item.ExportJSON(),
|
||||
Wrapper.XZP item => item.ExportJSON(),
|
||||
@@ -503,6 +509,26 @@ namespace SabreTools.Serialization
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
/// </summary>
|
||||
private static StringBuilder PrettyPrint(this Wrapper.WiseOverlayHeader item)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
WiseOverlayHeader.Print(builder, item.Model);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
/// </summary>
|
||||
private static StringBuilder PrettyPrint(this Wrapper.WiseScript item)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
WiseScript.Print(builder, item.Model);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
/// </summary>
|
||||
|
||||
@@ -34,12 +34,12 @@ namespace SabreTools.Serialization.Printers
|
||||
|
||||
builder.AppendLine(header.ID1, " ID1");
|
||||
builder.AppendLine(header.ID2, " ID1");
|
||||
builder.AppendLine($" Compression method: {header.CM} (0x{(byte)header.CM:X2})");
|
||||
builder.AppendLine($" Flags: {header.FLG} (0x{(byte)header.FLG:X2})");
|
||||
builder.AppendLine(header.MTIME, " Last modified time");
|
||||
builder.AppendLine($" Extra flags: {header.XFL} (0x{(byte)header.XFL:X2})");
|
||||
builder.AppendLine($" Operating system: {header.OS} (0x{(byte)header.OS:X2})");
|
||||
builder.AppendLine(header.XLEN, " Extra length");
|
||||
builder.AppendLine($" Compression method: {header.CompressionMethod} (0x{(byte)header.CompressionMethod:X2})");
|
||||
builder.AppendLine($" Flags: {header.Flags} (0x{(byte)header.Flags:X2})");
|
||||
builder.AppendLine(header.LastModifiedTime, " Last modified time");
|
||||
builder.AppendLine($" Extra flags: {header.ExtraFlags} (0x{(byte)header.ExtraFlags:X2})");
|
||||
builder.AppendLine($" Operating system: {header.OperatingSystem} (0x{(byte)header.OperatingSystem:X2})");
|
||||
builder.AppendLine(header.ExtraLength, " Extra length");
|
||||
Print(builder, header.ExtraField);
|
||||
builder.AppendLine(header.OriginalFileName, " Original file name");
|
||||
builder.AppendLine(header.FileComment, " File comment");
|
||||
@@ -63,9 +63,9 @@ namespace SabreTools.Serialization.Printers
|
||||
var entry = entries[i];
|
||||
|
||||
builder.AppendLine($" Extra Field {i}:");
|
||||
builder.AppendLine(entry.SI1, " Subfield ID1");
|
||||
builder.AppendLine(entry.SI2, " Subfield ID2");
|
||||
builder.AppendLine(entry.LEN, " Length");
|
||||
builder.AppendLine(entry.SubfieldID1, " Subfield ID1");
|
||||
builder.AppendLine(entry.SubfieldID2, " Subfield ID2");
|
||||
builder.AppendLine(entry.Length, " Length");
|
||||
builder.AppendLine(entry.Data, " Data");
|
||||
}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ namespace SabreTools.Serialization.Printers
|
||||
}
|
||||
|
||||
builder.AppendLine(trailer.CRC32, " CRC-32");
|
||||
builder.AppendLine(trailer.ISIZE, " Input size");
|
||||
builder.AppendLine(trailer.InputSize, " Input size");
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace SabreTools.Serialization.Printers
|
||||
builder.AppendLine(header.BlockTableSizeLong, " Block table size long");
|
||||
builder.AppendLine(header.HiBlockTableSize, " Hi-block table size");
|
||||
builder.AppendLine(header.HetTableSize, " HET table size");
|
||||
builder.AppendLine(header.BetTablesize, " BET table size"); // TODO: Fix casing
|
||||
builder.AppendLine(header.BetTableSize, " BET table size");
|
||||
builder.AppendLine(header.RawChunkSize, " Raw chunk size");
|
||||
builder.AppendLine(header.BlockTableMD5, " Block table MD5");
|
||||
builder.AppendLine(header.HashTableMD5, " Hash table MD5");
|
||||
|
||||
@@ -16,12 +16,7 @@ namespace SabreTools.Serialization.Printers
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder,
|
||||
archive.LocalFileHeaders,
|
||||
archive.EncryptionHeaders,
|
||||
archive.FileData,
|
||||
archive.DataDescriptors,
|
||||
archive.ZIP64DataDescriptors);
|
||||
Print(builder, archive.LocalFiles);
|
||||
Print(builder, archive.EndOfCentralDirectoryRecord);
|
||||
Print(builder, archive.ZIP64EndOfCentralDirectoryLocator);
|
||||
Print(builder, archive.ZIP64EndOfCentralDirectoryRecord);
|
||||
@@ -29,78 +24,77 @@ namespace SabreTools.Serialization.Printers
|
||||
Print(builder, archive.ArchiveExtraDataRecord);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder,
|
||||
LocalFileHeader[]? localFileHeaders,
|
||||
byte[][]? encryptionHeaders,
|
||||
byte[][]? fileData,
|
||||
DataDescriptor[]? dataDescriptors,
|
||||
DataDescriptor64[]? zip64DataDescriptors)
|
||||
private static void Print(StringBuilder builder, LocalFile[]? localFiles)
|
||||
{
|
||||
builder.AppendLine(" Local File Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (localFileHeaders == null || localFileHeaders.Length == 0)
|
||||
if (localFiles == null || localFiles.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No local files");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
if (encryptionHeaders == null || localFileHeaders.Length > encryptionHeaders.Length
|
||||
|| fileData == null || localFileHeaders.Length > fileData.Length)
|
||||
for (int i = 0; i < localFiles.Length; i++)
|
||||
{
|
||||
builder.AppendLine(" Mismatch in local file array values");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
for (int i = 0; i < localFileHeaders.Length; i++)
|
||||
{
|
||||
var localFileHeader = localFileHeaders[i];
|
||||
var encryptionHeader = encryptionHeaders != null && i < encryptionHeaders.Length ? encryptionHeaders[i] : null;
|
||||
var fileDatum = fileData != null && i < fileData.Length ? fileData[i] : null;
|
||||
var dataDescriptor = dataDescriptors != null && i < dataDescriptors.Length ? dataDescriptors[i] : null;
|
||||
var zip64DataDescriptor = zip64DataDescriptors != null && i < zip64DataDescriptors.Length ? zip64DataDescriptors[i] : null;
|
||||
|
||||
Print(builder, localFileHeader, encryptionHeader, fileDatum, dataDescriptor, zip64DataDescriptor, i);
|
||||
var localFile = localFiles[i];
|
||||
Print(builder, localFile, i);
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder,
|
||||
LocalFileHeader localFileHeader,
|
||||
byte[]? encryptionHeader,
|
||||
byte[]? fileData,
|
||||
DataDescriptor? dataDescriptor,
|
||||
DataDescriptor64? zip64DataDescriptor,
|
||||
int index)
|
||||
private static void Print(StringBuilder builder, LocalFile localFile, int index)
|
||||
{
|
||||
builder.AppendLine($" Local File Entry {index}");
|
||||
builder.AppendLine(localFileHeader.Signature, " [Local File Header] Signature");
|
||||
builder.AppendLine(localFileHeader.Version, " [Local File Header] Version");
|
||||
builder.AppendLine($" [Local File Header] Flags: {localFileHeader.Flags} (0x{localFileHeader.Flags:X})");
|
||||
builder.AppendLine($" [Local File Header] Compression method: {localFileHeader.CompressionMethod} (0x{localFileHeader.CompressionMethod:X})");
|
||||
builder.AppendLine(localFileHeader.LastModifedFileTime, " [Local File Header] Last modified file time"); // TODO: Parse from MS-DOS
|
||||
builder.AppendLine(localFileHeader.LastModifiedFileDate, " [Local File Header] Last modified file date"); // TODO: Parse from MS-DOS
|
||||
builder.AppendLine(localFileHeader.CRC32, " [Local File Header] CRC-32");
|
||||
builder.AppendLine(localFileHeader.CompressedSize, " [Local File Header] Compressed size");
|
||||
builder.AppendLine(localFileHeader.UncompressedSize, " [Local File Header] Uncompressed size");
|
||||
builder.AppendLine(localFileHeader.FileNameLength, " [Local File Header] File name length");
|
||||
builder.AppendLine(localFileHeader.ExtraFieldLength, " [Local File Header] Extra field length");
|
||||
builder.AppendLine(localFileHeader.FileName, " [Local File Header] File name");
|
||||
|
||||
var extraFields = Deserializers.PKZIP.ParseExtraFields(localFileHeader, localFileHeader.ExtraField);
|
||||
Print(builder, " [Local File Header] Extra Fields", extraFields);
|
||||
#region Local File Header
|
||||
|
||||
if (encryptionHeader == null)
|
||||
var localFileHeader = localFile.LocalFileHeader;
|
||||
if (localFileHeader == null)
|
||||
{
|
||||
builder.AppendLine(" [Encryption Header]: [NULL]");
|
||||
builder.AppendLine(" [Local File Header] [NULL]");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(encryptionHeader.Length, " [Encryption Header] Length");
|
||||
builder.AppendLine(encryptionHeader, " [Encryption Header] Data");
|
||||
builder.AppendLine(localFileHeader.Signature, " [Local File Header] Signature");
|
||||
builder.AppendLine(localFileHeader.Version, " [Local File Header] Version");
|
||||
builder.AppendLine($" [Local File Header] Flags: {localFileHeader.Flags} (0x{localFileHeader.Flags:X})");
|
||||
builder.AppendLine($" [Local File Header] Compression method: {localFileHeader.CompressionMethod} (0x{localFileHeader.CompressionMethod:X})");
|
||||
builder.AppendLine(localFileHeader.LastModifedFileTime, " [Local File Header] Last modified file time"); // TODO: Parse from MS-DOS
|
||||
builder.AppendLine(localFileHeader.LastModifiedFileDate, " [Local File Header] Last modified file date"); // TODO: Parse from MS-DOS
|
||||
builder.AppendLine(localFileHeader.CRC32, " [Local File Header] CRC-32");
|
||||
builder.AppendLine(localFileHeader.CompressedSize, " [Local File Header] Compressed size");
|
||||
builder.AppendLine(localFileHeader.UncompressedSize, " [Local File Header] Uncompressed size");
|
||||
builder.AppendLine(localFileHeader.FileNameLength, " [Local File Header] File name length");
|
||||
builder.AppendLine(localFileHeader.ExtraFieldLength, " [Local File Header] Extra field length");
|
||||
builder.AppendLine(localFileHeader.FileName, " [Local File Header] File name");
|
||||
|
||||
// TODO: Reenable this when models are fixed
|
||||
// var extraFields = Deserializers.PKZIP.ParseExtraFields(localFileHeader, localFileHeader.ExtraField);
|
||||
// Print(builder, " [Local File Header] Extra Fields", extraFields);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Encryption Headers
|
||||
|
||||
var encryptionHeaders = localFile.EncryptionHeaders;
|
||||
if (encryptionHeaders == null)
|
||||
{
|
||||
builder.AppendLine(" [Encryption Headers]: [NULL]");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(encryptionHeaders.Length, " [Encryption Headers] Length");
|
||||
builder.AppendLine(encryptionHeaders, " [Encryption Headers] Data");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region File Data
|
||||
|
||||
var fileData = localFile.FileData;
|
||||
if (fileData == null)
|
||||
{
|
||||
builder.AppendLine(" [File Data]: [NULL]");
|
||||
@@ -111,6 +105,11 @@ namespace SabreTools.Serialization.Printers
|
||||
//builder.AppendLine(fileData, " [File Data] Data");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data Descriptor
|
||||
|
||||
var dataDescriptor = localFile.DataDescriptor;
|
||||
if (dataDescriptor == null)
|
||||
{
|
||||
builder.AppendLine(" [Data Descriptor]: [NULL]");
|
||||
@@ -123,6 +122,7 @@ namespace SabreTools.Serialization.Printers
|
||||
builder.AppendLine(dataDescriptor.UncompressedSize, $" [Data Descriptor] Uncompressed size");
|
||||
}
|
||||
|
||||
var zip64DataDescriptor = localFile.ZIP64DataDescriptor;
|
||||
if (zip64DataDescriptor == null)
|
||||
{
|
||||
builder.AppendLine(" [ZIP64 Data Descriptor]: [NULL]");
|
||||
@@ -134,6 +134,8 @@ namespace SabreTools.Serialization.Printers
|
||||
builder.AppendLine(zip64DataDescriptor.CompressedSize, $" [ZIP64 Data Descriptor] Compressed size");
|
||||
builder.AppendLine(zip64DataDescriptor.UncompressedSize, $" [ZIP64 Data Descriptor] Uncompressed size");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, EndOfCentralDirectoryRecord? record)
|
||||
@@ -240,8 +242,9 @@ namespace SabreTools.Serialization.Printers
|
||||
builder.AppendLine(entry.FileName, " File name");
|
||||
builder.AppendLine(entry.FileComment, " File comment");
|
||||
|
||||
var extraFields = Deserializers.PKZIP.ParseExtraFields(entry, entry.ExtraField);
|
||||
Print(builder, " Extra Fields", extraFields);
|
||||
// TODO: Reenable this when models are fixed
|
||||
// var extraFields = Deserializers.PKZIP.ParseExtraFields(entry, entry.ExtraField);
|
||||
// Print(builder, " Extra Fields", extraFields);
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
@@ -312,7 +315,7 @@ namespace SabreTools.Serialization.Printers
|
||||
case DataStreamAlignment field: Print(builder, field); break;
|
||||
case MicrosoftOpenPackagingGrowthHint field: Print(builder, field); break;
|
||||
|
||||
default: Print(builder, entry); break;
|
||||
case UnknownExtraField field: Print(builder, field); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,35 +332,31 @@ namespace SabreTools.Serialization.Printers
|
||||
|
||||
private static void Print(StringBuilder builder, OS2ExtraField field)
|
||||
{
|
||||
builder.AppendLine(field.BSize, " Uncompressed block size");
|
||||
builder.AppendLine(field.CType, " Compression type");
|
||||
builder.AppendLine(field.EACRC, " CRC-32");
|
||||
builder.AppendLine(field.Var, " Data");
|
||||
builder.AppendLine(field.UncompressedBlockSize, " Uncompressed block size");
|
||||
builder.AppendLine(field.CompressionType, " Compression type");
|
||||
builder.AppendLine(field.CRC32, " CRC-32");
|
||||
builder.AppendLine(field.Data, " Data");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, NTFSExtraField field)
|
||||
{
|
||||
builder.AppendLine(field.Reserved, " Reserved");
|
||||
builder.AppendLine(field.Tags, " Tags");
|
||||
builder.AppendLine(field.Size, " Size");
|
||||
builder.AppendLine(field.Vars, " Vars");
|
||||
Print(builder, field.TagSizeVars);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, OpenVMSExtraField field)
|
||||
{
|
||||
builder.AppendLine(field.CRC, " CRC-32");
|
||||
builder.AppendLine(field.Tags, " Tags");
|
||||
builder.AppendLine(field.Size, " Size");
|
||||
builder.AppendLine(field.Vars, " Vars");
|
||||
Print(builder, field.TagSizeVars);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, UnixExtraField field)
|
||||
{
|
||||
builder.AppendLine(field.Atime, " File last access time");
|
||||
builder.AppendLine(field.Mtime, " File last modification time");
|
||||
builder.AppendLine(field.Uid, " File user ID");
|
||||
builder.AppendLine(field.Gid, " File group ID");
|
||||
builder.AppendLine(field.Var, " Data");
|
||||
builder.AppendLine(field.FileLastAccessTime, " File last access time");
|
||||
builder.AppendLine(field.FileLastModificationTime, " File last modification time");
|
||||
builder.AppendLine(field.FileUserID, " File user ID");
|
||||
builder.AppendLine(field.FileGroupID, " File group ID");
|
||||
builder.AppendLine(field.Data, " Data");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, PatchDescriptorExtraField field)
|
||||
@@ -396,9 +395,7 @@ namespace SabreTools.Serialization.Printers
|
||||
|
||||
private static void Print(StringBuilder builder, RecordManagementControls field)
|
||||
{
|
||||
builder.AppendLine(field.Tags, " Tags");
|
||||
builder.AppendLine(field.Size, " Size");
|
||||
builder.AppendLine(field.Vars, " Vars");
|
||||
Print(builder, field.TagSizeVars);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, PKCS7EncryptionRecipientCertificateList field)
|
||||
@@ -487,9 +484,29 @@ namespace SabreTools.Serialization.Printers
|
||||
builder.AppendLine(field.Padding, " Padding");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ExtensibleDataField field)
|
||||
private static void Print(StringBuilder builder, UnknownExtraField field)
|
||||
{
|
||||
// TODO: Print byte data here
|
||||
builder.AppendLine(field.Data, " Data");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, TagSizeVar[]? tuples)
|
||||
{
|
||||
builder.AppendLine(" Tag/Size/Var Tuples:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (tuples == null)
|
||||
{
|
||||
builder.AppendLine(" No tuples");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < tuples.Length; i++)
|
||||
{
|
||||
var tuple = tuples[i];
|
||||
|
||||
builder.AppendLine(tuple.Tag, " Tags");
|
||||
builder.AppendLine(tuple.Size, " Size");
|
||||
builder.AppendLine(tuple.Var, " Vars");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -6,6 +6,8 @@ using SabreTools.ASN1;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Models.PortableExecutable;
|
||||
using SabreTools.Models.PortableExecutable.COFFSymbolTableEntries;
|
||||
using SabreTools.Models.PortableExecutable.ResourceEntries;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
@@ -310,7 +312,7 @@ namespace SabreTools.Serialization.Printers
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, COFFSymbolTableEntry[]? entries)
|
||||
private static void Print(StringBuilder builder, BaseEntry[]? entries)
|
||||
{
|
||||
builder.AppendLine(" COFF Symbol Table Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
@@ -321,127 +323,99 @@ namespace SabreTools.Serialization.Printers
|
||||
return;
|
||||
}
|
||||
|
||||
int auxSymbolsRemaining = 0;
|
||||
int currentSymbolType = 0;
|
||||
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
|
||||
builder.AppendLine($" COFF Symbol Table Entry {i} (Subtype {currentSymbolType})");
|
||||
if (currentSymbolType == 0)
|
||||
switch (entry)
|
||||
{
|
||||
if (entry.ShortName != null)
|
||||
{
|
||||
builder.AppendLine(entry.ShortName, " Short name", Encoding.ASCII);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(entry.Zeroes, " Zeroes");
|
||||
builder.AppendLine(entry.Offset, " Offset");
|
||||
}
|
||||
builder.AppendLine(entry.Value, " Value");
|
||||
builder.AppendLine(entry.SectionNumber, " Section number");
|
||||
builder.AppendLine($" Symbol type: {entry.SymbolType} (0x{entry.SymbolType:X})");
|
||||
builder.AppendLine($" Storage class: {entry.StorageClass} (0x{entry.StorageClass:X})");
|
||||
builder.AppendLine(entry.NumberOfAuxSymbols, " Number of aux symbols");
|
||||
|
||||
auxSymbolsRemaining = entry.NumberOfAuxSymbols;
|
||||
if (auxSymbolsRemaining == 0)
|
||||
continue;
|
||||
|
||||
if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL
|
||||
&& entry.SymbolType == SymbolType.IMAGE_SYM_TYPE_FUNC
|
||||
&& entry.SectionNumber > 0)
|
||||
{
|
||||
currentSymbolType = 1;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FUNCTION
|
||||
&& entry.ShortName != null
|
||||
&& ((entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x62 && entry.ShortName[2] == 0x66) // .bf
|
||||
|| (entry.ShortName[0] == 0x2E && entry.ShortName[1] == 0x65 && entry.ShortName[2] == 0x66))) // .ef
|
||||
{
|
||||
currentSymbolType = 2;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_EXTERNAL
|
||||
&& entry.SectionNumber == (ushort)SectionNumber.IMAGE_SYM_UNDEFINED
|
||||
&& entry.Value == 0)
|
||||
{
|
||||
currentSymbolType = 3;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_FILE)
|
||||
{
|
||||
// TODO: Symbol name should be ".file"
|
||||
currentSymbolType = 4;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_STATIC)
|
||||
{
|
||||
// TODO: Should have the name of a section (like ".text")
|
||||
currentSymbolType = 5;
|
||||
}
|
||||
else if (entry.StorageClass == StorageClass.IMAGE_SYM_CLASS_CLR_TOKEN)
|
||||
{
|
||||
currentSymbolType = 6;
|
||||
}
|
||||
case StandardRecord item: Print(builder, item, i); break;
|
||||
case FunctionDefinition item: Print(builder, item, i); break;
|
||||
case Descriptor item: Print(builder, item, i); break;
|
||||
case WeakExternal item: Print(builder, item, i); break;
|
||||
case FileRecord item: Print(builder, item, i); break;
|
||||
case SectionDefinition item: Print(builder, item, i); break;
|
||||
case CLRTokenDefinition item: Print(builder, item, i); break;
|
||||
}
|
||||
else if (currentSymbolType == 1)
|
||||
{
|
||||
builder.AppendLine(entry.AuxFormat1TagIndex, " Tag index");
|
||||
builder.AppendLine(entry.AuxFormat1TotalSize, " Total size");
|
||||
builder.AppendLine(entry.AuxFormat1PointerToLinenumber, " Pointer to linenumber");
|
||||
builder.AppendLine(entry.AuxFormat1PointerToNextFunction, " Pointer to next function");
|
||||
builder.AppendLine(entry.AuxFormat1Unused, " Unused");
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
else if (currentSymbolType == 2)
|
||||
{
|
||||
builder.AppendLine(entry.AuxFormat2Unused1, " Unused");
|
||||
builder.AppendLine(entry.AuxFormat2Linenumber, " Linenumber");
|
||||
builder.AppendLine(entry.AuxFormat2Unused2, " Unused");
|
||||
builder.AppendLine(entry.AuxFormat2PointerToNextFunction, " Pointer to next function");
|
||||
builder.AppendLine(entry.AuxFormat2Unused3, " Unused");
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
else if (currentSymbolType == 3)
|
||||
{
|
||||
builder.AppendLine(entry.AuxFormat3TagIndex, " Tag index");
|
||||
builder.AppendLine(entry.AuxFormat3Characteristics, " Characteristics");
|
||||
builder.AppendLine(entry.AuxFormat3Unused, " Unused");
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
else if (currentSymbolType == 4)
|
||||
{
|
||||
builder.AppendLine(entry.AuxFormat4FileName, " File name", Encoding.ASCII);
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
else if (currentSymbolType == 5)
|
||||
{
|
||||
builder.AppendLine(entry.AuxFormat5Length, " Length");
|
||||
builder.AppendLine(entry.AuxFormat5NumberOfRelocations, " Number of relocations");
|
||||
builder.AppendLine(entry.AuxFormat5NumberOfLinenumbers, " Number of linenumbers");
|
||||
builder.AppendLine(entry.AuxFormat5CheckSum, " Checksum");
|
||||
builder.AppendLine(entry.AuxFormat5Number, " Number");
|
||||
builder.AppendLine(entry.AuxFormat5Selection, " Selection");
|
||||
builder.AppendLine(entry.AuxFormat5Unused, " Unused");
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
else if (currentSymbolType == 6)
|
||||
{
|
||||
builder.AppendLine(entry.AuxFormat6AuxType, " Aux type");
|
||||
builder.AppendLine(entry.AuxFormat6Reserved1, " Reserved");
|
||||
builder.AppendLine(entry.AuxFormat6SymbolTableIndex, " Symbol table index");
|
||||
builder.AppendLine(entry.AuxFormat6Reserved2, " Reserved");
|
||||
auxSymbolsRemaining--;
|
||||
}
|
||||
|
||||
// If we hit the last aux symbol, go back to normal format
|
||||
if (auxSymbolsRemaining == 0)
|
||||
currentSymbolType = 0;
|
||||
}
|
||||
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, StandardRecord entry, int i)
|
||||
{
|
||||
builder.AppendLine($" COFF Symbol Table Entry {i} (Standard Record)");
|
||||
if (entry.ShortName != null)
|
||||
{
|
||||
builder.AppendLine(entry.ShortName, " Short name", Encoding.ASCII);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(entry.Zeroes, " Zeroes");
|
||||
builder.AppendLine(entry.Offset, " Offset");
|
||||
}
|
||||
builder.AppendLine(entry.Value, " Value");
|
||||
builder.AppendLine(entry.SectionNumber, " Section number");
|
||||
builder.AppendLine($" Symbol type: {entry.SymbolType} (0x{entry.SymbolType:X})");
|
||||
builder.AppendLine($" Storage class: {entry.StorageClass} (0x{entry.StorageClass:X})");
|
||||
builder.AppendLine(entry.NumberOfAuxSymbols, " Number of aux symbols");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, FunctionDefinition entry, int i)
|
||||
{
|
||||
builder.AppendLine($" COFF Symbol Table Entry {i} (Function Definition)");
|
||||
builder.AppendLine(entry.TagIndex, " Tag index");
|
||||
builder.AppendLine(entry.TotalSize, " Total size");
|
||||
builder.AppendLine(entry.PointerToLinenumber, " Pointer to linenumber");
|
||||
builder.AppendLine(entry.PointerToNextFunction, " Pointer to next function");
|
||||
builder.AppendLine(entry.Unused, " Unused");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Descriptor entry, int i)
|
||||
{
|
||||
builder.AppendLine($" COFF Symbol Table Entry {i} (.bf and .ef Symbol)");
|
||||
builder.AppendLine(entry.Unused1, " Unused");
|
||||
builder.AppendLine(entry.Linenumber, " Linenumber");
|
||||
builder.AppendLine(entry.Unused2, " Unused");
|
||||
builder.AppendLine(entry.PointerToNextFunction, " Pointer to next function");
|
||||
builder.AppendLine(entry.Unused3, " Unused");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, WeakExternal entry, int i)
|
||||
{
|
||||
builder.AppendLine($" COFF Symbol Table Entry {i} (Weak External)");
|
||||
builder.AppendLine(entry.TagIndex, " Tag index");
|
||||
builder.AppendLine(entry.Characteristics, " Characteristics");
|
||||
builder.AppendLine(entry.Unused, " Unused");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, FileRecord entry, int i)
|
||||
{
|
||||
builder.AppendLine($" COFF Symbol Table Entry {i} (File)");
|
||||
builder.AppendLine(entry.FileName, " File name", Encoding.ASCII);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, SectionDefinition entry, int i)
|
||||
{
|
||||
builder.AppendLine($" COFF Symbol Table Entry {i} (Section Defintion)");
|
||||
builder.AppendLine(entry.Length, " Length");
|
||||
builder.AppendLine(entry.NumberOfRelocations, " Number of relocations");
|
||||
builder.AppendLine(entry.NumberOfLinenumbers, " Number of linenumbers");
|
||||
builder.AppendLine(entry.CheckSum, " Checksum");
|
||||
builder.AppendLine(entry.Number, " Number");
|
||||
builder.AppendLine(entry.Selection, " Selection");
|
||||
builder.AppendLine(entry.Unused, " Unused");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, CLRTokenDefinition entry, int i)
|
||||
{
|
||||
builder.AppendLine($" COFF Symbol Table Entry {i} (CLR Token Defintion)");
|
||||
builder.AppendLine(entry.AuxFormat6AuxType, " Aux type");
|
||||
builder.AppendLine(entry.AuxFormat6Reserved1, " Reserved");
|
||||
builder.AppendLine(entry.AuxFormat6SymbolTableIndex, " Symbol table index");
|
||||
builder.AppendLine(entry.AuxFormat6Reserved2, " Reserved");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, COFFStringTable? stringTable)
|
||||
{
|
||||
builder.AppendLine(" COFF String Table Information:");
|
||||
|
||||
@@ -20,20 +20,18 @@ namespace SabreTools.Serialization.Printers
|
||||
Print(builder, file.Entries);
|
||||
}
|
||||
|
||||
// TODO: Fix the entry type when Models is updated
|
||||
//private static void Print(StringBuilder builder, Entry[]? entries)
|
||||
private static void Print(StringBuilder builder, List<Entry>? entries)
|
||||
private static void Print(StringBuilder builder, Entry[]? entries)
|
||||
{
|
||||
builder.AppendLine(" Entries Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (entries == null || entries.Count == 0)
|
||||
if (entries == null || entries.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No entries");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < entries.Count; i++)
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
|
||||
@@ -75,20 +73,18 @@ namespace SabreTools.Serialization.Printers
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
// TODO: Fix the entry type when Models is updated
|
||||
// private static void Print(StringBuilder builder, Block[]? entries)
|
||||
private static void Print(StringBuilder builder, List<Block>? entries)
|
||||
private static void Print(StringBuilder builder, Block[]? entries)
|
||||
{
|
||||
builder.AppendLine(" Blocks:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (entries == null || entries.Count == 0)
|
||||
if (entries == null || entries.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No blocks");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < entries.Count; i++)
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
var block = entries[i];
|
||||
if (block.Data == null)
|
||||
|
||||
58
SabreTools.Serialization/Printers/WiseOverlayHeader.cs
Normal file
58
SabreTools.Serialization/Printers/WiseOverlayHeader.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.Text;
|
||||
using SabreTools.Models.WiseInstaller;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
public class WiseOverlayHeader : IPrinter<OverlayHeader>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder, OverlayHeader model)
|
||||
=> Print(builder, model);
|
||||
|
||||
public static void Print(StringBuilder builder, OverlayHeader overlayHeader)
|
||||
{
|
||||
#if NET20 || NET35
|
||||
bool pkzip = (overlayHeader.Flags & OverlayHeaderFlags.WISE_FLAG_PK_ZIP) != 0;
|
||||
#else
|
||||
bool pkzip = overlayHeader.Flags.HasFlag(OverlayHeaderFlags.WISE_FLAG_PK_ZIP);
|
||||
#endif
|
||||
|
||||
builder.AppendLine("Wise Installer Overlay Header Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine(overlayHeader.DllNameLen, "DLL name length");
|
||||
builder.AppendLine(overlayHeader.DllName, "DLL name");
|
||||
builder.AppendLine(overlayHeader.DllSize, "DLL size");
|
||||
builder.AppendLine($"Flags: {overlayHeader.Flags} (0x{(uint)overlayHeader.Flags:X4})");
|
||||
builder.AppendLine(pkzip, " Uses PKZIP containers");
|
||||
builder.AppendLine(overlayHeader.GraphicsData, "Graphics data");
|
||||
builder.AppendLine(overlayHeader.WiseScriptExitEventOffset, "Wise script exit event offset");
|
||||
builder.AppendLine(overlayHeader.WiseScriptCancelEventOffset, "Wise script cancel event offset");
|
||||
builder.AppendLine(overlayHeader.WiseScriptInflatedSize, "Wise script inflated size");
|
||||
builder.AppendLine(overlayHeader.WiseScriptDeflatedSize, "Wise script deflated size");
|
||||
builder.AppendLine(overlayHeader.WiseDllDeflatedSize, "Wise DLL deflated size");
|
||||
builder.AppendLine(overlayHeader.Ctl3d32DeflatedSize, "CTL3D32.DLL deflated size");
|
||||
builder.AppendLine(overlayHeader.SomeData4DeflatedSize, "FILE0004 deflated size");
|
||||
builder.AppendLine(overlayHeader.RegToolDeflatedSize, "Ocxreg32.EXE deflated size");
|
||||
builder.AppendLine(overlayHeader.ProgressDllDeflatedSize, "PROGRESS.DLL deflated size");
|
||||
builder.AppendLine(overlayHeader.SomeData7DeflatedSize, "FILE0007 deflated size");
|
||||
builder.AppendLine(overlayHeader.SomeData8DeflatedSize, "FILE0008 deflated size");
|
||||
builder.AppendLine(overlayHeader.SomeData9DeflatedSize, "FILE0009 deflated size");
|
||||
builder.AppendLine(overlayHeader.SomeData10DeflatedSize, "FILE000A deflated size");
|
||||
builder.AppendLine(overlayHeader.FinalFileDeflatedSize, "FILE000{n}.DAT deflated size");
|
||||
builder.AppendLine(overlayHeader.FinalFileInflatedSize, "FILE000{n}.DAT inflated size");
|
||||
builder.AppendLine(overlayHeader.EOF, "EOF");
|
||||
builder.AppendLine(overlayHeader.DibDeflatedSize, "DIB deflated size");
|
||||
builder.AppendLine(overlayHeader.DibInflatedSize, "DIB inflated size");
|
||||
builder.AppendLine(overlayHeader.InstallScriptDeflatedSize, "Install script deflated size");
|
||||
if (overlayHeader.CharacterSet != null)
|
||||
builder.AppendLine($"Character set: {overlayHeader.CharacterSet} (0x{(uint)overlayHeader.CharacterSet:X4})");
|
||||
else
|
||||
builder.AppendLine((uint?)null, $"Character set");
|
||||
builder.AppendLine($"Endianness: {overlayHeader.Endianness} (0x{(uint)overlayHeader.Endianness:X4})");
|
||||
builder.AppendLine(overlayHeader.InitTextLen, "Init text length");
|
||||
builder.AppendLine(overlayHeader.InitText, "Init text");
|
||||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
839
SabreTools.Serialization/Printers/WiseScript.cs
Normal file
839
SabreTools.Serialization/Printers/WiseScript.cs
Normal file
@@ -0,0 +1,839 @@
|
||||
using System.Text;
|
||||
using SabreTools.Models.WiseInstaller;
|
||||
using SabreTools.Models.WiseInstaller.Actions;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
public class WiseScript : IPrinter<ScriptFile>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder, ScriptFile model)
|
||||
=> Print(builder, model);
|
||||
|
||||
public static void Print(StringBuilder builder, ScriptFile scriptFile)
|
||||
{
|
||||
builder.AppendLine("Wise Installer Script File Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, scriptFile.Header);
|
||||
Print(builder, scriptFile.States);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ScriptHeader? header)
|
||||
{
|
||||
builder.AppendLine(" Header Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (header == null)
|
||||
{
|
||||
builder.AppendLine(" No header");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.Flags, " Flags");
|
||||
builder.AppendLine(header.UnknownU16_1, " UnknownU16_1");
|
||||
builder.AppendLine(header.UnknownU16_2, " UnknownU16_2");
|
||||
builder.AppendLine(header.SomeOffset1, " SomeOffset1");
|
||||
builder.AppendLine(header.SomeOffset2, " SomeOffset2");
|
||||
builder.AppendLine(header.UnknownBytes_2, " UnknownBytes_2");
|
||||
builder.AppendLine(header.DateTime, " Datetime");
|
||||
builder.AppendLine(header.VariableLengthData, " Variable length data");
|
||||
builder.AppendLine(header.FTPURL, " FTP URL");
|
||||
builder.AppendLine(header.LogPathname, " Log pathname");
|
||||
builder.AppendLine(header.MessageFont, " Font");
|
||||
builder.AppendLine(header.FontSize, " Font size");
|
||||
builder.AppendLine(header.Unknown_2, " Unknown_2");
|
||||
builder.AppendLine(header.LanguageCount, " Language count");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine(" Header strings");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (header.HeaderStrings == null || header.HeaderStrings.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No header strings");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < header.HeaderStrings.Length; i++)
|
||||
{
|
||||
var entry = header.HeaderStrings[i];
|
||||
builder.AppendLine($" Header String {i}: {entry}");
|
||||
}
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, MachineState[]? entries)
|
||||
{
|
||||
builder.AppendLine(" State Machine Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (entries == null || entries.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No state machine items");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < entries.Length; i++)
|
||||
{
|
||||
var entry = entries[i];
|
||||
|
||||
builder.AppendLine($" State Machine Entry {i}:");
|
||||
builder.AppendLine($" Op: {entry.Op} (0x{(byte)entry.Op:X2})");
|
||||
switch (entry.Data)
|
||||
{
|
||||
case InstallFile data: Print(builder, data); break;
|
||||
case NoOp data: Print(builder, data); break;
|
||||
case DisplayMessage data: Print(builder, data); break;
|
||||
case UserDefinedActionStep data: Print(builder, data); break;
|
||||
case EditIniFile data: Print(builder, data); break;
|
||||
case DisplayBillboard data: Print(builder, data); break;
|
||||
case ExecuteProgram data: Print(builder, data); break;
|
||||
case EndBlockStatement data: Print(builder, data); break;
|
||||
case CallDllFunction data: Print(builder, data); break;
|
||||
case EditRegistry data: Print(builder, data); break;
|
||||
case DeleteFile data: Print(builder, data); break;
|
||||
case IfWhileStatement data: Print(builder, data); break;
|
||||
case ElseStatement data: Print(builder, data); break;
|
||||
case StartUserDefinedAction data: Print(builder, data); break;
|
||||
case EndUserDefinedAction data: Print(builder, data); break;
|
||||
case CreateDirectory data: Print(builder, data); break;
|
||||
case CopyLocalFile data: Print(builder, data); break;
|
||||
case CustomDialogSet data: Print(builder, data); break;
|
||||
case GetSystemInformation data: Print(builder, data); break;
|
||||
case GetTemporaryFilename data: Print(builder, data); break;
|
||||
case PlayMultimediaFile data: Print(builder, data); break;
|
||||
case NewEvent data: Print(builder, data); break;
|
||||
case Unknown0x19 data: Print(builder, data); break;
|
||||
case ConfigODBCDataSource data: Print(builder, data); break;
|
||||
case IncludeScript data: Print(builder, data); break;
|
||||
case AddTextToInstallLog data: Print(builder, data); break;
|
||||
case RenameFileDirectory data: Print(builder, data); break;
|
||||
case OpenCloseInstallLog data: Print(builder, data); break;
|
||||
case ElseIfStatement data: Print(builder, data); break;
|
||||
case Unknown0x24 data: Print(builder, data); break;
|
||||
case Unknown0x25 data: Print(builder, data); break;
|
||||
|
||||
// Should never happen
|
||||
case InvalidOperation data: Print(builder, data); break;
|
||||
default: builder.AppendLine(" Data: [NULL]"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region State Actions
|
||||
|
||||
private static void Print(StringBuilder builder, InstallFile data)
|
||||
{
|
||||
builder.AppendLine($" Data: InstallFile");
|
||||
builder.AppendLine(data.Flags, $" Flags");
|
||||
builder.AppendLine(data.DeflateStart, $" Deflate start");
|
||||
builder.AppendLine(data.DeflateEnd, $" Deflate end");
|
||||
builder.AppendLine(data.Date, $" Date");
|
||||
builder.AppendLine(data.Time, $" Time");
|
||||
builder.AppendLine(data.InflatedSize, $" Inflated size");
|
||||
builder.AppendLine(data.Operand_7, $" Unknown");
|
||||
builder.AppendLine(data.Crc32, $" CRC-32");
|
||||
builder.AppendLine(data.DestinationPathname, $" Destination pathname");
|
||||
builder.AppendLine($" File texts");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (data.Description == null || data.Description.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No file texts");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < data.Description.Length; i++)
|
||||
{
|
||||
var entry = data.Description[i];
|
||||
builder.AppendLine($" File Text {i}: {entry}");
|
||||
}
|
||||
}
|
||||
builder.AppendLine(data.Source, $" Source");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, NoOp data)
|
||||
{
|
||||
builder.AppendLine($" Data: NoOp");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, DisplayMessage data)
|
||||
{
|
||||
builder.AppendLine($" Data: DisplayMessage");
|
||||
builder.AppendLine(data.Flags, $" Flags");
|
||||
builder.AppendLine($" Title/Text strings");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (data.TitleText == null || data.TitleText.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No title/text strings");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < data.TitleText.Length; i++)
|
||||
{
|
||||
var entry = data.TitleText[i];
|
||||
builder.AppendLine($" Title/Text String {i}: {entry}");
|
||||
}
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, UserDefinedActionStep data)
|
||||
{
|
||||
builder.AppendLine($" Data: UserDefinedActionStep");
|
||||
builder.AppendLine(data.Flags, $" Count");
|
||||
builder.AppendLine($" Script lines");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (data.ScriptLines == null || data.ScriptLines.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No script lines");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < data.ScriptLines.Length; i++)
|
||||
{
|
||||
var entry = data.ScriptLines[i];
|
||||
builder.AppendLine($" Script Line {i}: {entry}");
|
||||
}
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, EditIniFile data)
|
||||
{
|
||||
builder.AppendLine($" Data: EditIniFile");
|
||||
builder.AppendLine(data.Pathname, $" Pathname");
|
||||
builder.AppendLine(data.Section, $" Section");
|
||||
builder.AppendLine(data.Values, $" Values");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, DisplayBillboard data)
|
||||
{
|
||||
builder.AppendLine($" Data: DisplayBillboard");
|
||||
builder.AppendLine(data.Flags, $" Flags");
|
||||
|
||||
builder.AppendLine($" Deflate info:");
|
||||
builder.AppendLine($" -------------------------");
|
||||
if (data.DeflateInfo == null || data.DeflateInfo.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No deflate info items");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < data.DeflateInfo.Length; i++)
|
||||
{
|
||||
var entry = data.DeflateInfo[i];
|
||||
Print(builder, entry, 8, i);
|
||||
}
|
||||
}
|
||||
|
||||
builder.AppendLine(data.Terminator, $" Terminator");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ExecuteProgram data)
|
||||
{
|
||||
builder.AppendLine($" Data: ExecuteProgram");
|
||||
builder.AppendLine(data.Flags, $" Flags");
|
||||
builder.AppendLine(data.Pathname, $" Pathname");
|
||||
builder.AppendLine(data.CommandLine, $" Command Line");
|
||||
builder.AppendLine(data.DefaultDirectory, $" Default Directory");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, EndBlockStatement data)
|
||||
{
|
||||
builder.AppendLine($" Data: EndBlockStatement");
|
||||
builder.AppendLine(data.Operand_1, $" Operand 1");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, CallDllFunction data)
|
||||
{
|
||||
builder.AppendLine($" Data: CallDllFunction");
|
||||
builder.AppendLine(data.Flags, $" Flags");
|
||||
builder.AppendLine(data.DllPath, $" DLL path");
|
||||
builder.AppendLine(data.FunctionName, $" Function name");
|
||||
builder.AppendLine(data.FunctionName.FromWiseFunctionId(), $" Derived action name");
|
||||
builder.AppendLine(data.Operand_4, $" Operand 4");
|
||||
builder.AppendLine(data.ReturnVariable, $" Return variable");
|
||||
builder.AppendLine($" Entries");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (data.Entries == null || data.Entries.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No entry data");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < data.Entries.Length; i++)
|
||||
{
|
||||
var entry = data.Entries[i];
|
||||
switch (entry)
|
||||
{
|
||||
case AddDirectoryToPath args: Print(builder, args, i); break;
|
||||
case AddToAutoexecBat args: Print(builder, args, i); break;
|
||||
case AddToConfigSys args: Print(builder, args, i); break;
|
||||
case AddToSystemIni args: Print(builder, args, i); break;
|
||||
case ReadIniValue args: Print(builder, args, i); break;
|
||||
case GetRegistryKeyValue args: Print(builder, args, i); break;
|
||||
case RegisterFont args: Print(builder, args, i); break;
|
||||
case Win32SystemDirectory args: Print(builder, args, i); break;
|
||||
case CheckConfiguration args: Print(builder, args, i); break;
|
||||
case SearchForFile args: Print(builder, args, i); break;
|
||||
case ReadWriteBinaryFile args: Print(builder, args, i); break;
|
||||
case SetVariable args: Print(builder, args, i); break;
|
||||
case GetEnvironmentVariable args: Print(builder, args, i); break;
|
||||
case CheckIfFileDirExists args: Print(builder, args, i); break;
|
||||
case SetFileAttributes args: Print(builder, args, i); break;
|
||||
case SetFilesBuffers args: Print(builder, args, i); break;
|
||||
case FindFileInPath args: Print(builder, args, i); break;
|
||||
case CheckDiskSpace args: Print(builder, args, i); break;
|
||||
case InsertLineIntoTextFile args: Print(builder, args, i); break;
|
||||
case ParseString args: Print(builder, args, i); break;
|
||||
case ExitInstallation args: Print(builder, args, i); break;
|
||||
case SelfRegisterOCXsDLLs args: Print(builder, args, i); break;
|
||||
case InstallDirectXComponents args: Print(builder, args, i); break;
|
||||
case WizardBlockLoop args: Print(builder, args, i); break;
|
||||
case ReadUpdateTextFile args: Print(builder, args, i); break;
|
||||
case PostToHttpServer args: Print(builder, args, i); break;
|
||||
case PromptForFilename args: Print(builder, args, i); break;
|
||||
case StartStopService args: Print(builder, args, i); break;
|
||||
case CheckHttpConnection args: Print(builder, args, i); break;
|
||||
case ExternalDllCall args: Print(builder, args, i); break;
|
||||
|
||||
// Should never happen
|
||||
default: builder.AppendLine($" Entry {i}: [NULL]"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, EditRegistry data)
|
||||
{
|
||||
builder.AppendLine($" Data: EditRegistry");
|
||||
builder.AppendLine(data.FlagsAndRoot, $" Flags and root");
|
||||
builder.AppendLine(data.DataType, $" Data type");
|
||||
builder.AppendLine(data.UnknownFsllib, $" Unknown");
|
||||
builder.AppendLine(data.Key, $" Key");
|
||||
builder.AppendLine(data.NewValue, $" New value");
|
||||
builder.AppendLine(data.ValueName, $" Value name");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, DeleteFile data)
|
||||
{
|
||||
builder.AppendLine($" Data: DeleteFile");
|
||||
builder.AppendLine(data.Flags, $" Flags");
|
||||
builder.AppendLine(data.Pathname, $" Pathname");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, IfWhileStatement data)
|
||||
{
|
||||
builder.AppendLine($" Data: IfWhileStatement");
|
||||
builder.AppendLine(data.Flags, $" Flags");
|
||||
builder.AppendLine(data.Variable, $" Variable");
|
||||
builder.AppendLine(data.Value, $" Value");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ElseStatement data)
|
||||
{
|
||||
builder.AppendLine($" Data: ElseStatement");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, StartUserDefinedAction data)
|
||||
{
|
||||
builder.AppendLine($" Data: StartUserDefinedAction");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, EndUserDefinedAction data)
|
||||
{
|
||||
builder.AppendLine($" Data: EndUserDefinedAction");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, CreateDirectory data)
|
||||
{
|
||||
builder.AppendLine($" Data: CreateDirectory");
|
||||
builder.AppendLine(data.Pathname, $" Pathname");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, CopyLocalFile data)
|
||||
{
|
||||
builder.AppendLine($" Data: CopyLocalFile");
|
||||
builder.AppendLine(data.Flags, $" Flags");
|
||||
builder.AppendLine(data.Padding, $" Padding");
|
||||
builder.AppendLine(data.Destination, $" Destination");
|
||||
builder.AppendLine($" Descriptions");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (data.Description == null || data.Description.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No descriptions");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < data.Description.Length; i++)
|
||||
{
|
||||
var entry = data.Description[i];
|
||||
builder.AppendLine($" Description {i}: {entry}");
|
||||
}
|
||||
}
|
||||
builder.AppendLine();
|
||||
builder.AppendLine(data.Source, $" Source");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, CustomDialogSet data)
|
||||
{
|
||||
builder.AppendLine($" Data: CustomDialogSet");
|
||||
builder.AppendLine(data.DeflateStart, $" Deflate start");
|
||||
builder.AppendLine(data.DeflateEnd, $" Deflate end");
|
||||
builder.AppendLine(data.InflatedSize, $" Inflated size");
|
||||
builder.AppendLine(data.DisplayVariable, $" Display variable");
|
||||
builder.AppendLine(data.Name, $" Name");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, GetSystemInformation data)
|
||||
{
|
||||
builder.AppendLine($" Data: GetSystemInformation");
|
||||
builder.AppendLine(data.Flags, $" Variable");
|
||||
builder.AppendLine(data.Variable, $" Variable");
|
||||
builder.AppendLine(data.Pathname, $" Pathname");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, GetTemporaryFilename data)
|
||||
{
|
||||
builder.AppendLine($" Data: GetTemporaryFilename");
|
||||
builder.AppendLine(data.Variable, $" Variable");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, PlayMultimediaFile data)
|
||||
{
|
||||
builder.AppendLine($" Data: PlayMultimediaFile");
|
||||
builder.AppendLine(data.Flags, $" Flags");
|
||||
builder.AppendLine(data.XPosition, $" X position");
|
||||
builder.AppendLine(data.YPosition, $" Y position");
|
||||
builder.AppendLine(data.Pathname, $" Pathname");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, NewEvent data)
|
||||
{
|
||||
builder.AppendLine($" Data: NewEvent");
|
||||
builder.AppendLine(data.Padding, $" Padding");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Unknown0x19 data)
|
||||
{
|
||||
builder.AppendLine($" Data: Unknown0x19");
|
||||
builder.AppendLine(data.Operand_1, $" Unknown");
|
||||
builder.AppendLine(data.Operand_2, $" Unknown");
|
||||
builder.AppendLine(data.Operand_3, $" Unknown");
|
||||
builder.AppendLine(data.Operand_4, $" Unknown");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ConfigODBCDataSource data)
|
||||
{
|
||||
builder.AppendLine($" Data: ConfigODBCDataSource");
|
||||
builder.AppendLine(data.Flags, $" Flags");
|
||||
builder.AppendLine(data.FileFormat, $" File format");
|
||||
builder.AppendLine(data.ConnectionString, $" Connection string");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, IncludeScript data)
|
||||
{
|
||||
builder.AppendLine($" Data: IncludeScript");
|
||||
builder.AppendLine(data.Count, $" Count");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, AddTextToInstallLog data)
|
||||
{
|
||||
builder.AppendLine($" Data: AddTextToInstallLog");
|
||||
builder.AppendLine(data.Text, $" Text");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, RenameFileDirectory data)
|
||||
{
|
||||
builder.AppendLine($" Data: RenameFileDirectory");
|
||||
builder.AppendLine(data.OldPathname, $" Old pathname");
|
||||
builder.AppendLine(data.NewFileName, $" New file name");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, OpenCloseInstallLog data)
|
||||
{
|
||||
builder.AppendLine($" Data: OpenCloseInstallLog");
|
||||
builder.AppendLine(data.Flags, $" Flags");
|
||||
builder.AppendLine(data.LogName, $" Log name");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ElseIfStatement data)
|
||||
{
|
||||
builder.AppendLine($" Data: ElseIfStatement");
|
||||
builder.AppendLine(data.Operator, $" Operator");
|
||||
builder.AppendLine(data.Variable, $" Variable");
|
||||
builder.AppendLine(data.Value, $" Value");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Unknown0x24 data)
|
||||
{
|
||||
builder.AppendLine($" Data: Unknown0x24");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Unknown0x25 data)
|
||||
{
|
||||
builder.AppendLine($" Data: Unknown0x25");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, InvalidOperation data)
|
||||
{
|
||||
builder.AppendLine($" Data: InvalidOperation");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Function Actions
|
||||
|
||||
private static void Print(StringBuilder builder, AddDirectoryToPath data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: AddDirectoryToPath");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.Directory, $" Directory");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, AddToAutoexecBat data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: AddToAutoexecBat");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.FileToEdit, $" File to edit");
|
||||
builder.AppendLine(data.TextToInsert, $" Text to insert");
|
||||
builder.AppendLine(data.SearchForText, $" Search for text");
|
||||
builder.AppendLine(data.CommentText, $" Comment text");
|
||||
builder.AppendLine(data.LineNumber, $" Line number");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, AddToConfigSys data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: AddToConfigSys");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.FileToEdit, $" File to edit");
|
||||
builder.AppendLine(data.TextToInsert, $" Text to insert");
|
||||
builder.AppendLine(data.SearchForText, $" Search for text");
|
||||
builder.AppendLine(data.CommentText, $" Comment text");
|
||||
builder.AppendLine(data.LineNumber, $" Line number");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, AddToSystemIni data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: AddToSystemIni");
|
||||
builder.AppendLine(data.DeviceName, $" Device name");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ReadIniValue data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: ReadIniValue");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.Variable, $" Variable");
|
||||
builder.AppendLine(data.Pathname, $" Pathname");
|
||||
builder.AppendLine(data.Section, $" Section");
|
||||
builder.AppendLine(data.Item, $" Item");
|
||||
builder.AppendLine(data.DefaultValue, $" Default value");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, GetRegistryKeyValue data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: GetRegistryKeyValue");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.Variable, $" Variable");
|
||||
builder.AppendLine(data.Key, $" Key");
|
||||
builder.AppendLine(data.Default, $" Default");
|
||||
builder.AppendLine(data.ValueName, $" Value name");
|
||||
builder.AppendLine(data.Root, $" Root");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, RegisterFont data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: RegisterFont");
|
||||
builder.AppendLine(data.FontFileName, $" Font file name");
|
||||
builder.AppendLine(data.FontName, $" Font name");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, Win32SystemDirectory data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: Win32SystemDirectory");
|
||||
builder.AppendLine(data.VariableName, $" Variable name");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, CheckConfiguration data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: CheckConfiguration");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.Message, $" Message");
|
||||
builder.AppendLine(data.Title, $" Title");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, SearchForFile data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: SearchForFile");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.Variable, $" Variable");
|
||||
builder.AppendLine(data.FileName, $" File name");
|
||||
builder.AppendLine(data.DefaultValue, $" Default value");
|
||||
builder.AppendLine(data.MessageText, $" Message text");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ReadWriteBinaryFile data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: ReadWriteBinaryFile");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.FilePathname, $" File pathname");
|
||||
builder.AppendLine(data.VariableName, $" Variable name");
|
||||
builder.AppendLine(data.FileOffset, $" File offset");
|
||||
builder.AppendLine(data.MaxLength, $" Max length");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, SetVariable data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: SetVariable");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.Variable, $" Variable");
|
||||
builder.AppendLine(data.Value, $" Value");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, GetEnvironmentVariable data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: GetEnvironmentVariable");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.Variable, $" Variable");
|
||||
builder.AppendLine(data.Environment, $" Environment");
|
||||
builder.AppendLine(data.DefaultValue, $" Default value");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, CheckIfFileDirExists data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: CheckIfFileDirExists");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.Pathname, $" Pathname");
|
||||
builder.AppendLine(data.Message, $" Message");
|
||||
builder.AppendLine(data.Title, $" Title");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, SetFileAttributes data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: SetFileAttributes");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.FilePathname, $" File pathname");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, SetFilesBuffers data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: SetFilesBuffers");
|
||||
builder.AppendLine(data.MinimumFiles, $" Minimum files");
|
||||
builder.AppendLine(data.MinimumBuffers, $" Minimum buffers");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, FindFileInPath data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: FindFileInPath");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.VariableName, $" Variable name");
|
||||
builder.AppendLine(data.FileName, $" File name");
|
||||
builder.AppendLine(data.DefaultValue, $" Default value");
|
||||
builder.AppendLine(data.SearchDirectories, $" Search directories");
|
||||
builder.AppendLine(data.Description, $" Description");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, CheckDiskSpace data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: CheckDiskSpace");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.ReserveSpace, $" Reserve space");
|
||||
builder.AppendLine(data.StatusVariable, $" Status variable");
|
||||
builder.AppendLine(data.ComponentVariables, $" Component variables");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, InsertLineIntoTextFile data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: InsertLineIntoTextFile");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.FileToEdit, $" File to edit");
|
||||
builder.AppendLine(data.TextToInsert, $" Text to insert");
|
||||
builder.AppendLine(data.SearchForText, $" Search for text");
|
||||
builder.AppendLine(data.CommentText, $" Comment text");
|
||||
builder.AppendLine(data.LineNumber, $" Line number");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ParseString data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: ParseString");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.Source, $" Source");
|
||||
builder.AppendLine(data.PatternPosition, $" Pattern position");
|
||||
builder.AppendLine(data.DestinationVariable1, $" Destination variable 1");
|
||||
builder.AppendLine(data.DestinationVariable2, $" Destination variable 2");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ExitInstallation data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: ExitInstallation");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, SelfRegisterOCXsDLLs data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: SelfRegisterOCXsDLLs");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.Description, $" Description");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, InstallDirectXComponents data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: InstallDirectXComponents");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.RootPath, $" Root path");
|
||||
builder.AppendLine(data.LibraryPath, $" Library path");
|
||||
builder.AppendLine(data.SizeOrOffsetOrFlag, $" Size or offset");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, WizardBlockLoop data, int i)
|
||||
{
|
||||
// TODO: Fix this when the model is updated
|
||||
builder.AppendLine($" Entry {i}: WizardBlockLoop");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.DirectionVariable, $" Direction variable");
|
||||
builder.AppendLine(data.DisplayVariable, $" Display variable");
|
||||
builder.AppendLine(data.XPosition, $" X position");
|
||||
builder.AppendLine(data.YPosition, $" Y position");
|
||||
builder.AppendLine(data.FillerColor, $" Filler color");
|
||||
builder.AppendLine(data.Operand_6, $" Operand 6");
|
||||
builder.AppendLine(data.Operand_7, $" Operand 7");
|
||||
builder.AppendLine(data.Operand_8, $" Operand 8");
|
||||
builder.AppendLine(data.DialogVariableValueCompare, $" Dialog variable value compare");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ReadUpdateTextFile data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: ReadUpdateTextFile");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.Variable, $" Variable");
|
||||
builder.AppendLine(data.Pathname, $" Pathname");
|
||||
builder.AppendLine(data.LanguageStrings, $" Language strings");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, PostToHttpServer data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: PostToHttpServer");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.URL, $" URL");
|
||||
builder.AppendLine(data.PostData, $" POST data");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, PromptForFilename data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: PromptForFilename");
|
||||
builder.AppendLine(data.DataFlags, $" Data flags");
|
||||
builder.AppendLine(data.DestinationVariable, $" Destination variable");
|
||||
builder.AppendLine(data.DefaultExtension, $" Default extension");
|
||||
builder.AppendLine(data.DialogTitle, $" Dialog title");
|
||||
builder.AppendLine(data.FilterList, $" Filter list");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, StartStopService data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: StartStopService");
|
||||
builder.AppendLine(data.Operation, $" Operation");
|
||||
builder.AppendLine(data.ServiceName, $" Service name");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, CheckHttpConnection data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: CheckHttpConnection");
|
||||
builder.AppendLine(data.UrlToCheck, $" URL to check");
|
||||
builder.AppendLine(data.Win32ErrorTextVariable, $" Win32 error text variable");
|
||||
builder.AppendLine(data.Win32ErrorNumberVariable, $" Win32 error number variable");
|
||||
builder.AppendLine(data.Win16ErrorTextVariable, $" Win16 error text variable");
|
||||
builder.AppendLine(data.Win16ErrorNumberVariable, $" Win16 error number variable");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ExternalDllCall data, int i)
|
||||
{
|
||||
builder.AppendLine($" Entry {i}: ExternalDllCall");
|
||||
if (data.Args == null)
|
||||
builder.AppendLine((string?)null, $" Args");
|
||||
else
|
||||
builder.AppendLine(string.Join(", ", data.Args), $" Args");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Additional
|
||||
|
||||
private static void Print(StringBuilder builder, DeflateEntry data, int indent, int index = -1)
|
||||
{
|
||||
string padding = string.Empty.PadLeft(indent, ' ');
|
||||
|
||||
if (index >= 0)
|
||||
builder.AppendLine($"{padding}Deflate info {index}");
|
||||
else
|
||||
builder.AppendLine($"{padding}Deflate info");
|
||||
|
||||
builder.AppendLine($"{padding}-------------------------");
|
||||
builder.AppendLine(data.DeflateStart, $"{padding} Deflate start");
|
||||
builder.AppendLine(data.DeflateEnd, $"{padding} Deflate end");
|
||||
builder.AppendLine(data.InflatedSize, $"{padding} Inflated size");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -65,8 +65,8 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.ASN1" Version="1.6.0" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.7.0" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.6.1" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.7.1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.7.0" />
|
||||
<PackageReference Include="SabreTools.Matching" Version="1.6.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.40.0" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
_dataOffset = 10;
|
||||
|
||||
// Add extra lengths
|
||||
_dataOffset += Header.XLEN;
|
||||
_dataOffset += Header.ExtraLength;
|
||||
if (Header.OriginalFileName != null)
|
||||
_dataOffset += Header.OriginalFileName.Length + 1;
|
||||
if (Header.FileComment != null)
|
||||
@@ -152,9 +152,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
}
|
||||
|
||||
// Ensure that DEFLATE is being used
|
||||
if (Header.CM != CompressionMethod.Deflate)
|
||||
if (Header.CompressionMethod != CompressionMethod.Deflate)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Invalid compression method {Header.CM} detected, only DEFLATE is supported. Skipping...");
|
||||
if (includeDebug) Console.Error.WriteLine($"Invalid compression method {Header.CompressionMethod} detected, only DEFLATE is supported. Skipping...");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,8 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <inheritdoc cref="Archive.ZIP64EndOfCentralDirectoryRecord"/>
|
||||
public EndOfCentralDirectoryRecord64? ZIP64EndOfCentralDirectoryRecord => Model.ZIP64EndOfCentralDirectoryRecord;
|
||||
|
||||
/// <inheritdoc cref="Archive.LocalFileHeaders"/>
|
||||
/// TODO: Use LocalFiles when Models is updated
|
||||
public LocalFileHeader[]? LocalFiles => Model.LocalFileHeaders;
|
||||
/// <inheritdoc cref="Archive.LocalFiles"/>
|
||||
public LocalFile[]? LocalFiles => Model.LocalFiles;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Text;
|
||||
using SabreTools.IO.Compression.zlib;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Models.PortableExecutable.ResourceEntries;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
@@ -763,12 +764,12 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <summary>
|
||||
/// Cached version info data
|
||||
/// </summary>
|
||||
private Models.PortableExecutable.VersionInfo? _versionInfo = null;
|
||||
private VersionInfo? _versionInfo = null;
|
||||
|
||||
/// <summary>
|
||||
/// Cached assembly manifest data
|
||||
/// </summary>
|
||||
private Models.PortableExecutable.AssemblyManifest? _assemblyManifest = null;
|
||||
private AssemblyManifest? _assemblyManifest = null;
|
||||
|
||||
/// <summary>
|
||||
/// Lock object for reading from the source
|
||||
@@ -875,7 +876,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return null;
|
||||
|
||||
// Try to find a key that matches
|
||||
Models.PortableExecutable.StringData? match = null;
|
||||
StringData? match = null;
|
||||
foreach (var st in stringTable)
|
||||
{
|
||||
if (st.Children == null || st.Length == 0)
|
||||
@@ -894,7 +895,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// Get the assembly manifest, if possible
|
||||
/// </summary>
|
||||
/// <returns>Assembly manifest object, null on error</returns>
|
||||
private Models.PortableExecutable.AssemblyManifest? GetAssemblyManifest()
|
||||
private AssemblyManifest? GetAssemblyManifest()
|
||||
{
|
||||
// Use the cached data if possible
|
||||
if (_assemblyManifest != null)
|
||||
@@ -1443,18 +1444,18 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// </summary>
|
||||
/// <param name="title">Dialog box title to check for</param>
|
||||
/// <returns>List of matching resources</returns>
|
||||
public List<Models.PortableExecutable.DialogBoxResource?> FindDialogByTitle(string title)
|
||||
public List<DialogBoxResource?> FindDialogByTitle(string title)
|
||||
{
|
||||
// Ensure that we have the resource data cached
|
||||
if (ResourceData == null)
|
||||
return [];
|
||||
|
||||
var resources = new List<Models.PortableExecutable.DialogBoxResource?>();
|
||||
var resources = new List<DialogBoxResource?>();
|
||||
foreach (var resource in ResourceData.Values)
|
||||
{
|
||||
if (resource == null)
|
||||
continue;
|
||||
if (resource is not Models.PortableExecutable.DialogBoxResource dbr || dbr == null)
|
||||
if (resource is not DialogBoxResource dbr || dbr == null)
|
||||
continue;
|
||||
|
||||
if (dbr.DialogTemplate?.TitleResource?.Contains(title) ?? false)
|
||||
@@ -1471,18 +1472,18 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// </summary>
|
||||
/// <param name="title">Dialog box item title to check for</param>
|
||||
/// <returns>List of matching resources</returns>
|
||||
public List<Models.PortableExecutable.DialogBoxResource?> FindDialogBoxByItemTitle(string title)
|
||||
public List<DialogBoxResource?> FindDialogBoxByItemTitle(string title)
|
||||
{
|
||||
// Ensure that we have the resource data cached
|
||||
if (ResourceData == null)
|
||||
return [];
|
||||
|
||||
var resources = new List<Models.PortableExecutable.DialogBoxResource?>();
|
||||
var resources = new List<DialogBoxResource?>();
|
||||
foreach (var resource in ResourceData.Values)
|
||||
{
|
||||
if (resource == null)
|
||||
continue;
|
||||
if (resource is not Models.PortableExecutable.DialogBoxResource dbr || dbr == null)
|
||||
if (resource is not DialogBoxResource dbr || dbr == null)
|
||||
continue;
|
||||
|
||||
if (dbr.DialogItemTemplates != null)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.Models.TAR;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
@@ -186,7 +185,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
}
|
||||
|
||||
// Get the file size
|
||||
string sizeOctalString = Encoding.ASCII.GetString(header.Size!).TrimEnd('\0');
|
||||
string sizeOctalString = header.Size!.TrimEnd('\0');
|
||||
if (sizeOctalString.Length == 0)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"Entry {i} has an invalid size, skipping...");
|
||||
@@ -204,9 +203,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
while (entrySize > 0)
|
||||
{
|
||||
// Exit early if block number is invalid
|
||||
if (blockNumber >= entry.Blocks.Count)
|
||||
if (blockNumber >= entry.Blocks.Length)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Invalid block number {i + 1} of {entry.Blocks.Count}, file may be incomplete!");
|
||||
if (includeDebug) Console.Error.WriteLine($"Invalid block number {i + 1} of {entry.Blocks.Length}, file may be incomplete!");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
// TODO: Remove when IO is updated
|
||||
namespace SabreTools.IO.Streams
|
||||
{
|
||||
/// <summary>
|
||||
/// Stream representing a view into a source
|
||||
/// </summary>
|
||||
public class ViewStream : Stream
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanRead => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanSeek => _source.CanSeek;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanWrite => false;
|
||||
|
||||
/// <summary>
|
||||
/// Filename from the source, if possible
|
||||
/// </summary>
|
||||
public string? Filename
|
||||
{
|
||||
get
|
||||
{
|
||||
// A subset of streams have a filename
|
||||
if (_source is FileStream fs)
|
||||
return fs.Name;
|
||||
else if (_source is ViewStream vs)
|
||||
return vs.Filename;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Length => _length;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
// Handle 0-length sources
|
||||
if (_length <= 0)
|
||||
return 0;
|
||||
|
||||
return _source.Position - _initialPosition;
|
||||
}
|
||||
set
|
||||
{
|
||||
// Handle 0-length sources
|
||||
if (_length <= 0)
|
||||
{
|
||||
_source.Position = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
long position = value;
|
||||
|
||||
// Handle out-of-bounds seeks
|
||||
if (position < 0)
|
||||
position = 0;
|
||||
else if (position >= _length)
|
||||
position = _length - 1;
|
||||
|
||||
_source.Position = _initialPosition + position;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Instance Variables
|
||||
|
||||
/// <summary>
|
||||
/// Initial position within the underlying data
|
||||
/// </summary>
|
||||
protected long _initialPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Usable length in the underlying data
|
||||
/// </summary>
|
||||
protected long _length;
|
||||
|
||||
/// <summary>
|
||||
/// Source data
|
||||
/// </summary>
|
||||
protected Stream _source;
|
||||
|
||||
/// <summary>
|
||||
/// Lock object for reading from the source
|
||||
/// </summary>
|
||||
private readonly object _sourceLock = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new ViewStream from a Stream
|
||||
/// </summary>
|
||||
public ViewStream(Stream data, long offset)
|
||||
{
|
||||
if (!data.CanRead)
|
||||
throw new ArgumentException(nameof(data));
|
||||
if (offset < 0 || offset > data.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
|
||||
_source = data;
|
||||
_initialPosition = offset;
|
||||
_length = data.Length - offset;
|
||||
|
||||
_source.Seek(_initialPosition, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new ViewStream from a Stream
|
||||
/// </summary>
|
||||
public ViewStream(Stream data, long offset, long length)
|
||||
{
|
||||
if (!data.CanRead)
|
||||
throw new ArgumentException(nameof(data));
|
||||
if (offset < 0 || offset > data.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
if (length < 0 || offset + length > data.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
|
||||
_source = data;
|
||||
_initialPosition = offset;
|
||||
_length = length;
|
||||
|
||||
_source.Seek(_initialPosition, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new ViewStream from a byte array
|
||||
/// </summary>
|
||||
public ViewStream(byte[] data, long offset)
|
||||
{
|
||||
if (offset < 0 || offset > data.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
|
||||
long length = data.Length - offset;
|
||||
_source = new MemoryStream(data, (int)offset, (int)length);
|
||||
_initialPosition = 0;
|
||||
_length = length;
|
||||
|
||||
_source.Seek(_initialPosition, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct a new ViewStream from a byte array
|
||||
/// </summary>
|
||||
public ViewStream(byte[] data, long offset, long length)
|
||||
{
|
||||
if (offset < 0 || offset > data.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(offset));
|
||||
if (length < 0 || offset + length > data.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(length));
|
||||
|
||||
_source = new MemoryStream(data, (int)offset, (int)length);
|
||||
_initialPosition = 0;
|
||||
_length = length;
|
||||
|
||||
_source.Seek(_initialPosition, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Data
|
||||
|
||||
/// <summary>
|
||||
/// Check if a data segment is valid in the data source
|
||||
/// </summary>
|
||||
/// <param name="offset">Position in the source</param>
|
||||
/// <param name="count">Length of the data to check</param>
|
||||
/// <returns>True if the positional data is valid, false otherwise</returns>
|
||||
public bool SegmentValid(long offset, long count)
|
||||
{
|
||||
if (offset < 0 || offset > Length)
|
||||
return false;
|
||||
if (count < 0 || offset + count > Length)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Stream Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Flush()
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
// Invalid cases always return 0
|
||||
if (buffer.Length == 0)
|
||||
return 0;
|
||||
if (offset < 0 || offset >= buffer.Length)
|
||||
return 0;
|
||||
if (count < 0 || offset + count > buffer.Length)
|
||||
return 0;
|
||||
|
||||
// Short-circuit 0-byte reads
|
||||
if (count == 0)
|
||||
return 0;
|
||||
|
||||
try
|
||||
{
|
||||
lock (_sourceLock)
|
||||
{
|
||||
return _source.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Absorb the error
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
// Handle the "seek"
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin: Position = offset; break;
|
||||
case SeekOrigin.Current: Position += offset; break;
|
||||
case SeekOrigin.End: Position = _length + offset - 1; break;
|
||||
default: throw new ArgumentException($"Invalid value for {nameof(origin)}");
|
||||
}
|
||||
|
||||
return Position;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetLength(long value)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
623
SabreTools.Serialization/Wrappers/WiseOverlayHeader.cs
Normal file
623
SabreTools.Serialization/Wrappers/WiseOverlayHeader.cs
Normal file
@@ -0,0 +1,623 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Compression.Deflate;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.IO.Streams;
|
||||
using SabreTools.Matching;
|
||||
using SabreTools.Models.WiseInstaller;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class WiseOverlayHeader : WrapperBase<OverlayHeader>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DescriptionString => "Wise Installer Overlay Header";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extension Properties
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.Ctl3d32DeflatedSize"/>
|
||||
public uint Ctl3d32DeflatedSize => Model.Ctl3d32DeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.DibDeflatedSize"/>
|
||||
public uint DibDeflatedSize => Model.DibDeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.DibInflatedSize"/>
|
||||
public uint DibInflatedSize => Model.DibInflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.FinalFileDeflatedSize"/>
|
||||
public uint FinalFileDeflatedSize => Model.FinalFileDeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.FinalFileInflatedSize"/>
|
||||
public uint FinalFileInflatedSize => Model.FinalFileInflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.Flags"/>
|
||||
public OverlayHeaderFlags Flags => Model.Flags;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.InstallScriptDeflatedSize"/>
|
||||
public uint InstallScriptDeflatedSize => Model.InstallScriptDeflatedSize ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if data is packed in PKZIP containers
|
||||
/// </summary>
|
||||
public bool IsPKZIP
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET20 || NET35
|
||||
return (Flags & OverlayHeaderFlags.WISE_FLAG_PK_ZIP) != 0;
|
||||
#else
|
||||
return Flags.HasFlag(OverlayHeaderFlags.WISE_FLAG_PK_ZIP);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.ProgressDllDeflatedSize"/>
|
||||
public uint ProgressDllDeflatedSize => Model.ProgressDllDeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.SomeData4DeflatedSize"/>
|
||||
public uint SomeData4DeflatedSize => Model.SomeData4DeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.SomeData7DeflatedSize"/>
|
||||
public uint SomeData7DeflatedSize => Model.SomeData7DeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.SomeData8DeflatedSize"/>
|
||||
public uint SomeData8DeflatedSize => Model.SomeData8DeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.SomeData9DeflatedSize"/>
|
||||
public uint SomeData9DeflatedSize => Model.SomeData9DeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.SomeData10DeflatedSize"/>
|
||||
public uint SomeData10DeflatedSize => Model.SomeData10DeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.RegToolDeflatedSize"/>
|
||||
public uint RegToolDeflatedSize => Model.RegToolDeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.WiseDllDeflatedSize"/>
|
||||
public uint WiseDllDeflatedSize => Model.WiseDllDeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.WiseScriptDeflatedSize"/>
|
||||
public uint WiseScriptDeflatedSize => Model.WiseScriptDeflatedSize;
|
||||
|
||||
/// <inheritdoc cref="OverlayHeader.WiseScriptInflatedSize"/>
|
||||
public uint WiseScriptInflatedSize => Model.WiseScriptInflatedSize;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public WiseOverlayHeader(OverlayHeader? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public WiseOverlayHeader(OverlayHeader? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Wise installer overlay header from a byte array and offset
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the header</param>
|
||||
/// <param name="offset">Offset within the array to parse</param>
|
||||
/// <returns>A Wise installer overlay header wrapper on success, null on failure</returns>
|
||||
public static WiseOverlayHeader? Create(byte[]? data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and use that
|
||||
var dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return Create(dataStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Wise installer overlay header from a Stream
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the header</param>
|
||||
/// <returns>A Wise installer overlay header wrapper on success, null on failure</returns>
|
||||
public static WiseOverlayHeader? Create(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var model = Deserializers.WiseOverlayHeader.DeserializeStream(data);
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new WiseOverlayHeader(model, data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from a Wise installer to an output directory
|
||||
/// </summary>
|
||||
/// <param name="filename">Input filename to read from</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public static bool ExtractAll(string? filename, string outputDirectory, bool includeDebug)
|
||||
{
|
||||
// If the filename is invalid
|
||||
if (filename == null)
|
||||
return false;
|
||||
|
||||
// If the file could not be opened
|
||||
if (!OpenFile(filename, includeDebug, out var stream))
|
||||
return false;
|
||||
|
||||
// Get the source directory
|
||||
string? sourceDirectory = Path.GetDirectoryName(Path.GetFullPath(filename));
|
||||
|
||||
return ExtractAll(stream, sourceDirectory, outputDirectory, includeDebug);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from a Wise installer to an output directory
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the Wise installer</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public static bool ExtractAll(Stream? data, string outputDirectory, bool includeDebug)
|
||||
=> ExtractAll(data, sourceDirectory: null, outputDirectory, includeDebug);
|
||||
|
||||
/// <summary>
|
||||
/// Extract all files from a Wise installer to an output directory
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the Wise installer</param>
|
||||
/// <param name="sourceDirectory">Directory where installer files live, if possible</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if all files extracted, false otherwise</returns>
|
||||
public static bool ExtractAll(Stream? data, string? sourceDirectory, string outputDirectory, bool includeDebug)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return false;
|
||||
|
||||
// Attempt to get the overlay header
|
||||
if (!FindOverlayHeader(data, includeDebug, out var header) || header == null)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Could not parse the overlay header");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract the header-defined files
|
||||
bool extracted = header.ExtractHeaderDefinedFiles(data, outputDirectory, includeDebug, out long dataStart);
|
||||
if (!extracted)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Could not extract header-defined files");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Open the script file from the output directory
|
||||
var scriptStream = File.OpenRead(Path.Combine(outputDirectory, "WiseScript.bin"));
|
||||
var script = WiseScript.Create(scriptStream);
|
||||
if (script == null)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Could not parse WiseScript.bin");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Process the state machine
|
||||
return script.ProcessStateMachine(data,
|
||||
sourceDirectory,
|
||||
dataStart,
|
||||
outputDirectory,
|
||||
header.IsPKZIP,
|
||||
includeDebug);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the overlay header from the Wise installer, if possible
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the Wise installer</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <param name="header">The found overlay header on success, null otherwise</param>
|
||||
/// <returns>True if the header was found and valid, false otherwise</returns>
|
||||
public static bool FindOverlayHeader(Stream data, bool includeDebug, out WiseOverlayHeader? header)
|
||||
{
|
||||
// Set the default header value
|
||||
header = null;
|
||||
|
||||
// Attempt to deserialize the file as either NE or PE
|
||||
var wrapper = WrapperFactory.CreateExecutableWrapper(data);
|
||||
if (wrapper is NewExecutable ne)
|
||||
{
|
||||
return FindOverlayHeader(data, ne, includeDebug, out header);
|
||||
}
|
||||
else if (wrapper is PortableExecutable pe)
|
||||
{
|
||||
return FindOverlayHeader(data, pe, includeDebug, out header);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Only NE and PE executables are supported");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the overlay header from a NE Wise installer, if possible
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the Wise installer</param>
|
||||
/// <param name="nex">Wrapper representing the NE</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <param name="header">The found overlay header on success, null otherwise</param>
|
||||
/// <returns>True if the header was found and valid, false otherwise</returns>
|
||||
public static bool FindOverlayHeader(Stream data, NewExecutable nex, bool includeDebug, out WiseOverlayHeader? header)
|
||||
{
|
||||
// Set the default header value
|
||||
header = null;
|
||||
|
||||
// Get the overlay offset
|
||||
long overlayOffset = nex.OverlayAddress;
|
||||
if (overlayOffset < 0 || overlayOffset >= data.Length)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Could not parse the overlay header");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to get the overlay header
|
||||
data.Seek(overlayOffset, SeekOrigin.Begin);
|
||||
header = Create(data);
|
||||
if (header != null)
|
||||
return true;
|
||||
|
||||
// Align and loop to see if it can be found
|
||||
data.Seek(overlayOffset, SeekOrigin.Begin);
|
||||
data.AlignToBoundary(0x10);
|
||||
overlayOffset = data.Position;
|
||||
while (data.Position < data.Length)
|
||||
{
|
||||
data.Seek(overlayOffset, SeekOrigin.Begin);
|
||||
header = Create(data);
|
||||
if (header != null)
|
||||
return true;
|
||||
|
||||
overlayOffset += 0x10;
|
||||
}
|
||||
|
||||
header = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the overlay header from a PE Wise installer, if possible
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the Wise installer</param>
|
||||
/// <param name="pex">Wrapper representing the PE</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <param name="header">The found overlay header on success, null otherwise</param>
|
||||
/// <returns>True if the header was found and valid, false otherwise</returns>
|
||||
public static bool FindOverlayHeader(Stream data, PortableExecutable pex, bool includeDebug, out WiseOverlayHeader? header)
|
||||
{
|
||||
// Set the default header value
|
||||
header = null;
|
||||
|
||||
// Get the overlay offset
|
||||
long overlayOffset = pex.OverlayAddress;
|
||||
|
||||
// Attempt to get the overlay header
|
||||
if (overlayOffset >= 0 && overlayOffset < pex.Length)
|
||||
{
|
||||
data.Seek(overlayOffset, SeekOrigin.Begin);
|
||||
header = Create(data);
|
||||
if (header != null)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check section data
|
||||
foreach (var section in pex.Model.SectionTable ?? [])
|
||||
{
|
||||
string sectionName = Encoding.ASCII.GetString(section.Name ?? []).TrimEnd('\0');
|
||||
long sectionOffset = section.VirtualAddress.ConvertVirtualAddress(pex.Model.SectionTable);
|
||||
data.Seek(sectionOffset, SeekOrigin.Begin);
|
||||
|
||||
header = Create(data);
|
||||
if (header != null)
|
||||
return true;
|
||||
|
||||
// Check after the resource table
|
||||
if (sectionName == ".rsrc")
|
||||
{
|
||||
// Data immediately following
|
||||
long afterResourceOffset = sectionOffset + section.SizeOfRawData;
|
||||
data.Seek(afterResourceOffset, SeekOrigin.Begin);
|
||||
|
||||
header = Create(data);
|
||||
if (header != null)
|
||||
return true;
|
||||
|
||||
// Data following padding data
|
||||
data.Seek(afterResourceOffset, SeekOrigin.Begin);
|
||||
_ = data.ReadNullTerminatedAnsiString();
|
||||
|
||||
header = Create(data);
|
||||
if (header != null)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no resources
|
||||
if (pex.Model.OptionalHeader?.ResourceTable == null || pex.ResourceData == null)
|
||||
return false;
|
||||
|
||||
// Get the resources that have an executable signature
|
||||
bool exeResources = false;
|
||||
foreach (var kvp in pex.ResourceData)
|
||||
{
|
||||
if (kvp.Value == null || kvp.Value is not byte[] ba)
|
||||
continue;
|
||||
if (!ba.StartsWith(Models.MSDOS.Constants.SignatureBytes))
|
||||
continue;
|
||||
|
||||
exeResources = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// If there are no executable resources
|
||||
if (!exeResources)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Could not find the overlay header");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the raw resource table offset
|
||||
long resourceTableOffset = pex.Model.OptionalHeader.ResourceTable.VirtualAddress.ConvertVirtualAddress(pex.Model.SectionTable);
|
||||
if (resourceTableOffset <= 0)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Could not find the overlay header");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Search the resource table data for the offset
|
||||
long resourceOffset = -1;
|
||||
data.Seek(resourceTableOffset, SeekOrigin.Begin);
|
||||
while (data.Position < resourceTableOffset + pex.Model.OptionalHeader.ResourceTable.Size && data.Position < data.Length)
|
||||
{
|
||||
ushort possibleSignature = data.ReadUInt16();
|
||||
if (possibleSignature == Models.MSDOS.Constants.SignatureUInt16)
|
||||
{
|
||||
resourceOffset = data.Position - 2;
|
||||
break;
|
||||
}
|
||||
|
||||
data.Seek(-1, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
// If there was no valid offset, somehow
|
||||
if (resourceOffset == -1)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Could not find the overlay header");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the executable and recurse
|
||||
data.Seek(resourceOffset, SeekOrigin.Begin);
|
||||
var resourceExe = WrapperFactory.CreateExecutableWrapper(data);
|
||||
if (resourceExe is not PortableExecutable resourcePex)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Could not find the overlay header");
|
||||
return false;
|
||||
}
|
||||
|
||||
return FindOverlayHeader(data, resourcePex, includeDebug, out header);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open a potential WISE installer file and any additional files
|
||||
/// </summary>
|
||||
/// <param name="filename">Input filename or base name to read from</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if the file could be opened, false otherwise</returns>
|
||||
private static bool OpenFile(string filename, bool includeDebug, out ReadOnlyCompositeStream? stream)
|
||||
{
|
||||
// If the file exists as-is
|
||||
if (File.Exists(filename))
|
||||
{
|
||||
var fileStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
stream = new ReadOnlyCompositeStream([fileStream]);
|
||||
|
||||
// Debug statement
|
||||
if (includeDebug) Console.WriteLine($"File {filename} was found and opened");
|
||||
|
||||
// Strip the extension and rebuild
|
||||
string? directory = Path.GetDirectoryName(filename);
|
||||
filename = Path.GetFileNameWithoutExtension(filename);
|
||||
if (directory != null)
|
||||
filename = Path.Combine(directory, filename);
|
||||
}
|
||||
|
||||
// If the base name was provided, try to open the associated exe
|
||||
else if (File.Exists($"{filename}.EXE"))
|
||||
{
|
||||
var fileStream = File.Open($"{filename}.EXE", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
stream = new ReadOnlyCompositeStream([fileStream]);
|
||||
|
||||
// Debug statement
|
||||
if (includeDebug) Console.WriteLine($"File {filename}.EXE was found and opened");
|
||||
}
|
||||
else if (File.Exists($"{filename}.exe"))
|
||||
{
|
||||
var fileStream = File.Open($"{filename}.exe", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
stream = new ReadOnlyCompositeStream([fileStream]);
|
||||
|
||||
// Debug statement
|
||||
if (includeDebug) Console.WriteLine($"File {filename}.exe was found and opened");
|
||||
}
|
||||
|
||||
// Otherwise, the file cannot be opened
|
||||
else
|
||||
{
|
||||
stream = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the pattern for file naming
|
||||
string filePattern = string.Empty;
|
||||
bool longDigits = false;
|
||||
|
||||
byte fileno = 0;
|
||||
bool foundStart = false;
|
||||
for (; fileno < 3; fileno++)
|
||||
{
|
||||
if (File.Exists($"{filename}.W0{fileno}"))
|
||||
{
|
||||
foundStart = true;
|
||||
filePattern = $"{filename}.W";
|
||||
longDigits = false;
|
||||
break;
|
||||
}
|
||||
else if (File.Exists($"{filename}.w0{fileno}"))
|
||||
{
|
||||
foundStart = true;
|
||||
filePattern = $"{filename}.w";
|
||||
longDigits = false;
|
||||
break;
|
||||
}
|
||||
else if (File.Exists($"{filename}.00{fileno}"))
|
||||
{
|
||||
foundStart = true;
|
||||
filePattern = $"{filename}.";
|
||||
longDigits = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If no starting part has been found
|
||||
if (!foundStart)
|
||||
return true;
|
||||
|
||||
// Loop through and try to read all additional files
|
||||
for (; ; fileno++)
|
||||
{
|
||||
string nextPart = longDigits ? $"{filePattern}{fileno:D3}" : $"{filePattern}{fileno:D2}";
|
||||
if (!File.Exists(nextPart))
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"Part {nextPart} was not found");
|
||||
break;
|
||||
}
|
||||
|
||||
// Debug statement
|
||||
if (includeDebug) Console.WriteLine($"Part {nextPart} was found and appended");
|
||||
|
||||
var fileStream = File.Open(nextPart, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
stream.AddStream(fileStream);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract the predefined, static files defined in the header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the Wise installer</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if the files extracted successfully, false otherwise</returns>
|
||||
private bool ExtractHeaderDefinedFiles(Stream data, string outputDirectory, bool includeDebug, out long dataStart)
|
||||
{
|
||||
// TODO: This needs to seek to the end of the header before extracting
|
||||
|
||||
// Determine where the remaining compressed data starts
|
||||
dataStart = data.Position;
|
||||
|
||||
// Extract WiseColors.dib, if it exists
|
||||
var expected = new DeflateInfo { InputSize = DibDeflatedSize, OutputSize = DibInflatedSize, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "WiseColors.dib", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract WiseScript.bin
|
||||
expected = new DeflateInfo { InputSize = WiseScriptDeflatedSize, OutputSize = WiseScriptInflatedSize, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "WiseScript.bin", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract WISE0001.DLL, if it exists
|
||||
expected = new DeflateInfo { InputSize = WiseDllDeflatedSize, OutputSize = -1, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "WISE0001.DLL", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract CTL3D32.DLL, if it exists
|
||||
expected = new DeflateInfo { InputSize = Ctl3d32DeflatedSize, OutputSize = -1, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "CTL3D32.DLL", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract FILE0004, if it exists
|
||||
expected = new DeflateInfo { InputSize = SomeData4DeflatedSize, OutputSize = -1, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "FILE0004", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract Ocxreg32.EXE, if it exists
|
||||
expected = new DeflateInfo { InputSize = RegToolDeflatedSize, OutputSize = -1, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "Ocxreg32.EXE", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract PROGRESS.DLL, if it exists
|
||||
expected = new DeflateInfo { InputSize = ProgressDllDeflatedSize, OutputSize = -1, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "PROGRESS.DLL", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract FILE0007, if it exists
|
||||
expected = new DeflateInfo { InputSize = SomeData7DeflatedSize, OutputSize = -1, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "FILE0007", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract FILE0008, if it exists
|
||||
expected = new DeflateInfo { InputSize = SomeData8DeflatedSize, OutputSize = -1, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "FILE0008", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract FILE0009, if it exists
|
||||
expected = new DeflateInfo { InputSize = SomeData9DeflatedSize, OutputSize = -1, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "FILE0009", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract FILE000A, if it exists
|
||||
expected = new DeflateInfo { InputSize = SomeData10DeflatedSize, OutputSize = -1, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "FILE000A", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract install script, if it exists
|
||||
expected = new DeflateInfo { InputSize = InstallScriptDeflatedSize, OutputSize = -1, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, "INSTALL_SCRIPT", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
// Extract FILE000{n}.DAT, if it exists
|
||||
expected = new DeflateInfo { InputSize = FinalFileDeflatedSize, OutputSize = FinalFileInflatedSize, Crc32 = 0 };
|
||||
if (InflateWrapper.ExtractFile(data, IsPKZIP ? null : "FILE00XX.DAT", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
|
||||
return false;
|
||||
|
||||
dataStart = data.Position;
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
485
SabreTools.Serialization/Wrappers/WiseScript.cs
Normal file
485
SabreTools.Serialization/Wrappers/WiseScript.cs
Normal file
@@ -0,0 +1,485 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Compression.Deflate;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.WiseInstaller;
|
||||
using SabreTools.Models.WiseInstaller.Actions;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class WiseScript : WrapperBase<ScriptFile>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DescriptionString => "Wise Installer Script File";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extension Properties
|
||||
|
||||
/// <inheritdoc cref="ScriptHeader.DateTime"/>
|
||||
public uint DateTime => Model.Header?.DateTime ?? 0;
|
||||
|
||||
/// <inheritdoc cref="ScriptHeader.Flags"/>
|
||||
public ushort Flags => Model.Header?.Flags ?? 0;
|
||||
|
||||
/// <inheritdoc cref="ScriptHeader.FontSize"/>
|
||||
public uint FontSize => Model.Header?.FontSize ?? 0;
|
||||
|
||||
/// <inheritdoc cref="ScriptHeader.FTPURL"/>
|
||||
public string? FTPURL => Model.Header?.FTPURL;
|
||||
|
||||
/// <inheritdoc cref="ScriptFile.HeaderStrings"/>
|
||||
public string[]? HeaderStrings => Model.Header?.HeaderStrings;
|
||||
|
||||
/// <inheritdoc cref="ScriptHeader.LanguageCount"/>
|
||||
public byte LanguageCount => Model.Header?.LanguageCount ?? 0;
|
||||
|
||||
/// <inheritdoc cref="ScriptHeader.LogPathname"/>
|
||||
public string? LogPathname => Model.Header?.LogPathname;
|
||||
|
||||
/// <inheritdoc cref="ScriptHeader.MessageFont"/>
|
||||
public string? MessageFont => Model.Header?.MessageFont;
|
||||
|
||||
/// <inheritdoc cref="ScriptFile.States"/>
|
||||
public MachineState[]? States => Model.States;
|
||||
|
||||
/// <inheritdoc cref="ScriptHeader.UnknownU16_1"/>
|
||||
public ushort UnknownU16_1 => Model.Header?.UnknownU16_1 ?? 0;
|
||||
|
||||
/// <inheritdoc cref="ScriptHeader.UnknownU16_2"/>
|
||||
public ushort UnknownU16_2 => Model.Header?.UnknownU16_2 ?? 0;
|
||||
|
||||
/// <inheritdoc cref="ScriptFile.VariableLengthData"/>
|
||||
public byte[]? VariableLengthData => Model.Header?.VariableLengthData;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public WiseScript(ScriptFile? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public WiseScript(ScriptFile? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Wise installer script file from a byte array and offset
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the script</param>
|
||||
/// <param name="offset">Offset within the array to parse</param>
|
||||
/// <returns>A Wise installer script file wrapper on success, null on failure</returns>
|
||||
public static WiseScript? Create(byte[]? data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and use that
|
||||
var dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return Create(dataStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Wise installer script file from a Stream
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the script</param>
|
||||
/// <returns>A Wise installer script file wrapper on success, null on failure</returns>
|
||||
public static WiseScript? Create(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var model = Deserializers.WiseScript.DeserializeStream(data);
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new WiseScript(model, data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Processing
|
||||
|
||||
/// <summary>
|
||||
/// Process the state machine and perform all required actions
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the Wise installer</param>
|
||||
/// <param name="sourceDirectory">Directory where installer files live, if possible</param>
|
||||
/// <param name="script">Parsed script to retrieve information from</param>
|
||||
/// <param name="dataStart">Start of the deflated data</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="isPkzip">Indicates if PKZIP containers are used</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if there were no errors during processing, false otherwise</returns>
|
||||
public bool ProcessStateMachine(Stream data,
|
||||
string? sourceDirectory,
|
||||
long dataStart,
|
||||
string outputDirectory,
|
||||
bool isPkzip,
|
||||
bool includeDebug)
|
||||
{
|
||||
// If the state machine is invalid
|
||||
if (States == null || States.Length == 0)
|
||||
return false;
|
||||
|
||||
// Initialize important loop information
|
||||
int normalFileCount = 0;
|
||||
Dictionary<string, string> environment = [];
|
||||
if (sourceDirectory != null)
|
||||
environment.Add("INST", sourceDirectory);
|
||||
|
||||
// Loop through the state machine and process
|
||||
foreach (var state in States)
|
||||
{
|
||||
switch (state.Op)
|
||||
{
|
||||
case OperationCode.InstallFile:
|
||||
if (state.Data is not InstallFile fileHeader)
|
||||
return false;
|
||||
|
||||
// Try to extract to the output directory
|
||||
ExtractFile(data, dataStart, fileHeader, ++normalFileCount, outputDirectory, isPkzip, includeDebug);
|
||||
break;
|
||||
|
||||
case OperationCode.EditIniFile:
|
||||
if (state.Data is not EditIniFile editIniFile)
|
||||
return false;
|
||||
|
||||
// Try to write to the output directory
|
||||
WriteIniData(editIniFile, outputDirectory, ++normalFileCount);
|
||||
break;
|
||||
|
||||
case OperationCode.DisplayBillboard:
|
||||
if (state.Data is not DisplayBillboard displayBillboard)
|
||||
return false;
|
||||
|
||||
// Try to extract to the output directory
|
||||
ExtractFile(data, dataStart, displayBillboard, ++normalFileCount, outputDirectory, isPkzip, includeDebug);
|
||||
break;
|
||||
|
||||
case OperationCode.DeleteFile:
|
||||
if (state.Data is not DeleteFile deleteFile)
|
||||
return false;
|
||||
|
||||
if (includeDebug) Console.WriteLine($"File {deleteFile.Pathname} is supposed to be deleted");
|
||||
break;
|
||||
|
||||
case OperationCode.CreateDirectory:
|
||||
if (state.Data is not CreateDirectory createDirectory)
|
||||
return false;
|
||||
if (createDirectory.Pathname == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"Directory {createDirectory.Pathname} is being created");
|
||||
|
||||
// Ensure directory separators are consistent
|
||||
string newDirectoryName = Path.Combine(outputDirectory, createDirectory.Pathname);
|
||||
if (Path.DirectorySeparatorChar == '\\')
|
||||
newDirectoryName = newDirectoryName.Replace('/', '\\');
|
||||
else if (Path.DirectorySeparatorChar == '/')
|
||||
newDirectoryName = newDirectoryName.Replace('\\', '/');
|
||||
|
||||
// Perform path replacements
|
||||
foreach (var kvp in environment)
|
||||
{
|
||||
newDirectoryName = newDirectoryName.Replace($"%{kvp.Key}%", kvp.Value);
|
||||
}
|
||||
|
||||
newDirectoryName = newDirectoryName.Replace("%", string.Empty);
|
||||
|
||||
// Remove wildcards from end of the path
|
||||
if (newDirectoryName.EndsWith("*.*"))
|
||||
newDirectoryName = newDirectoryName.Substring(0, newDirectoryName.Length - 4);
|
||||
|
||||
Directory.CreateDirectory(newDirectoryName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"Directory {createDirectory.Pathname} could not be created!");
|
||||
}
|
||||
break;
|
||||
|
||||
case OperationCode.CopyLocalFile:
|
||||
if (state.Data is not CopyLocalFile copyLocalFile)
|
||||
return false;
|
||||
if (copyLocalFile.Source == null)
|
||||
return false;
|
||||
if (copyLocalFile.Destination == null)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"File {copyLocalFile.Source} is being copied to {copyLocalFile.Destination}");
|
||||
|
||||
// Ensure directory separators are consistent
|
||||
string oldFilePath = copyLocalFile.Source;
|
||||
if (Path.DirectorySeparatorChar == '\\')
|
||||
oldFilePath = oldFilePath.Replace('/', '\\');
|
||||
else if (Path.DirectorySeparatorChar == '/')
|
||||
oldFilePath = oldFilePath.Replace('\\', '/');
|
||||
|
||||
// Perform path replacements
|
||||
foreach (var kvp in environment)
|
||||
{
|
||||
oldFilePath = oldFilePath.Replace($"%{kvp.Key}%", kvp.Value);
|
||||
}
|
||||
|
||||
oldFilePath = oldFilePath.Replace("%", string.Empty);
|
||||
|
||||
// Sanity check
|
||||
if (!File.Exists(oldFilePath))
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"File {copyLocalFile.Source} is supposed to be copied to {copyLocalFile.Destination}, but it does not exist!");
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure directory separators are consistent
|
||||
string newFilePath = Path.Combine(outputDirectory, copyLocalFile.Destination);
|
||||
if (Path.DirectorySeparatorChar == '\\')
|
||||
newFilePath = newFilePath.Replace('/', '\\');
|
||||
else if (Path.DirectorySeparatorChar == '/')
|
||||
newFilePath = newFilePath.Replace('\\', '/');
|
||||
|
||||
// Perform path replacements
|
||||
foreach (var kvp in environment)
|
||||
{
|
||||
newFilePath = newFilePath.Replace($"%{kvp.Key}%", kvp.Value);
|
||||
}
|
||||
|
||||
newFilePath = newFilePath.Replace("%", string.Empty);
|
||||
|
||||
// Sanity check
|
||||
string? newFileDirectory = Path.GetDirectoryName(newFilePath);
|
||||
if (newFileDirectory != null && !Directory.Exists(newFileDirectory))
|
||||
Directory.CreateDirectory(newFileDirectory);
|
||||
|
||||
File.Copy(oldFilePath, newFilePath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"File {copyLocalFile.Source} could not be copied!");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case OperationCode.CustomDialogSet:
|
||||
if (state.Data is not CustomDialogSet customDialogSet)
|
||||
return false;
|
||||
|
||||
// Try to extract to the output directory
|
||||
++normalFileCount;
|
||||
ExtractFile(data, dataStart, customDialogSet, outputDirectory, isPkzip, includeDebug);
|
||||
break;
|
||||
|
||||
case OperationCode.GetTemporaryFilename:
|
||||
if (state.Data is not GetTemporaryFilename getTemporaryFilename)
|
||||
return false;
|
||||
|
||||
if (getTemporaryFilename.Variable != null)
|
||||
environment[getTemporaryFilename.Variable] = Guid.NewGuid().ToString();
|
||||
break;
|
||||
|
||||
case OperationCode.AddTextToInstallLog:
|
||||
if (state.Data is not AddTextToInstallLog addTextToInstallLog)
|
||||
return false;
|
||||
|
||||
if (includeDebug) Console.WriteLine($"INSTALL.LOG: {addTextToInstallLog.Text}");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to extract a file defined by a file header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the Wise installer</param>
|
||||
/// <param name="dataStart">Start of the deflated data</param>
|
||||
/// <param name="obj">Deflate information</param>
|
||||
/// <param name="index">File index for automatic naming</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="isPkzip">Indicates if PKZIP containers are used</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if the file extracted successfully, false otherwise</returns>
|
||||
public static ExtractionStatus ExtractFile(Stream data,
|
||||
long dataStart,
|
||||
InstallFile obj,
|
||||
int index,
|
||||
string outputDirectory,
|
||||
bool isPkzip,
|
||||
bool includeDebug)
|
||||
{
|
||||
// Get expected values
|
||||
var expected = new DeflateInfo
|
||||
{
|
||||
InputSize = obj.DeflateEnd - obj.DeflateStart,
|
||||
OutputSize = obj.InflatedSize,
|
||||
Crc32 = obj.Crc32,
|
||||
};
|
||||
|
||||
// Perform path replacements
|
||||
string filename = obj.DestinationPathname ?? $"WISE{index:X4}";
|
||||
filename = filename.Replace("%", string.Empty);
|
||||
data.Seek(dataStart + obj.DeflateStart, SeekOrigin.Begin);
|
||||
return InflateWrapper.ExtractFile(data,
|
||||
filename,
|
||||
outputDirectory,
|
||||
expected,
|
||||
isPkzip,
|
||||
includeDebug);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to extract a file defined by a file header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the Wise installer</param>
|
||||
/// <param name="dataStart">Start of the deflated data</param>
|
||||
/// <param name="obj">Deflate information</param>
|
||||
/// <param name="index">File index for automatic naming</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="isPkzip">Indicates if PKZIP containers are used</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if the file extracted successfully, false otherwise</returns>
|
||||
public static ExtractionStatus ExtractFile(Stream data,
|
||||
long dataStart,
|
||||
DisplayBillboard obj,
|
||||
int index,
|
||||
string outputDirectory,
|
||||
bool isPkzip,
|
||||
bool includeDebug)
|
||||
{
|
||||
// Get the generated base name
|
||||
string baseName = $"CustomBillboardSet_{obj.Flags:X4}-{obj.Operand_2}-{obj.Operand_3}";
|
||||
|
||||
// If there are no deflate objects
|
||||
if (obj.DeflateInfo == null)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"Skipping {baseName} because the deflate object array is null!");
|
||||
return ExtractionStatus.FAIL;
|
||||
}
|
||||
|
||||
// Loop through the values
|
||||
for (int i = 0; i < obj.DeflateInfo.Length; i++)
|
||||
{
|
||||
// Get the deflate info object
|
||||
var info = obj.DeflateInfo[i];
|
||||
|
||||
// Get expected values
|
||||
var expected = new DeflateInfo
|
||||
{
|
||||
InputSize = info.DeflateEnd - info.DeflateStart,
|
||||
OutputSize = info.InflatedSize,
|
||||
Crc32 = 0,
|
||||
};
|
||||
|
||||
// Perform path replacements
|
||||
string filename = $"{baseName}{index:X4}";
|
||||
data.Seek(dataStart + info.DeflateStart, SeekOrigin.Begin);
|
||||
_ = InflateWrapper.ExtractFile(data, filename, outputDirectory, expected, isPkzip, includeDebug);
|
||||
}
|
||||
|
||||
// Always return good -- TODO: Fix this
|
||||
return ExtractionStatus.GOOD;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to extract a file defined by a file header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the Wise installer</param>
|
||||
/// <param name="dataStart">Start of the deflated data</param>
|
||||
/// <param name="obj">Deflate information</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="isPkzip">Indicates if PKZIP containers are used</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if the file extracted successfully, false otherwise</returns>
|
||||
public static ExtractionStatus ExtractFile(Stream data,
|
||||
long dataStart,
|
||||
CustomDialogSet obj,
|
||||
string outputDirectory,
|
||||
bool isPkzip,
|
||||
bool includeDebug)
|
||||
{
|
||||
// Get expected values
|
||||
var expected = new DeflateInfo
|
||||
{
|
||||
InputSize = obj.DeflateEnd - obj.DeflateStart,
|
||||
OutputSize = obj.InflatedSize,
|
||||
Crc32 = 0,
|
||||
};
|
||||
|
||||
// Perform path replacements
|
||||
string filename = $"CustomDialogSet_{obj.DisplayVariable}-{obj.Name}";
|
||||
filename = filename.Replace("%", string.Empty);
|
||||
data.Seek(dataStart + obj.DeflateStart, SeekOrigin.Begin);
|
||||
return InflateWrapper.ExtractFile(data, filename, outputDirectory, expected, isPkzip, includeDebug);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to write INI data to the correct file
|
||||
/// </summary>
|
||||
/// <param name="obj">INI information</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="index">File index for automatic naming</param>
|
||||
private static void WriteIniData(EditIniFile obj, string outputDirectory, int index)
|
||||
{
|
||||
// Ensure directory separators are consistent
|
||||
string iniFilePath = obj.Pathname ?? $"WISE{index:X4}.ini";
|
||||
if (Path.DirectorySeparatorChar == '\\')
|
||||
iniFilePath = iniFilePath.Replace('/', '\\');
|
||||
else if (Path.DirectorySeparatorChar == '/')
|
||||
iniFilePath = iniFilePath.Replace('\\', '/');
|
||||
|
||||
// Ignore path replacements
|
||||
iniFilePath = iniFilePath.Replace("%", string.Empty);
|
||||
|
||||
// Ensure the full output directory exists
|
||||
iniFilePath = Path.Combine(outputDirectory, iniFilePath);
|
||||
var directoryName = Path.GetDirectoryName(iniFilePath);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
using (var iniFile = File.Open(iniFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
|
||||
{
|
||||
iniFile.Write(Encoding.ASCII.GetBytes($"[{obj.Section}]\n"));
|
||||
iniFile.Write(Encoding.ASCII.GetBytes($"{obj.Values ?? string.Empty}\n"));
|
||||
iniFile.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
359
SabreTools.Serialization/Wrappers/WiseSectionHeader.cs
Normal file
359
SabreTools.Serialization/Wrappers/WiseSectionHeader.cs
Normal file
@@ -0,0 +1,359 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.IO.Compression.Deflate;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.WiseInstaller;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class WiseSectionHeader : WrapperBase<SectionHeader>, IExtractable
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DescriptionString => "Self-Extracting Wise Installer Header";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extension Properties
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownDataSize"/>
|
||||
public uint UnknownDataSize => Model.UnknownDataSize;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.SecondExecutableFileEntryLength"/> // TODO: VERIFY ON CHANGE
|
||||
public uint SecondExecutableFileEntryLength => Model.SecondExecutableFileEntryLength;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue2"/>
|
||||
public uint UnknownValue2 => Model.UnknownValue2;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue3"/>
|
||||
public uint UnknownValue3 => Model.UnknownValue3;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue4"/>
|
||||
public uint UnknownValue4 => Model.UnknownValue4;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.FirstExecutableFileEntryLength"/>
|
||||
public uint FirstExecutableFileEntryLength => Model.FirstExecutableFileEntryLength; // TODO: VERIFY ON CHANGE
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.MsiFileEntryLength"/>
|
||||
public uint MsiFileEntryLength => Model.MsiFileEntryLength;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue7"/>
|
||||
public uint UnknownValue7 => Model.UnknownValue7;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue8"/>
|
||||
public uint UnknownValue8 => Model.UnknownValue8;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.ThirdExecutableFileEntryLength"/>
|
||||
public uint ThirdExecutableFileEntryLength => Model.ThirdExecutableFileEntryLength;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue10"/>
|
||||
public uint UnknownValue10 => Model.UnknownValue10;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue11"/>
|
||||
public uint UnknownValue11 => Model.UnknownValue11;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue12"/>
|
||||
public uint UnknownValue12 => Model.UnknownValue12;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue13"/>
|
||||
public uint UnknownValue13 => Model.UnknownValue13;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue14"/>
|
||||
public uint UnknownValue14 => Model.UnknownValue14;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue15"/>
|
||||
public uint UnknownValue15 => Model.UnknownValue15;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue16"/>
|
||||
public uint UnknownValue16 => Model.UnknownValue16;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue17"/>
|
||||
public uint UnknownValue17 => Model.UnknownValue17;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.UnknownValue18"/>
|
||||
public uint UnknownValue18 => Model.UnknownValue18;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.Version"/>
|
||||
public byte[]? Version => Model.Version;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.PreStringValues"/>
|
||||
public byte[]? PreStringValues => Model.PreStringValues;
|
||||
|
||||
/// <inheritdoc cref="SectionHeader.Strings"/>
|
||||
public byte[][]? Strings => Model.Strings;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public WiseSectionHeader(SectionHeader? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public WiseSectionHeader(SectionHeader? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Wise Self-Extracting installer .WISE section from a byte array and offset
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the section</param>
|
||||
/// <param name="offset">Offset within the array to parse</param>
|
||||
/// <returns>A Wise Self-Extracting installer .WISE section wrapper on success, null on failure</returns>
|
||||
public static WiseSectionHeader? Create(byte[]? data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (offset < 0 || offset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Create a memory stream and use that
|
||||
var dataStream = new MemoryStream(data, offset, data.Length - offset);
|
||||
return Create(dataStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Wise Self-Extracting installer .WISE section from a Stream
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the section</param>
|
||||
/// <returns>A Wise Self-Extracting installer .WISE section wrapper on success, null on failure</returns>
|
||||
public static WiseSectionHeader? Create(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || !data.CanRead)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var model = Deserializers.WiseSectionHeader.DeserializeStream(data);
|
||||
if (model == null)
|
||||
return null;
|
||||
|
||||
return new WiseSectionHeader(model, data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extraction
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Extract(string outputDirectory, bool includeDebug)
|
||||
{
|
||||
// Extract the header-defined files
|
||||
bool extracted = ExtractHeaderDefinedFiles(outputDirectory, includeDebug);
|
||||
if (!extracted)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Could not extract header-defined files");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Currently unaware of any NE samples. That said, as they wouldn't have a .WISE section, it's unclear how such
|
||||
// samples could be identified.
|
||||
|
||||
/// <summary>
|
||||
/// Extract the predefined, static files defined in the header
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>True if the files extracted successfully, false otherwise</returns>
|
||||
private bool ExtractHeaderDefinedFiles(string outputDirectory, bool includeDebug)
|
||||
{
|
||||
// TODO: All reads need to be sequential. At the moment, the data position is at the start of the header;
|
||||
// TODO: it needs to be wherever the header parser was when it finished. This applies to all logic in the
|
||||
// TODO: WiseSectionHeader deserializer and wrapper code.
|
||||
|
||||
// Extract first executable, if it exists
|
||||
if (ExtractFile("FirstExecutable.exe", outputDirectory, FirstExecutableFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
|
||||
return false;
|
||||
|
||||
// Extract second executable, if it exists
|
||||
// If there's a size provided for the second executable but no size for the first executable, the size of
|
||||
// the second executable appears to be some unrelated value that's larger than the second executable
|
||||
// actually is. Currently unable to extract properly in these cases, as no header value in such installers
|
||||
// seems to actually correspond to the real size of the second executable.
|
||||
if (ExtractFile("SecondExecutable.exe", outputDirectory, SecondExecutableFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
|
||||
return false;
|
||||
|
||||
// Extract third executable, if it exists
|
||||
if (ExtractFile("ThirdExecutable.exe", outputDirectory, ThirdExecutableFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
|
||||
return false;
|
||||
|
||||
// Extract main MSI file
|
||||
if (ExtractFile("ExtoutputDirectory: ractedMsi.msi", outputDirectory, MsiFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
|
||||
{
|
||||
// Fallback- seek to the position that's the length of the MSI file entry from the end, then try and
|
||||
// extract from there.
|
||||
_dataSource.Seek(-MsiFileEntryLength, SeekOrigin.End);
|
||||
if (ExtractFile("ExtractedMsi.msi", outputDirectory, MsiFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
|
||||
return false; // The fallback also failed.
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to extract a file defined by a filename
|
||||
/// </summary>
|
||||
/// <param name="filename">Output filename, null to auto-generate</param>
|
||||
/// <param name="outputDirectory">Output directory to write to</param>
|
||||
/// <param name="entrySize">Expected size of the file plus crc32</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns>Extraction status representing the final state</returns>
|
||||
/// <remarks>Assumes that the current stream position is the end of where the data lives</remarks>
|
||||
private ExtractionStatus ExtractFile(string filename,
|
||||
string outputDirectory,
|
||||
uint entrySize,
|
||||
bool includeDebug)
|
||||
{
|
||||
if (includeDebug) Console.WriteLine($"Attempting to extract {filename}");
|
||||
|
||||
// Extract the file
|
||||
var destination = new MemoryStream();
|
||||
ExtractionStatus status;
|
||||
if (!(Version != null && Version[1] == 0x01))
|
||||
{
|
||||
status = ExtractStreamWithChecksum(destination, entrySize, includeDebug);
|
||||
}
|
||||
else // hack for Codesited5.exe , very early and very strange.
|
||||
{
|
||||
status = ExtractStreamWithoutChecksum(destination, entrySize, includeDebug);
|
||||
}
|
||||
|
||||
// If the extracted data is invalid
|
||||
if (status != ExtractionStatus.GOOD || destination == null)
|
||||
return status;
|
||||
|
||||
// Ensure the full output directory exists
|
||||
filename = Path.Combine(outputDirectory, filename);
|
||||
var directoryName = Path.GetDirectoryName(filename);
|
||||
if (directoryName != null && !Directory.Exists(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
// Write the output file
|
||||
File.WriteAllBytes(filename, destination.ToArray());
|
||||
return status;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract source data with a trailing CRC-32 checksum
|
||||
/// </summary>
|
||||
/// <param name="destination">Stream where the file data will be written</param>
|
||||
/// <param name="entrySize">Expected size of the file plus crc32</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns></returns>
|
||||
private ExtractionStatus ExtractStreamWithChecksum(Stream destination, uint entrySize, bool includeDebug)
|
||||
{
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"Offset: {_dataSource.Position:X8}, Expected Read: {entrySize}, Expected Write:{entrySize - 4}"); // clamp to zero
|
||||
|
||||
// Check the validity of the inputs
|
||||
if (entrySize == 0)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Not attempting to extract, expected to read 0 bytes");
|
||||
return ExtractionStatus.GOOD; // If size is 0, then it shouldn't be extracted
|
||||
}
|
||||
else if (entrySize > (_dataSource.Length - _dataSource.Position))
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Not attempting to extract, expected to read {entrySize} bytes but only {_dataSource.Position} bytes remain");
|
||||
return ExtractionStatus.INVALID;
|
||||
}
|
||||
|
||||
// Extract the file
|
||||
try
|
||||
{
|
||||
byte[] actual = _dataSource.ReadBytes((int)entrySize - 4);
|
||||
uint expectedCrc32 = _dataSource.ReadUInt32();
|
||||
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"Expected CRC-32: {expectedCrc32:X8}");
|
||||
|
||||
byte[]? hashBytes = HashTool.GetByteArrayHashArray(actual, HashType.CRC32);
|
||||
if (hashBytes != null)
|
||||
{
|
||||
uint actualCrc32 = BitConverter.ToUInt32(hashBytes, 0);
|
||||
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"Actual CRC-32: {actualCrc32:X8}");
|
||||
|
||||
if (expectedCrc32 != actualCrc32)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Mismatched CRC-32 values!");
|
||||
return ExtractionStatus.BAD_CRC;
|
||||
}
|
||||
}
|
||||
|
||||
destination.Write(actual, 0, actual.Length);
|
||||
return ExtractionStatus.GOOD;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Could not extract");
|
||||
return ExtractionStatus.FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract source data without a trailing CRC-32 checksum
|
||||
/// </summary>
|
||||
/// <param name="destination">Stream where the file data will be written</param>
|
||||
/// <param name="entrySize">Expected size of the file</param>
|
||||
/// <param name="includeDebug">True to include debug data, false otherwise</param>
|
||||
/// <returns></returns>
|
||||
private ExtractionStatus ExtractStreamWithoutChecksum(Stream destination, uint entrySize, bool includeDebug)
|
||||
{
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine($"Offset: {_dataSource.Position:X8}, Expected Read: {entrySize}, Expected Write:{entrySize - 4}");
|
||||
|
||||
// Check the validity of the inputs
|
||||
if (entrySize == 0)
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Not attempting to extract, expected to read 0 bytes");
|
||||
return ExtractionStatus.GOOD; // If size is 0, then it shouldn't be extracted
|
||||
}
|
||||
else if (entrySize > (_dataSource.Length - _dataSource.Position))
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine($"Not attempting to extract, expected to read {entrySize} bytes but only {_dataSource.Position} bytes remain");
|
||||
return ExtractionStatus.INVALID;
|
||||
}
|
||||
|
||||
// Extract the file
|
||||
try
|
||||
{
|
||||
byte[] actual = _dataSource.ReadBytes((int)entrySize);
|
||||
|
||||
// Debug output
|
||||
if (includeDebug) Console.WriteLine("No CRC-32!");
|
||||
|
||||
destination.Write(actual, 0, actual.Length);
|
||||
return ExtractionStatus.GOOD;
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (includeDebug) Console.Error.WriteLine("Could not extract");
|
||||
return ExtractionStatus.FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user