mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-02-04 05:36:12 +00:00
Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86a9846300 | ||
|
|
db877d253c | ||
|
|
0acf1e3b08 | ||
|
|
362ed3a9b6 | ||
|
|
758878a229 | ||
|
|
ffb6dfc333 | ||
|
|
66da74e00a | ||
|
|
d41a0045cb | ||
|
|
b65629ba0e | ||
|
|
9518e6d1a0 | ||
|
|
4f374ee885 | ||
|
|
afa239056e | ||
|
|
886825af11 | ||
|
|
198de925aa | ||
|
|
3f7b71e9a5 | ||
|
|
95baaf8603 | ||
|
|
3673264bab | ||
|
|
64fb5a6b63 | ||
|
|
e9c959ccdb | ||
|
|
4b7487e92e | ||
|
|
52dbcffd8e | ||
|
|
24ae354bc2 | ||
|
|
b30b91fd91 | ||
|
|
efb63afc74 | ||
|
|
16706f7169 | ||
|
|
d7c32676b5 | ||
|
|
c8c45446bc | ||
|
|
f4de2e27d7 | ||
|
|
970fcbd93b | ||
|
|
57d1cd7f1e | ||
|
|
522fc372fa | ||
|
|
7141690fcb | ||
|
|
c7d9177e68 |
@@ -27,9 +27,9 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.4.5" />
|
||||
<PackageReference Include="xunit" Version="2.7.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.8">
|
||||
<PackageReference Include="SabreTools.Models" Version="1.4.8" />
|
||||
<PackageReference Include="xunit" Version="2.8.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -17,6 +17,11 @@ namespace SabreTools.Serialization.Deserializers
|
||||
IFileDeserializer<TModel>,
|
||||
IStreamDeserializer<TModel>
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if compressed files should be decompressed before processing
|
||||
/// </summary>
|
||||
protected virtual bool SkipCompression => false;
|
||||
|
||||
#region IByteDeserializer
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -42,7 +47,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// <inheritdoc/>
|
||||
public virtual TModel? Deserialize(string? path)
|
||||
{
|
||||
using var stream = PathProcessor.OpenStream(path);
|
||||
using var stream = PathProcessor.OpenStream(path, SkipCompression);
|
||||
return DeserializeStream(stream);
|
||||
}
|
||||
|
||||
|
||||
@@ -476,7 +476,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var entry = new ResidentNamesTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
if (entry.Length > 0 && data.Position + entry.Length <= data.Length)
|
||||
{
|
||||
byte[]? name = data.ReadBytes(entry.Length);
|
||||
if (name != null)
|
||||
@@ -810,7 +810,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var entry = new ImportModuleNameTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
if (entry.Length > 0 && data.Position + entry.Length <= data.Length)
|
||||
{
|
||||
byte[]? name = data.ReadBytes(entry.Length);
|
||||
if (name != null)
|
||||
@@ -831,7 +831,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var entry = new ImportModuleProcedureNameTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
if (entry.Length > 0 && data.Position + entry.Length <= data.Length)
|
||||
{
|
||||
byte[]? name = data.ReadBytes(entry.Length);
|
||||
if (name != null)
|
||||
@@ -862,7 +862,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var entry = new NonResidentNamesTableEntry();
|
||||
|
||||
entry.Length = data.ReadByteValue();
|
||||
if (entry.Length > 0)
|
||||
if (entry.Length > 0 && data.Position + entry.Length <= data.Length)
|
||||
{
|
||||
byte[]? name = data.ReadBytes(entry.Length);
|
||||
if (name != null)
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// If the offset for the segment table doesn't exist
|
||||
tableAddress = initialOffset
|
||||
+ (int)stub.Header.NewExeHeaderAddr
|
||||
+ executableHeader.SegmentTableOffset;
|
||||
+ executableHeader.ResourceTableOffset;
|
||||
if (tableAddress >= data.Length)
|
||||
return executable;
|
||||
|
||||
@@ -262,7 +262,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="count">Number of resource table entries to read</param>
|
||||
/// <returns>Filled resource table on success, null on error</returns>
|
||||
public static ResourceTable? ParseResourceTable(Stream data, int count)
|
||||
public static ResourceTable? ParseResourceTable(Stream data, ushort count)
|
||||
{
|
||||
long initialOffset = data.Position;
|
||||
|
||||
@@ -270,13 +270,23 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var resourceTable = new ResourceTable();
|
||||
|
||||
resourceTable.AlignmentShiftCount = data.ReadUInt16();
|
||||
resourceTable.ResourceTypes = new ResourceTypeInformationEntry[count];
|
||||
for (int i = 0; i < resourceTable.ResourceTypes.Length; i++)
|
||||
var resourceTypes = new List<ResourceTypeInformationEntry>();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var entry = new ResourceTypeInformationEntry();
|
||||
|
||||
entry.TypeID = data.ReadUInt16();
|
||||
entry.ResourceCount = data.ReadUInt16();
|
||||
entry.Reserved = data.ReadUInt32();
|
||||
|
||||
// A zero type ID marks the end of the resource type information blocks.
|
||||
if (entry.TypeID == 0)
|
||||
{
|
||||
resourceTypes.Add(entry);
|
||||
break;
|
||||
}
|
||||
|
||||
entry.Resources = new ResourceTypeResourceEntry[entry.ResourceCount];
|
||||
for (int j = 0; j < entry.ResourceCount; j++)
|
||||
{
|
||||
@@ -287,20 +297,23 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
entry.Resources[j] = resource;
|
||||
}
|
||||
resourceTable.ResourceTypes[i] = entry;
|
||||
resourceTypes.Add(entry);
|
||||
}
|
||||
|
||||
resourceTable.ResourceTypes = [.. resourceTypes];
|
||||
|
||||
// Get the full list of unique string offsets
|
||||
var stringOffsets = resourceTable.ResourceTypes
|
||||
.Where(rt => rt != null)
|
||||
.Where(rt => rt!.IsIntegerType() == false)
|
||||
.Where(rt => !rt!.IsIntegerType() && rt!.TypeID != 0)
|
||||
.Select(rt => rt!.TypeID)
|
||||
.Union(resourceTable.ResourceTypes
|
||||
.Where(rt => rt != null)
|
||||
.Where(rt => rt != null && rt!.TypeID != 0)
|
||||
.SelectMany(rt => rt!.Resources ?? [])
|
||||
.Where(r => r!.IsIntegerType() == false)
|
||||
.Where(r => !r!.IsIntegerType())
|
||||
.Select(r => r!.ResourceID))
|
||||
.Distinct()
|
||||
.Where(o => o != 0)
|
||||
.OrderBy(o => o)
|
||||
.ToList();
|
||||
|
||||
@@ -359,7 +372,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// TODO: Use marshalling here instead of building
|
||||
var residentNameTable = new List<ResidentNameTableEntry>();
|
||||
|
||||
while (data.Position < endOffset)
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
var entry = ParseResidentNameTableEntry(data);
|
||||
if (entry == null)
|
||||
@@ -432,7 +445,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// TODO: Use marshalling here instead of building
|
||||
var importedNameTable = new Dictionary<ushort, ImportedNameTableEntry>();
|
||||
|
||||
while (data.Position < endOffset)
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
ushort currentOffset = (ushort)data.Position;
|
||||
var entry = ParseImportedNameTableEntry(data);
|
||||
@@ -472,7 +485,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// TODO: Use marshalling here instead of building
|
||||
var entryTable = new List<EntryTableBundle>();
|
||||
|
||||
while (data.Position < endOffset)
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
var entry = new EntryTableBundle();
|
||||
entry.EntryCount = data.ReadByteValue();
|
||||
@@ -511,7 +524,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// TODO: Use marshalling here instead of building
|
||||
var residentNameTable = new List<NonResidentNameTableEntry>();
|
||||
|
||||
while (data.Position < endOffset)
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
var entry = ParseNonResidentNameTableEntry(data);
|
||||
if (entry == null)
|
||||
|
||||
697
SabreTools.Serialization/Deserializers/PKZIP.cs
Normal file
697
SabreTools.Serialization/Deserializers/PKZIP.cs
Normal file
@@ -0,0 +1,697 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using SabreTools.IO.Extensions;
|
||||
using SabreTools.Models.PKZIP;
|
||||
using static SabreTools.Models.PKZIP.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Deserializers
|
||||
{
|
||||
public class PKZIP : BaseBinaryDeserializer<Archive>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
protected override bool SkipCompression => true;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Archive? Deserialize(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
return null;
|
||||
|
||||
// If the offset is out of bounds
|
||||
if (data.Position < 0 || data.Position >= data.Length)
|
||||
return null;
|
||||
|
||||
var archive = new Archive();
|
||||
|
||||
#region End of Central Directory Record
|
||||
|
||||
// Find the end of central directory record
|
||||
long eocdrOffset = SearchForEndOfCentralDirectoryRecord(data);
|
||||
if (eocdrOffset < 0 || eocdrOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the end of central directory record
|
||||
data.Seek(eocdrOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the end of central directory record
|
||||
var eocdr = ParseEndOfCentralDirectoryRecord(data);
|
||||
if (eocdr == null)
|
||||
return null;
|
||||
|
||||
// Assign the end of central directory record
|
||||
archive.EndOfCentralDirectoryRecord = eocdr;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ZIP64 End of Central Directory Locator and Record
|
||||
|
||||
// Set a flag for ZIP64 not found by default
|
||||
bool zip64 = false;
|
||||
|
||||
// Process ZIP64 if any fields are set to max value
|
||||
if (eocdr.DiskNumber == 0xFFFF
|
||||
|| eocdr.StartDiskNumber == 0xFFFF
|
||||
|| eocdr.TotalEntriesOnDisk == 0xFFFF
|
||||
|| eocdr.TotalEntries == 0xFFFF
|
||||
|| eocdr.CentralDirectorySize == 0xFFFFFFFF
|
||||
|| eocdr.CentralDirectoryOffset == 0xFFFFFFFF)
|
||||
{
|
||||
// Set the ZIP64 flag
|
||||
zip64 = true;
|
||||
|
||||
// Find the ZIP64 end of central directory locator
|
||||
long eocdlOffset = SearchForZIP64EndOfCentralDirectoryLocator(data);
|
||||
if (eocdlOffset < 0 || eocdlOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the ZIP64 end of central directory locator
|
||||
data.Seek(eocdlOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the ZIP64 end of central directory locator
|
||||
var eocdl64 = ParseEndOfCentralDirectoryLocator64(data);
|
||||
if (eocdl64 == null)
|
||||
return null;
|
||||
|
||||
// Assign the ZIP64 end of central directory record
|
||||
archive.ZIP64EndOfCentralDirectoryLocator = eocdl64;
|
||||
|
||||
// Try to get the ZIP64 end of central directory record offset
|
||||
if ((long)eocdl64.CentralDirectoryOffset < 0 || (long)eocdl64.CentralDirectoryOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the ZIP64 end of central directory record
|
||||
data.Seek((long)eocdl64.CentralDirectoryOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the ZIP64 end of central directory record
|
||||
var eocdr64 = ParseEndOfCentralDirectoryRecord64(data);
|
||||
if (eocdr64 == null)
|
||||
return null;
|
||||
|
||||
// Assign the ZIP64 end of central directory record
|
||||
archive.ZIP64EndOfCentralDirectoryRecord = eocdr64;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Central Directory Records
|
||||
|
||||
// Try to get the central directory record offset
|
||||
long cdrOffset, cdrSize;
|
||||
if (zip64 && archive.ZIP64EndOfCentralDirectoryRecord != null)
|
||||
{
|
||||
cdrOffset = (long)archive.ZIP64EndOfCentralDirectoryRecord.CentralDirectoryOffset;
|
||||
cdrSize = (long)archive.ZIP64EndOfCentralDirectoryRecord.CentralDirectorySize;
|
||||
}
|
||||
else if (archive.EndOfCentralDirectoryRecord != null)
|
||||
{
|
||||
cdrOffset = archive.EndOfCentralDirectoryRecord.CentralDirectoryOffset;
|
||||
cdrSize = archive.EndOfCentralDirectoryRecord.CentralDirectorySize;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try to get the central directory record offset
|
||||
if (cdrOffset < 0 || cdrOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the first central directory record
|
||||
data.Seek(cdrOffset, SeekOrigin.Begin);
|
||||
|
||||
// Cache the current offset
|
||||
long currentOffset = data.Position;
|
||||
|
||||
// Read the central directory records
|
||||
var cdrs = new List<CentralDirectoryFileHeader>();
|
||||
while (data.Position < currentOffset + cdrSize)
|
||||
{
|
||||
// Read the central directory record
|
||||
var cdr = ParseCentralDirectoryFileHeader(data);
|
||||
if (cdr == null)
|
||||
return null;
|
||||
|
||||
// Add the central directory record
|
||||
cdrs.Add(cdr);
|
||||
}
|
||||
|
||||
// Assign the central directory records
|
||||
archive.CentralDirectoryHeaders = [.. cdrs];
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Handle digital signature -- immediately following central directory records
|
||||
|
||||
#region Archive Extra Data Record
|
||||
|
||||
// Find the archive extra data record
|
||||
long aedrOffset = SearchForArchiveExtraDataRecord(data, cdrOffset);
|
||||
if (aedrOffset >= 0 && aedrOffset < data.Length)
|
||||
{
|
||||
// Seek to the archive extra data record
|
||||
data.Seek(aedrOffset, SeekOrigin.Begin);
|
||||
|
||||
// Read the archive extra data record
|
||||
var aedr = ParseArchiveExtraDataRecord(data);
|
||||
if (aedr == null)
|
||||
return null;
|
||||
|
||||
// Assign the archive extra data record
|
||||
archive.ArchiveExtraDataRecord = aedr;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Local File
|
||||
|
||||
// Setup all of the collections
|
||||
var localFileHeaders = new List<LocalFileHeader?>();
|
||||
var encryptionHeaders = new List<byte[]?>();
|
||||
var fileData = new List<byte[]>(); // TODO: Should this data be read here?
|
||||
var dataDescriptors = new List<DataDescriptor?>();
|
||||
var zip64DataDescriptors = new List<DataDescriptor64?>();
|
||||
|
||||
// Read the local file headers
|
||||
for (int i = 0; i < archive.CentralDirectoryHeaders.Length; i++)
|
||||
{
|
||||
var header = archive.CentralDirectoryHeaders[i];
|
||||
|
||||
// Get the local file header offset
|
||||
long headerOffset = header.RelativeOffsetOfLocalHeader;
|
||||
if (headerOffset == 0xFFFFFFFF && header.ExtraField != null)
|
||||
{
|
||||
// TODO: Parse into a proper structure instead of this
|
||||
byte[] extraData = header.ExtraField;
|
||||
if (BitConverter.ToUInt16(extraData, 0) == 0x0001)
|
||||
headerOffset = BitConverter.ToInt64(extraData, 4);
|
||||
}
|
||||
|
||||
if (headerOffset < 0 || headerOffset >= data.Length)
|
||||
return null;
|
||||
|
||||
// Seek to the local file header
|
||||
data.Seek(headerOffset, SeekOrigin.Begin);
|
||||
|
||||
// Try to parse the local header
|
||||
var localFileHeader = ParseLocalFileHeader(data);
|
||||
if (localFileHeader == null)
|
||||
{
|
||||
// Add a placeholder null item
|
||||
localFileHeaders.Add(null);
|
||||
encryptionHeaders.Add(null);
|
||||
fileData.Add([]);
|
||||
dataDescriptors.Add(null);
|
||||
zip64DataDescriptors.Add(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the local file header
|
||||
localFileHeaders.Add(localFileHeader);
|
||||
|
||||
// Only read the encryption header if necessary
|
||||
#if NET20 || NET35
|
||||
if ((header.Flags & GeneralPurposeBitFlags.FileEncrypted) != 0)
|
||||
#else
|
||||
if (header.Flags.HasFlag(GeneralPurposeBitFlags.FileEncrypted))
|
||||
#endif
|
||||
{
|
||||
// Try to read the encryption header data -- TODO: Verify amount to read
|
||||
byte[] encryptionHeader = data.ReadBytes(12);
|
||||
if (encryptionHeader.Length != 12)
|
||||
return null;
|
||||
|
||||
// Add the encryption header
|
||||
encryptionHeaders.Add(encryptionHeader);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the null encryption header
|
||||
encryptionHeaders.Add(null);
|
||||
}
|
||||
|
||||
// Try to read the file data
|
||||
byte[] fileDatum = data.ReadBytes((int)header.CompressedSize);
|
||||
if (fileDatum.Length < header.CompressedSize)
|
||||
return null;
|
||||
|
||||
// Add the file data
|
||||
fileData.Add(fileDatum);
|
||||
|
||||
// Only read the data descriptor if necessary
|
||||
#if NET20 || NET35
|
||||
if ((header.Flags & GeneralPurposeBitFlags.NoCRC) != 0)
|
||||
#else
|
||||
if (header.Flags.HasFlag(GeneralPurposeBitFlags.NoCRC))
|
||||
#endif
|
||||
{
|
||||
// Select the data descriptor that is being used
|
||||
if (zip64)
|
||||
{
|
||||
// Try to parse the data descriptor
|
||||
var dataDescriptor64 = ParseDataDescriptor64(data);
|
||||
if (dataDescriptor64 == null)
|
||||
return null;
|
||||
|
||||
// Add the data descriptor
|
||||
dataDescriptors.Add(null);
|
||||
zip64DataDescriptors.Add(dataDescriptor64);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to parse the data descriptor
|
||||
var dataDescriptor = ParseDataDescriptor(data);
|
||||
if (dataDescriptor == null)
|
||||
return null;
|
||||
|
||||
// Add the data descriptor
|
||||
dataDescriptors.Add(dataDescriptor);
|
||||
zip64DataDescriptors.Add(null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add the null data descriptor
|
||||
dataDescriptors.Add(null);
|
||||
zip64DataDescriptors.Add(null);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign the local file headers
|
||||
archive.LocalFileHeaders = [.. localFileHeaders];
|
||||
|
||||
// Assign the encryption headers
|
||||
archive.EncryptionHeaders = [.. encryptionHeaders];
|
||||
|
||||
// Assign the file data
|
||||
archive.FileData = [.. fileData];
|
||||
|
||||
// Assign the data descriptors
|
||||
archive.DataDescriptors = [.. dataDescriptors];
|
||||
archive.ZIP64DataDescriptors = [.. zip64DataDescriptors];
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Handle archive decryption header
|
||||
|
||||
return archive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for the end of central directory record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Position of the end of central directory record, -1 on error</returns>
|
||||
public static long SearchForEndOfCentralDirectoryRecord(Stream data)
|
||||
{
|
||||
// Cache the current offset
|
||||
long current = data.Position;
|
||||
|
||||
// Seek to the minimum size of the record from the end
|
||||
data.Seek(-22, SeekOrigin.End);
|
||||
|
||||
// Attempt to find the end of central directory signature
|
||||
while (data.Position > 0)
|
||||
{
|
||||
// Read the potential signature
|
||||
uint possibleSignature = data.ReadUInt32();
|
||||
if (possibleSignature == EndOfCentralDirectoryRecordSignature)
|
||||
{
|
||||
long signaturePosition = data.Position - 4;
|
||||
data.Seek(current, SeekOrigin.Begin);
|
||||
return signaturePosition;
|
||||
}
|
||||
|
||||
// If we find any other signature
|
||||
switch (possibleSignature)
|
||||
{
|
||||
case ArchiveExtraDataRecordSignature:
|
||||
case CentralDirectoryFileHeaderSignature:
|
||||
case DataDescriptorSignature:
|
||||
case DigitalSignatureSignature:
|
||||
case EndOfCentralDirectoryLocator64Signature:
|
||||
case EndOfCentralDirectoryRecord64Signature:
|
||||
case LocalFileHeaderSignature:
|
||||
data.Seek(current, SeekOrigin.Begin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Seek backward 5 bytes, if possible
|
||||
data.Seek(-5, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
// No signature was found
|
||||
data.Seek(current, SeekOrigin.Begin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an end of central directory record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled end of central directory record on success, null on error</returns>
|
||||
public static EndOfCentralDirectoryRecord? ParseEndOfCentralDirectoryRecord(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new EndOfCentralDirectoryRecord();
|
||||
|
||||
record.Signature = data.ReadUInt32();
|
||||
if (record.Signature != EndOfCentralDirectoryRecordSignature)
|
||||
return null;
|
||||
|
||||
record.DiskNumber = data.ReadUInt16();
|
||||
record.StartDiskNumber = data.ReadUInt16();
|
||||
record.TotalEntriesOnDisk = data.ReadUInt16();
|
||||
record.TotalEntries = data.ReadUInt16();
|
||||
record.CentralDirectorySize = data.ReadUInt32();
|
||||
record.CentralDirectoryOffset = data.ReadUInt32();
|
||||
record.FileCommentLength = data.ReadUInt16();
|
||||
if (record.FileCommentLength > 0 && data.Position + record.FileCommentLength <= data.Length)
|
||||
{
|
||||
byte[] commentBytes = data.ReadBytes(record.FileCommentLength);
|
||||
if (commentBytes.Length != record.FileCommentLength)
|
||||
return null;
|
||||
|
||||
record.FileComment = Encoding.ASCII.GetString(commentBytes);
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for the ZIP64 end of central directory locator
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Position of the ZIP64 end of central directory locator, -1 on error</returns>
|
||||
public static long SearchForZIP64EndOfCentralDirectoryLocator(Stream data)
|
||||
{
|
||||
// Cache the current offset
|
||||
long current = data.Position;
|
||||
|
||||
// Seek to the minimum size of the record from the minimum start
|
||||
// of theend of central directory record
|
||||
data.Seek(-22 + -20, SeekOrigin.Current);
|
||||
|
||||
// Attempt to find the ZIP64 end of central directory locator signature
|
||||
while (data.Position > 0)
|
||||
{
|
||||
// Read the potential signature
|
||||
uint possibleSignature = data.ReadUInt32();
|
||||
if (possibleSignature == EndOfCentralDirectoryLocator64Signature)
|
||||
{
|
||||
long signaturePosition = data.Position - 4;
|
||||
data.Seek(current, SeekOrigin.Begin);
|
||||
return signaturePosition;
|
||||
}
|
||||
|
||||
// If we find any other signature
|
||||
switch (possibleSignature)
|
||||
{
|
||||
case ArchiveExtraDataRecordSignature:
|
||||
case CentralDirectoryFileHeaderSignature:
|
||||
case DataDescriptorSignature:
|
||||
case DigitalSignatureSignature:
|
||||
case EndOfCentralDirectoryRecordSignature:
|
||||
case EndOfCentralDirectoryRecord64Signature:
|
||||
case LocalFileHeaderSignature:
|
||||
data.Seek(current, SeekOrigin.Begin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Seek backward 5 bytes, if possible
|
||||
data.Seek(-5, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
// No signature was found
|
||||
data.Seek(current, SeekOrigin.Begin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ZIP64 end of central directory locator
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ZIP64 end of central directory locator on success, null on error</returns>
|
||||
public static EndOfCentralDirectoryLocator64? ParseEndOfCentralDirectoryLocator64(Stream data)
|
||||
{
|
||||
return data.ReadType<EndOfCentralDirectoryLocator64>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ZIP64 end of central directory record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ZIP64 end of central directory record on success, null on error</returns>
|
||||
public static EndOfCentralDirectoryRecord64? ParseEndOfCentralDirectoryRecord64(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new EndOfCentralDirectoryRecord64();
|
||||
|
||||
record.Signature = data.ReadUInt32();
|
||||
if (record.Signature != EndOfCentralDirectoryRecord64Signature)
|
||||
return null;
|
||||
|
||||
record.DirectoryRecordSize = data.ReadUInt64();
|
||||
record.HostSystem = (HostSystem)data.ReadByteValue();
|
||||
record.VersionMadeBy = data.ReadByteValue();
|
||||
record.VersionNeededToExtract = data.ReadUInt16();
|
||||
record.DiskNumber = data.ReadUInt32();
|
||||
record.StartDiskNumber = data.ReadUInt32();
|
||||
record.TotalEntriesOnDisk = data.ReadUInt64();
|
||||
record.TotalEntries = data.ReadUInt64();
|
||||
record.CentralDirectorySize = data.ReadUInt64();
|
||||
record.CentralDirectoryOffset = data.ReadUInt64();
|
||||
|
||||
// TODO: Handle the ExtensibleDataSector -- How to detect if exists?
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a central directory file header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled central directory file header on success, null on error</returns>
|
||||
public static CentralDirectoryFileHeader? ParseCentralDirectoryFileHeader(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var header = new CentralDirectoryFileHeader();
|
||||
|
||||
header.Signature = data.ReadUInt32();
|
||||
if (header.Signature != CentralDirectoryFileHeaderSignature)
|
||||
return null;
|
||||
|
||||
header.HostSystem = (HostSystem)data.ReadByteValue();
|
||||
header.VersionMadeBy = data.ReadByteValue();
|
||||
header.VersionNeededToExtract = data.ReadUInt16();
|
||||
header.Flags = (GeneralPurposeBitFlags)data.ReadUInt16();
|
||||
header.CompressionMethod = (CompressionMethod)data.ReadUInt16();
|
||||
header.LastModifedFileTime = data.ReadUInt16();
|
||||
header.LastModifiedFileDate = data.ReadUInt16();
|
||||
header.CRC32 = data.ReadUInt32();
|
||||
header.CompressedSize = data.ReadUInt32();
|
||||
header.UncompressedSize = data.ReadUInt32();
|
||||
header.FileNameLength = data.ReadUInt16();
|
||||
header.ExtraFieldLength = data.ReadUInt16();
|
||||
header.FileCommentLength = data.ReadUInt16();
|
||||
header.DiskNumberStart = data.ReadUInt16();
|
||||
header.InternalFileAttributes = (InternalFileAttributes)data.ReadUInt16();
|
||||
header.ExternalFileAttributes = data.ReadUInt32();
|
||||
header.RelativeOffsetOfLocalHeader = data.ReadUInt32();
|
||||
|
||||
if (header.FileNameLength > 0 && data.Position + header.FileNameLength <= data.Length)
|
||||
{
|
||||
byte[] filenameBytes = data.ReadBytes(header.FileNameLength);
|
||||
if (filenameBytes.Length != header.FileNameLength)
|
||||
return null;
|
||||
|
||||
header.FileName = Encoding.ASCII.GetString(filenameBytes);
|
||||
}
|
||||
if (header.ExtraFieldLength > 0 && data.Position + header.ExtraFieldLength <= data.Length)
|
||||
{
|
||||
byte[] extraBytes = data.ReadBytes(header.ExtraFieldLength);
|
||||
if (extraBytes.Length != header.ExtraFieldLength)
|
||||
return null;
|
||||
|
||||
header.ExtraField = extraBytes;
|
||||
}
|
||||
if (header.FileCommentLength > 0 && data.Position + header.FileCommentLength <= data.Length)
|
||||
{
|
||||
byte[] commentBytes = data.ReadBytes(header.FileCommentLength);
|
||||
if (commentBytes.Length != header.FileCommentLength)
|
||||
return null;
|
||||
|
||||
header.FileComment = Encoding.ASCII.GetString(commentBytes);
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for the archive extra data record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="centralDirectoryoffset">Offset to the first central directory record</param>
|
||||
/// <returns>Position of the archive extra data record, -1 on error</returns>
|
||||
public static long SearchForArchiveExtraDataRecord(Stream data, long centralDirectoryoffset)
|
||||
{
|
||||
// Cache the current offset
|
||||
long current = data.Position;
|
||||
|
||||
// Seek to the minimum size of the record from the central directory
|
||||
data.Seek(centralDirectoryoffset - 8, SeekOrigin.Begin);
|
||||
|
||||
// Attempt to find the end of central directory signature
|
||||
while (data.Position > 0)
|
||||
{
|
||||
// Read the potential signature
|
||||
uint possibleSignature = data.ReadUInt32();
|
||||
if (possibleSignature == ArchiveExtraDataRecordSignature)
|
||||
{
|
||||
long signaturePosition = data.Position - 4;
|
||||
data.Seek(current, SeekOrigin.Begin);
|
||||
return signaturePosition;
|
||||
}
|
||||
|
||||
// If we find any other signature
|
||||
switch (possibleSignature)
|
||||
{
|
||||
case CentralDirectoryFileHeaderSignature:
|
||||
case DataDescriptorSignature:
|
||||
case DigitalSignatureSignature:
|
||||
case EndOfCentralDirectoryLocator64Signature:
|
||||
case EndOfCentralDirectoryRecordSignature:
|
||||
case EndOfCentralDirectoryRecord64Signature:
|
||||
case LocalFileHeaderSignature:
|
||||
data.Seek(current, SeekOrigin.Begin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Seek backward 5 bytes, if possible
|
||||
data.Seek(-5, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
// No signature was found
|
||||
data.Seek(current, SeekOrigin.Begin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an archive extra data record
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled archive extra data record on success, null on error</returns>
|
||||
public static ArchiveExtraDataRecord? ParseArchiveExtraDataRecord(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var record = new ArchiveExtraDataRecord();
|
||||
|
||||
record.Signature = data.ReadUInt32();
|
||||
if (record.Signature != ArchiveExtraDataRecordSignature)
|
||||
return null;
|
||||
|
||||
record.ExtraFieldLength = data.ReadUInt32();
|
||||
if (record.ExtraFieldLength > 0 && data.Position + record.ExtraFieldLength <= data.Length)
|
||||
{
|
||||
byte[] extraBytes = data.ReadBytes((int)record.ExtraFieldLength);
|
||||
if (extraBytes.Length != record.ExtraFieldLength)
|
||||
return null;
|
||||
|
||||
record.ExtraFieldData = extraBytes;
|
||||
}
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a local file header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled local file header on success, null on error</returns>
|
||||
public static LocalFileHeader? ParseLocalFileHeader(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var header = new LocalFileHeader();
|
||||
|
||||
header.Signature = data.ReadUInt32();
|
||||
if (header.Signature != LocalFileHeaderSignature)
|
||||
return null;
|
||||
|
||||
header.Version = data.ReadUInt16();
|
||||
header.Flags = (GeneralPurposeBitFlags)data.ReadUInt16();
|
||||
header.CompressionMethod = (CompressionMethod)data.ReadUInt16();
|
||||
header.LastModifedFileTime = data.ReadUInt16();
|
||||
header.LastModifiedFileDate = data.ReadUInt16();
|
||||
header.CRC32 = data.ReadUInt32();
|
||||
header.CompressedSize = data.ReadUInt32();
|
||||
header.UncompressedSize = data.ReadUInt32();
|
||||
header.FileNameLength = data.ReadUInt16();
|
||||
header.ExtraFieldLength = data.ReadUInt16();
|
||||
|
||||
if (header.FileNameLength > 0 && data.Position + header.FileNameLength <= data.Length)
|
||||
{
|
||||
byte[] filenameBytes = data.ReadBytes(header.FileNameLength);
|
||||
if (filenameBytes.Length != header.FileNameLength)
|
||||
return null;
|
||||
|
||||
header.FileName = Encoding.ASCII.GetString(filenameBytes);
|
||||
}
|
||||
if (header.ExtraFieldLength > 0 && data.Position + header.ExtraFieldLength <= data.Length)
|
||||
{
|
||||
byte[] extraBytes = data.ReadBytes(header.ExtraFieldLength);
|
||||
if (extraBytes.Length != header.ExtraFieldLength)
|
||||
return null;
|
||||
|
||||
header.ExtraField = extraBytes;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a data descriptor
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled data descriptor on success, null on error</returns>
|
||||
public static DataDescriptor? ParseDataDescriptor(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var dataDescriptor = new DataDescriptor();
|
||||
|
||||
// Signatures are expected but not required
|
||||
dataDescriptor.Signature = data.ReadUInt32();
|
||||
if (dataDescriptor.Signature != DataDescriptorSignature)
|
||||
data.Seek(-4, SeekOrigin.Current);
|
||||
|
||||
dataDescriptor.CRC32 = data.ReadUInt32();
|
||||
dataDescriptor.CompressedSize = data.ReadUInt32();
|
||||
dataDescriptor.UncompressedSize = data.ReadUInt32();
|
||||
|
||||
return dataDescriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a ZIP64 data descriptor
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled ZIP64 data descriptor on success, null on error</returns>
|
||||
public static DataDescriptor64? ParseDataDescriptor64(Stream data)
|
||||
{
|
||||
// TODO: Use marshalling here instead of building
|
||||
var zip64DataDescriptor = new DataDescriptor64();
|
||||
|
||||
// Signatures are expected but not required
|
||||
zip64DataDescriptor.Signature = data.ReadUInt32();
|
||||
if (zip64DataDescriptor.Signature != DataDescriptorSignature)
|
||||
data.Seek(-4, SeekOrigin.Current);
|
||||
|
||||
zip64DataDescriptor.CRC32 = data.ReadUInt32();
|
||||
zip64DataDescriptor.CompressedSize = data.ReadUInt64();
|
||||
zip64DataDescriptor.UncompressedSize = data.ReadUInt64();
|
||||
|
||||
return zip64DataDescriptor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -495,7 +495,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
entry.Value = data.ReadUInt32();
|
||||
entry.SectionNumber = data.ReadUInt16();
|
||||
entry.SymbolType = (SymbolType)data.ReadUInt16();
|
||||
entry.StorageClass = (StorageClass)data.ReadByte();
|
||||
entry.StorageClass = (StorageClass)data.ReadByteValue();
|
||||
entry.NumberOfAuxSymbols = data.ReadByteValue();
|
||||
coffSymbolTable[i] = entry;
|
||||
|
||||
@@ -668,7 +668,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
entry.CertificateType = (WindowsCertificateType)data.ReadUInt16();
|
||||
|
||||
int certificateDataLength = (int)(entry.Length - 8);
|
||||
if (certificateDataLength > 0)
|
||||
if (certificateDataLength > 0 && data.Position + certificateDataLength <= data.Length)
|
||||
entry.Certificate = data.ReadBytes(certificateDataLength);
|
||||
|
||||
attributeCertificateTable.Add(entry);
|
||||
@@ -703,7 +703,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
// TODO: Use marshalling here instead of building
|
||||
var baseRelocationTable = new List<BaseRelocationBlock>();
|
||||
|
||||
while (data.Position < endOffset)
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
var baseRelocationBlock = new BaseRelocationBlock();
|
||||
|
||||
@@ -724,12 +724,12 @@ namespace SabreTools.Serialization.Deserializers
|
||||
totalSize += 2;
|
||||
}
|
||||
|
||||
baseRelocationBlock.TypeOffsetFieldEntries = typeOffsetFieldEntries.ToArray();
|
||||
baseRelocationBlock.TypeOffsetFieldEntries = [.. typeOffsetFieldEntries];
|
||||
|
||||
baseRelocationTable.Add(baseRelocationBlock);
|
||||
}
|
||||
|
||||
return baseRelocationTable.ToArray();
|
||||
return [.. baseRelocationTable];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -745,7 +745,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
var debugDirectoryTable = new List<DebugDirectoryEntry>();
|
||||
|
||||
while (data.Position < endOffset)
|
||||
while (data.Position < endOffset && data.Position < data.Length)
|
||||
{
|
||||
var debugDirectoryEntry = data.ReadType<DebugDirectoryEntry>();
|
||||
if (debugDirectoryEntry == null)
|
||||
@@ -1164,7 +1164,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
var resourceDirectoryString = new ResourceDirectoryString();
|
||||
|
||||
resourceDirectoryString.Length = data.ReadUInt16();
|
||||
if (resourceDirectoryString.Length > 0)
|
||||
if (resourceDirectoryString.Length > 0 && data.Position + resourceDirectoryString.Length <= data.Length)
|
||||
resourceDirectoryString.UnicodeString = data.ReadBytes(resourceDirectoryString.Length * 2);
|
||||
|
||||
entry.Name = resourceDirectoryString;
|
||||
@@ -1194,7 +1194,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Read the data from the offset
|
||||
offset = resourceDataEntry.DataRVA.ConvertVirtualAddress(sections);
|
||||
if (offset > 0 && resourceDataEntry.Size > 0)
|
||||
if (offset > 0 && resourceDataEntry.Size > 0 && offset + (int)resourceDataEntry.Size < data.Length)
|
||||
{
|
||||
data.Seek(offset, SeekOrigin.Begin);
|
||||
resourceDataEntry.Data = data.ReadBytes((int)resourceDataEntry.Size);
|
||||
@@ -1246,7 +1246,7 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
resourceDirectoryTable.Entries[totalEntryCount] = new ResourceDirectoryEntry
|
||||
{
|
||||
Name = new ResourceDirectoryString { UnicodeString = Encoding.ASCII.GetBytes("HIDDEN RESOURCE") },
|
||||
Name = new ResourceDirectoryString { UnicodeString = Encoding.Unicode.GetBytes("HIDDEN RESOURCE") },
|
||||
IntegerID = uint.MaxValue,
|
||||
DataEntryOffset = (uint)data.Position,
|
||||
DataEntry = new ResourceDataEntry
|
||||
|
||||
@@ -67,7 +67,10 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
#region Archive Hashes
|
||||
|
||||
if (header?.Version == 2 && file.ExtendedHeader != null && file.ExtendedHeader.ArchiveHashLength > 0)
|
||||
if (header?.Version == 2
|
||||
&& file.ExtendedHeader != null
|
||||
&& file.ExtendedHeader.ArchiveHashLength > 0
|
||||
&& data.Position + file.ExtendedHeader.ArchiveHashLength <= data.Length)
|
||||
{
|
||||
// Create the archive hashes list
|
||||
var archiveHashes = new List<ArchiveHash>();
|
||||
@@ -218,7 +221,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// Get the preload data pointer
|
||||
long preloadDataPointer = -1; int preloadDataLength = -1;
|
||||
if (directoryEntry.ArchiveIndex == HL_VPK_NO_ARCHIVE && directoryEntry.EntryLength > 0)
|
||||
if (directoryEntry.ArchiveIndex == HL_VPK_NO_ARCHIVE
|
||||
&& directoryEntry.EntryLength > 0
|
||||
&& data.Position + directoryEntry.EntryLength <= data.Length)
|
||||
{
|
||||
preloadDataPointer = directoryEntry.EntryOffset;
|
||||
preloadDataLength = (int)directoryEntry.EntryLength;
|
||||
@@ -231,7 +236,9 @@ namespace SabreTools.Serialization.Deserializers
|
||||
|
||||
// If we had a valid preload data pointer
|
||||
byte[]? preloadData = null;
|
||||
if (preloadDataPointer >= 0 && preloadDataLength > 0)
|
||||
if (preloadDataPointer >= 0
|
||||
&& preloadDataLength > 0
|
||||
&& data.Position + preloadDataLength <= data.Length)
|
||||
{
|
||||
// Cache the current offset
|
||||
long initialOffset = data.Position;
|
||||
|
||||
@@ -9,13 +9,8 @@ namespace SabreTools.Serialization
|
||||
/// </summary>
|
||||
/// <param name="entry">Resource type information entry to check</param>
|
||||
/// <returns>True if the entry is an integer type, false if an offset, null on error</returns>
|
||||
public static bool? IsIntegerType(this ResourceTypeInformationEntry entry)
|
||||
public static bool IsIntegerType(this ResourceTypeInformationEntry entry)
|
||||
{
|
||||
// We can't do anything with an invalid entry
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
// If the highest order bit is set, it's an integer type
|
||||
return (entry.TypeID & 0x8000) != 0;
|
||||
}
|
||||
|
||||
@@ -24,13 +19,8 @@ namespace SabreTools.Serialization
|
||||
/// </summary>
|
||||
/// <param name="entry">Resource type resource entry to check</param>
|
||||
/// <returns>True if the entry is an integer type, false if an offset, null on error</returns>
|
||||
public static bool? IsIntegerType(this ResourceTypeResourceEntry entry)
|
||||
public static bool IsIntegerType(this ResourceTypeResourceEntry entry)
|
||||
{
|
||||
// We can't do anything with an invalid entry
|
||||
if (entry == null)
|
||||
return null;
|
||||
|
||||
// If the highest order bit is set, it's an integer type
|
||||
return (entry.ResourceID & 0x8000) != 0;
|
||||
}
|
||||
|
||||
@@ -41,10 +31,6 @@ namespace SabreTools.Serialization
|
||||
/// <returns>SegmentEntryType corresponding to the type</returns>
|
||||
public static SegmentEntryType GetEntryType(this EntryTableBundle entry)
|
||||
{
|
||||
// We can't do anything with an invalid entry
|
||||
if (entry == null)
|
||||
return SegmentEntryType.Unused;
|
||||
|
||||
// Determine the entry type based on segment indicator
|
||||
if (entry.SegmentIndicator == 0x00)
|
||||
return SegmentEntryType.Unused;
|
||||
|
||||
@@ -127,6 +127,15 @@ namespace SabreTools.Serialization
|
||||
if (rsdsProgramDatabase.Signature != 0x53445352)
|
||||
return null;
|
||||
|
||||
#if NET20 || NET35 || NET40 || NET452 || NET462
|
||||
// Convert ASCII string to UTF-8
|
||||
if (rsdsProgramDatabase.PathAndFileName != null)
|
||||
{
|
||||
byte[] bytes = Encoding.ASCII.GetBytes(rsdsProgramDatabase.PathAndFileName);
|
||||
rsdsProgramDatabase.PathAndFileName = Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
#endif
|
||||
|
||||
return rsdsProgramDatabase;
|
||||
}
|
||||
|
||||
@@ -595,7 +604,11 @@ namespace SabreTools.Serialization
|
||||
#region Class resource
|
||||
|
||||
currentOffset = offset;
|
||||
ushort classResourceIdentifier = entry.Data.ReadUInt16(ref offset);
|
||||
ushort classResourceIdentifier;
|
||||
if (offset >= entry.Data.Length)
|
||||
classResourceIdentifier = 0x0000;
|
||||
else
|
||||
classResourceIdentifier = entry.Data.ReadUInt16(ref offset);
|
||||
offset = currentOffset;
|
||||
|
||||
// 0x0000 means no elements
|
||||
@@ -631,7 +644,11 @@ namespace SabreTools.Serialization
|
||||
#region Title resource
|
||||
|
||||
currentOffset = offset;
|
||||
ushort titleResourceIdentifier = entry.Data.ReadUInt16(ref offset);
|
||||
ushort titleResourceIdentifier;
|
||||
if (offset >= entry.Data.Length)
|
||||
titleResourceIdentifier = 0x0000;
|
||||
else
|
||||
titleResourceIdentifier = entry.Data.ReadUInt16(ref offset);
|
||||
offset = currentOffset;
|
||||
|
||||
// 0x0000 means no elements
|
||||
@@ -891,7 +908,7 @@ namespace SabreTools.Serialization
|
||||
if (menuHeaderExtended == null)
|
||||
return null;
|
||||
|
||||
menuResource.ExtendedMenuHeader = menuHeaderExtended;
|
||||
menuResource.MenuHeader = menuHeaderExtended;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -920,7 +937,7 @@ namespace SabreTools.Serialization
|
||||
}
|
||||
}
|
||||
|
||||
menuResource.ExtendedMenuItems = [.. extendedMenuItems];
|
||||
menuResource.MenuItems = [.. extendedMenuItems];
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -928,7 +945,7 @@ namespace SabreTools.Serialization
|
||||
{
|
||||
#region Menu header
|
||||
|
||||
var menuHeader = entry.Data.ReadType<MenuHeader>(ref offset);
|
||||
var menuHeader = entry.Data.ReadType<NormalMenuHeader>(ref offset);
|
||||
if (menuHeader == null)
|
||||
return null;
|
||||
|
||||
@@ -1067,21 +1084,9 @@ namespace SabreTools.Serialization
|
||||
// Loop through and add
|
||||
while (offset < entry.Data.Length)
|
||||
{
|
||||
ushort stringLength = entry.Data.ReadUInt16(ref offset);
|
||||
if (stringLength == 0)
|
||||
string? stringValue = entry.Data.ReadPrefixedUnicodeString(ref offset);
|
||||
if (stringValue != null)
|
||||
{
|
||||
stringTable[stringIndex++] = "[EMPTY]";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stringLength * 2 > entry.Data.Length - offset)
|
||||
{
|
||||
Console.WriteLine($"{stringLength * 2} requested but {entry.Data.Length - offset} remains");
|
||||
stringLength = (ushort)((entry.Data.Length - offset) / 2);
|
||||
}
|
||||
|
||||
string stringValue = Encoding.Unicode.GetString(entry.Data, offset, stringLength * 2);
|
||||
offset += stringLength * 2;
|
||||
stringValue = stringValue.Replace("\n", "\\n").Replace("\r", newValue: "\\r");
|
||||
stringTable[stringIndex++] = stringValue;
|
||||
}
|
||||
@@ -1118,7 +1123,7 @@ namespace SabreTools.Serialization
|
||||
versionInfo.Padding1 = entry.Data.ReadUInt16(ref offset);
|
||||
|
||||
// Read fixed file info
|
||||
if (versionInfo.ValueLength > 0)
|
||||
if (versionInfo.ValueLength > 0 && offset + versionInfo.ValueLength <= entry.Data.Length)
|
||||
{
|
||||
var fixedFileInfo = entry.Data.ReadType<FixedFileInfo>(ref offset);
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace SabreTools.Serialization
|
||||
/// </summary>
|
||||
/// <param name="path">Path to open as a stream</param>
|
||||
/// <returns>Stream representing the file, null on error</returns>
|
||||
public static Stream? OpenStream(string? path)
|
||||
public static Stream? OpenStream(string? path, bool skipCompression = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -26,11 +26,11 @@ namespace SabreTools.Serialization
|
||||
string ext = Path.GetExtension(path).TrimStart('.');
|
||||
|
||||
// Determine what we do based on the extension
|
||||
if (string.Equals(ext, "gz", StringComparison.OrdinalIgnoreCase))
|
||||
if (!skipCompression && string.Equals(ext, "gz", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return new GZipStream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
else if (string.Equals(ext, "zip", StringComparison.OrdinalIgnoreCase))
|
||||
else if (!skipCompression && string.Equals(ext, "zip", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// TODO: Support zip-compressed files
|
||||
return null;
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace SabreTools.Serialization
|
||||
Wrapper.PAK item => item.PrettyPrint(),
|
||||
Wrapper.PFF item => item.PrettyPrint(),
|
||||
Wrapper.PIC item => item.PrettyPrint(),
|
||||
Wrapper.PKZIP item => item.PrettyPrint(),
|
||||
Wrapper.PlayJAudioFile item => item.PrettyPrint(),
|
||||
Wrapper.PlayJPlaylist item => item.PrettyPrint(),
|
||||
Wrapper.PortableExecutable item => item.PrettyPrint(),
|
||||
@@ -97,6 +98,7 @@ namespace SabreTools.Serialization
|
||||
Wrapper.PAK item => item.ExportJSON(),
|
||||
Wrapper.PFF item => item.ExportJSON(),
|
||||
Wrapper.PIC item => item.ExportJSON(),
|
||||
Wrapper.PKZIP item => item.ExportJSON(),
|
||||
Wrapper.PlayJAudioFile item => item.ExportJSON(),
|
||||
Wrapper.PlayJPlaylist item => item.ExportJSON(),
|
||||
Wrapper.PortableExecutable item => item.ExportJSON(),
|
||||
@@ -314,6 +316,16 @@ namespace SabreTools.Serialization
|
||||
PIC.Print(builder, item.Model);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
/// </summary>
|
||||
private static StringBuilder PrettyPrint(this Wrapper.PKZIP item)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
PKZIP.Print(builder, item.Model);
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Export the item information as pretty-printed text
|
||||
|
||||
@@ -188,7 +188,7 @@ namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
// TODO: If not integer type, print out name
|
||||
var resource = entry.Resources[j];
|
||||
builder.AppendLine($" Resource Entry {i}");
|
||||
builder.AppendLine($" Resource Entry {j}");
|
||||
if (resource == null)
|
||||
{
|
||||
builder.AppendLine(" [NULL]");
|
||||
|
||||
279
SabreTools.Serialization/Printers/PKZIP.cs
Normal file
279
SabreTools.Serialization/Printers/PKZIP.cs
Normal file
@@ -0,0 +1,279 @@
|
||||
using System.Text;
|
||||
using SabreTools.Models.PKZIP;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
|
||||
namespace SabreTools.Serialization.Printers
|
||||
{
|
||||
public class PKZIP : IPrinter<Archive>
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public void PrintInformation(StringBuilder builder, Archive model)
|
||||
=> Print(builder, model);
|
||||
|
||||
public static void Print(StringBuilder builder, Archive archive)
|
||||
{
|
||||
builder.AppendLine("PKZIP Archive (or Derived Format) Information:");
|
||||
builder.AppendLine("-------------------------");
|
||||
builder.AppendLine();
|
||||
|
||||
Print(builder, archive.EndOfCentralDirectoryRecord);
|
||||
Print(builder, archive.ZIP64EndOfCentralDirectoryLocator);
|
||||
Print(builder, archive.ZIP64EndOfCentralDirectoryRecord);
|
||||
Print(builder, archive.CentralDirectoryHeaders);
|
||||
Print(builder, archive.ArchiveExtraDataRecord);
|
||||
Print(builder,
|
||||
archive.LocalFileHeaders,
|
||||
archive.EncryptionHeaders,
|
||||
archive.FileData,
|
||||
archive.DataDescriptors,
|
||||
archive.ZIP64DataDescriptors);
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, EndOfCentralDirectoryRecord? record)
|
||||
{
|
||||
builder.AppendLine(" End of Central Directory Record Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (record == null)
|
||||
{
|
||||
builder.AppendLine(" No end of central directory record");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(record.Signature, " Signature");
|
||||
builder.AppendLine(record.DiskNumber, " Disk number");
|
||||
builder.AppendLine(record.StartDiskNumber, " Start disk number");
|
||||
builder.AppendLine(record.TotalEntriesOnDisk, " Total entries on disk");
|
||||
builder.AppendLine(record.TotalEntries, " Total entries");
|
||||
builder.AppendLine(record.CentralDirectorySize, " Central directory size");
|
||||
builder.AppendLine(record.CentralDirectoryOffset, " Central directory offset");
|
||||
builder.AppendLine(record.FileCommentLength, " File comment length");
|
||||
builder.AppendLine(record.FileComment, " File comment");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, EndOfCentralDirectoryLocator64? locator)
|
||||
{
|
||||
builder.AppendLine(" ZIP64 End of Central Directory Locator Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (locator == null)
|
||||
{
|
||||
builder.AppendLine(" No ZIP64 end of central directory locator");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(locator.Signature, " Signature");
|
||||
builder.AppendLine(locator.StartDiskNumber, " Start disk number");
|
||||
builder.AppendLine(locator.CentralDirectoryOffset, " Central directory offset");
|
||||
builder.AppendLine(locator.TotalDisks, " Total disks");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, EndOfCentralDirectoryRecord64? record)
|
||||
{
|
||||
builder.AppendLine(" ZIP64 End of Central Directory Record Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (record == null)
|
||||
{
|
||||
builder.AppendLine(" No ZIP64 end of central directory record");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(record.Signature, " Signature");
|
||||
builder.AppendLine(record.DirectoryRecordSize, " Directory record size");
|
||||
builder.AppendLine($" Host system: {record.HostSystem} (0x{record.HostSystem:X})");
|
||||
builder.AppendLine(record.VersionMadeBy, " Version made by");
|
||||
builder.AppendLine(record.VersionNeededToExtract, " Version needed to extract");
|
||||
builder.AppendLine(record.DiskNumber, " Disk number");
|
||||
builder.AppendLine(record.StartDiskNumber, " Start disk number");
|
||||
builder.AppendLine(record.TotalEntriesOnDisk, " Total entries on disk");
|
||||
builder.AppendLine(record.TotalEntries, " Total entries");
|
||||
builder.AppendLine(record.CentralDirectorySize, " Central directory size");
|
||||
builder.AppendLine(record.CentralDirectoryOffset, " Central directory offset");
|
||||
//builder.AppendLine(record.ExtensibleDataSector, " Extensible data sector");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, CentralDirectoryFileHeader?[]? headers)
|
||||
{
|
||||
builder.AppendLine(" Central Directory File Headers Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (headers == null || headers.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No central directory file headers");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < headers.Length; i++)
|
||||
{
|
||||
var record = headers[i];
|
||||
Print(builder, record, i);
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, CentralDirectoryFileHeader? header, int index)
|
||||
{
|
||||
builder.AppendLine($" Central Directory File Header Entry {index}");
|
||||
if (header == null)
|
||||
{
|
||||
builder.AppendLine(" [NULL]");
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(header.Signature, " Signature");
|
||||
builder.AppendLine($" Host system: {header.HostSystem} (0x{header.HostSystem:X})");
|
||||
builder.AppendLine(header.VersionMadeBy, " Version made by");
|
||||
builder.AppendLine(header.VersionNeededToExtract, " Version needed to extract");
|
||||
builder.AppendLine($" Flags: {header.Flags} (0x{header.Flags:X})");
|
||||
builder.AppendLine($" Compression method: {header.CompressionMethod} (0x{header.CompressionMethod:X})");
|
||||
builder.AppendLine(header.LastModifedFileTime, " Last modified file time"); // TODO: Parse from MS-DOS
|
||||
builder.AppendLine(header.LastModifiedFileDate, " Last modified file date"); // TODO: Parse from MS-DOS
|
||||
builder.AppendLine(header.CRC32, " CRC-32");
|
||||
builder.AppendLine(header.CompressedSize, " Compressed size");
|
||||
builder.AppendLine(header.UncompressedSize, " Uncompressed size");
|
||||
builder.AppendLine(header.FileNameLength, " File name length");
|
||||
builder.AppendLine(header.ExtraFieldLength, " Extra field length");
|
||||
builder.AppendLine(header.FileCommentLength, " File comment length");
|
||||
builder.AppendLine(header.DiskNumberStart, " Disk number start");
|
||||
builder.AppendLine($" Internal file attributes: {header.InternalFileAttributes} (0x{header.InternalFileAttributes:X})");
|
||||
builder.AppendLine(header.ExternalFileAttributes, " External file attributes");
|
||||
builder.AppendLine(header.RelativeOffsetOfLocalHeader, " Relative offset of local header");
|
||||
builder.AppendLine(header.FileName, " File name");
|
||||
builder.AppendLine(header.ExtraField, " Extra field");
|
||||
builder.AppendLine(header.FileComment, " File comment");
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder, ArchiveExtraDataRecord? record)
|
||||
{
|
||||
builder.AppendLine(" Archive Extra Data Record Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (record == null)
|
||||
{
|
||||
builder.AppendLine(" No archive extra data record");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
builder.AppendLine(record.Signature, " Signature");
|
||||
builder.AppendLine(record.ExtraFieldLength, " Extra field length");
|
||||
builder.AppendLine(record.ExtraFieldData, " Extra field data");
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder,
|
||||
LocalFileHeader[]? localFileHeaders,
|
||||
byte[]?[]? encryptionHeaders,
|
||||
byte[][]? fileData,
|
||||
DataDescriptor?[]? dataDescriptors,
|
||||
DataDescriptor64?[]? zip64DataDescriptors)
|
||||
{
|
||||
builder.AppendLine(" Local File Information:");
|
||||
builder.AppendLine(" -------------------------");
|
||||
if (localFileHeaders == null || localFileHeaders.Length == 0)
|
||||
{
|
||||
builder.AppendLine(" No local files");
|
||||
builder.AppendLine();
|
||||
return;
|
||||
}
|
||||
|
||||
if (encryptionHeaders == null || localFileHeaders.Length > encryptionHeaders.Length
|
||||
|| fileData == null || localFileHeaders.Length > fileData.Length
|
||||
|| dataDescriptors == null || localFileHeaders.Length > dataDescriptors.Length
|
||||
|| zip64DataDescriptors == null || localFileHeaders.Length > zip64DataDescriptors.Length)
|
||||
{
|
||||
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);
|
||||
}
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
private static void Print(StringBuilder builder,
|
||||
LocalFileHeader localFileHeader,
|
||||
byte[]? encryptionHeader,
|
||||
byte[]? fileData,
|
||||
DataDescriptor? dataDescriptor,
|
||||
DataDescriptor64? zip64DataDescriptor,
|
||||
int index)
|
||||
{
|
||||
builder.AppendLine($" Local File Entry {index}");
|
||||
if (localFileHeader == null)
|
||||
{
|
||||
builder.AppendLine(" [NULL]");
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
builder.AppendLine(localFileHeader.ExtraField, " [Local File Header] Extra field");
|
||||
|
||||
if (encryptionHeader == null)
|
||||
{
|
||||
builder.AppendLine(" [Encryption Header]: [NULL]");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(encryptionHeader.Length, " [Encryption Header] Length");
|
||||
builder.AppendLine(encryptionHeader, " [Encryption Header] Data");
|
||||
}
|
||||
|
||||
if (fileData == null)
|
||||
{
|
||||
builder.AppendLine(" [File Data]: [NULL]");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(fileData.Length, " [File Data] Length");
|
||||
//builder.AppendLine(fileData, " [File Data] Data");
|
||||
}
|
||||
|
||||
if (dataDescriptor == null)
|
||||
{
|
||||
builder.AppendLine(" [Data Descriptor]: [NULL]");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(dataDescriptor.Signature, " [Data Descriptor] Signature");
|
||||
builder.AppendLine(dataDescriptor.CRC32, " [Data Descriptor] CRC-32");
|
||||
builder.AppendLine(dataDescriptor.CompressedSize, " [Data Descriptor] Compressed size");
|
||||
builder.AppendLine(dataDescriptor.UncompressedSize, " [Data Descriptor] Uncompressed size");
|
||||
}
|
||||
|
||||
if (zip64DataDescriptor == null)
|
||||
{
|
||||
builder.AppendLine(" [ZIP64 Data Descriptor]: [NULL]");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine(zip64DataDescriptor.Signature, " [ZIP64 Data Descriptor] Signature");
|
||||
builder.AppendLine(zip64DataDescriptor.CRC32, " [ZIP64 Data Descriptor] CRC-32");
|
||||
builder.AppendLine(zip64DataDescriptor.CompressedSize, " [ZIP64 Data Descriptor] Compressed size");
|
||||
builder.AppendLine(zip64DataDescriptor.UncompressedSize, " [ZIP64 Data Descriptor] Uncompressed size");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -964,7 +964,7 @@ namespace SabreTools.Serialization.Printers
|
||||
|
||||
var newTypes = new List<object>(types ?? []);
|
||||
if (entry.Name?.UnicodeString != null)
|
||||
newTypes.Add(Encoding.UTF8.GetString(entry.Name.UnicodeString));
|
||||
newTypes.Add(Encoding.Unicode.GetString(entry.Name.UnicodeString));
|
||||
else
|
||||
newTypes.Add(entry.IntegerID);
|
||||
|
||||
@@ -1120,79 +1120,67 @@ namespace SabreTools.Serialization.Printers
|
||||
|
||||
if (menu.MenuHeader != null)
|
||||
{
|
||||
builder.AppendLine(menu.MenuHeader.Version, $"{padding}Version");
|
||||
builder.AppendLine(menu.MenuHeader.HeaderSize, $"{padding}Header size");
|
||||
if (menu.MenuHeader is NormalMenuHeader normalMenuHeader)
|
||||
{
|
||||
builder.AppendLine(normalMenuHeader.Version, $"{padding}Version");
|
||||
builder.AppendLine(normalMenuHeader.HeaderSize, $"{padding}Header size");
|
||||
}
|
||||
else if (menu.MenuHeader is MenuHeaderExtended menuHeaderExtended)
|
||||
{
|
||||
builder.AppendLine(menuHeaderExtended.Version, $"{padding}Version");
|
||||
builder.AppendLine(menuHeaderExtended.Offset, $"{padding}Offset");
|
||||
builder.AppendLine(menuHeaderExtended.HelpID, $"{padding}Help ID");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"{padding}Menu header found, but malformed");
|
||||
}
|
||||
builder.AppendLine();
|
||||
builder.AppendLine($"{padding}Menu items");
|
||||
builder.AppendLine($"{padding}-------------------------");
|
||||
if (menu.MenuItems == null || menu.MenuItems.Length == 0)
|
||||
{
|
||||
builder.AppendLine($"{padding}No menu items");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < menu.MenuItems.Length; i++)
|
||||
{
|
||||
var menuItem = menu.MenuItems[i];
|
||||
builder.AppendLine($"{padding}Menu item {i}");
|
||||
if (menuItem == null)
|
||||
{
|
||||
builder.AppendLine($"{padding} [NULL]");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (menuItem is NormalMenuItem normalMenuItem)
|
||||
{
|
||||
builder.AppendLine($"{padding} Resource info: {normalMenuItem.NormalResInfo} (0x{normalMenuItem.NormalResInfo:X})");
|
||||
builder.AppendLine(normalMenuItem.NormalMenuText, $"{padding} Menu text");
|
||||
}
|
||||
else if (menuItem is PopupMenuItem popupMenuItem)
|
||||
{
|
||||
builder.AppendLine($"{padding} Item type: {popupMenuItem.PopupItemType} (0x{popupMenuItem.PopupItemType:X})");
|
||||
builder.AppendLine($"{padding} State: {popupMenuItem.PopupState} (0x{popupMenuItem.PopupState:X})");
|
||||
builder.AppendLine(popupMenuItem.PopupID, $"{padding} ID");
|
||||
builder.AppendLine($"{padding} Resource info: {popupMenuItem.PopupResInfo} (0x{popupMenuItem.PopupResInfo:X})");
|
||||
builder.AppendLine(popupMenuItem.PopupMenuText, $"{padding} Menu text");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (menu.ExtendedMenuHeader != null)
|
||||
|
||||
builder.AppendLine($"{padding}Menu items");
|
||||
builder.AppendLine($"{padding}-------------------------");
|
||||
if (menu.MenuItems == null || menu.MenuItems.Length == 0)
|
||||
{
|
||||
builder.AppendLine(menu.ExtendedMenuHeader.Version, $"{padding}Version");
|
||||
builder.AppendLine(menu.ExtendedMenuHeader.Offset, $"{padding}Offset");
|
||||
builder.AppendLine(menu.ExtendedMenuHeader.HelpID, $"{padding}Help ID");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine($"{padding}Menu items");
|
||||
builder.AppendLine($"{padding}-------------------------");
|
||||
if (menu.ExtendedMenuHeader.Offset == 0
|
||||
|| menu.ExtendedMenuItems == null
|
||||
|| menu.ExtendedMenuItems.Length == 0)
|
||||
{
|
||||
builder.AppendLine($"{padding}No menu items");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < menu.ExtendedMenuItems.Length; i++)
|
||||
{
|
||||
var menuItem = menu.ExtendedMenuItems[i];
|
||||
|
||||
builder.AppendLine($"{padding}Menu item {i}");
|
||||
if (menuItem == null)
|
||||
{
|
||||
builder.AppendLine($"{padding} [NULL]");
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.AppendLine($"{padding} Item type: {menuItem.ItemType} (0x{menuItem.ItemType:X})");
|
||||
builder.AppendLine($"{padding} State: {menuItem.State} (0x{menuItem.State:X})");
|
||||
builder.AppendLine(menuItem.ID, $"{padding} ID");
|
||||
builder.AppendLine($"{padding} Flags: {menuItem.Flags} (0x{menuItem.Flags:X})");
|
||||
builder.AppendLine(menuItem.MenuText, $"{padding} Menu text");
|
||||
}
|
||||
builder.AppendLine($"{padding}No menu items");
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
for (int i = 0; i < menu.MenuItems.Length; i++)
|
||||
{
|
||||
builder.AppendLine($"{padding}Menu resource found, but malformed");
|
||||
var menuItem = menu.MenuItems[i];
|
||||
builder.AppendLine($"{padding}Menu item {i}");
|
||||
if (menuItem == null)
|
||||
{
|
||||
builder.AppendLine($"{padding} [NULL]");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (menuItem is NormalMenuItem normalMenuItem)
|
||||
{
|
||||
builder.AppendLine($"{padding} Resource info: {normalMenuItem.NormalResInfo} (0x{normalMenuItem.NormalResInfo:X})");
|
||||
builder.AppendLine(normalMenuItem.NormalMenuText, $"{padding} Menu text");
|
||||
}
|
||||
else if (menuItem is PopupMenuItem popupMenuItem)
|
||||
{
|
||||
builder.AppendLine($"{padding} Item type: {popupMenuItem.PopupItemType} (0x{popupMenuItem.PopupItemType:X})");
|
||||
builder.AppendLine($"{padding} State: {popupMenuItem.PopupState} (0x{popupMenuItem.PopupState:X})");
|
||||
builder.AppendLine(popupMenuItem.PopupID, $"{padding} ID");
|
||||
builder.AppendLine($"{padding} Resource info: {popupMenuItem.PopupResInfo} (0x{popupMenuItem.PopupResInfo:X})");
|
||||
builder.AppendLine(popupMenuItem.PopupMenuText, $"{padding} Menu text");
|
||||
}
|
||||
else if (menuItem is MenuItemExtended menuItemExtended)
|
||||
{
|
||||
builder.AppendLine($"{padding} Item type: {menuItemExtended.ItemType} (0x{menuItemExtended.ItemType:X})");
|
||||
builder.AppendLine($"{padding} State: {menuItemExtended.State} (0x{menuItemExtended.State:X})");
|
||||
builder.AppendLine(menuItemExtended.ID, $"{padding} ID");
|
||||
builder.AppendLine($"{padding} Flags: {menuItemExtended.Flags} (0x{menuItemExtended.Flags:X})");
|
||||
builder.AppendLine(menuItemExtended.MenuText, $"{padding} Menu text");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AppendLine($"{padding} Menu item found, but malformed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<Version>1.6.0</Version>
|
||||
<Version>1.6.5</Version>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski</Authors>
|
||||
@@ -30,8 +30,8 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.ASN1" Version="1.3.2" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.2.0" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.4.5" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.4.5" />
|
||||
<PackageReference Include="SabreTools.IO" Version="1.4.11" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.4.8" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace SabreTools.Serialization
|
||||
public static StringBuilder AppendLine(this StringBuilder sb, sbyte? value, string prefixString)
|
||||
{
|
||||
value ??= 0;
|
||||
string valueString = $"{value} (0x{value:X})";
|
||||
string valueString = $"{value} (0x{value:X2})";
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace SabreTools.Serialization
|
||||
public static StringBuilder AppendLine(this StringBuilder sb, byte? value, string prefixString)
|
||||
{
|
||||
value ??= 0;
|
||||
string valueString = $"{value} (0x{value:X})";
|
||||
string valueString = $"{value} (0x{value:X2})";
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace SabreTools.Serialization
|
||||
public static StringBuilder AppendLine(this StringBuilder sb, short? value, string prefixString)
|
||||
{
|
||||
value ??= 0;
|
||||
string valueString = $"{value} (0x{value:X})";
|
||||
string valueString = $"{value} (0x{value:X4})";
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace SabreTools.Serialization
|
||||
public static StringBuilder AppendLine(this StringBuilder sb, ushort? value, string prefixString)
|
||||
{
|
||||
value ??= 0;
|
||||
string valueString = $"{value} (0x{value:X})";
|
||||
string valueString = $"{value} (0x{value:X4})";
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace SabreTools.Serialization
|
||||
public static StringBuilder AppendLine(this StringBuilder sb, int? value, string prefixString)
|
||||
{
|
||||
value ??= 0;
|
||||
string valueString = $"{value} (0x{value:X})";
|
||||
string valueString = $"{value} (0x{value:X8})";
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace SabreTools.Serialization
|
||||
public static StringBuilder AppendLine(this StringBuilder sb, uint? value, string prefixString)
|
||||
{
|
||||
value ??= 0;
|
||||
string valueString = $"{value} (0x{value:X})";
|
||||
string valueString = $"{value} (0x{value:X8})";
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace SabreTools.Serialization
|
||||
public static StringBuilder AppendLine(this StringBuilder sb, long? value, string prefixString)
|
||||
{
|
||||
value ??= 0;
|
||||
string valueString = $"{value} (0x{value:X})";
|
||||
string valueString = $"{value} (0x{value:X16})";
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace SabreTools.Serialization
|
||||
public static StringBuilder AppendLine(this StringBuilder sb, ulong? value, string prefixString)
|
||||
{
|
||||
value ??= 0;
|
||||
string valueString = $"{value} (0x{value:X})";
|
||||
string valueString = $"{value} (0x{value:X16})";
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
@@ -110,7 +110,6 @@ namespace SabreTools.Serialization
|
||||
/// </summary>
|
||||
public static StringBuilder AppendLine(this StringBuilder sb, string? value, string prefixString)
|
||||
{
|
||||
value ??= string.Empty;
|
||||
string valueString = value ?? "[NULL]";
|
||||
return sb.AppendLine($"{prefixString}: {valueString}");
|
||||
}
|
||||
|
||||
79
SabreTools.Serialization/Wrappers/PKZIP.cs
Normal file
79
SabreTools.Serialization/Wrappers/PKZIP.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System.IO;
|
||||
using SabreTools.Models.PKZIP;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
public class PKZIP : WrapperBase<Archive>
|
||||
{
|
||||
#region Descriptive Properties
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string DescriptionString => "PKZIP Archive (or Derived Format)";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <inheritdoc/>
|
||||
public PKZIP(Archive? model, byte[]? data, int offset)
|
||||
: base(model, data, offset)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public PKZIP(Archive? model, Stream? data)
|
||||
: base(model, data)
|
||||
{
|
||||
// All logic is handled by the base class
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a PKZIP archive (or derived format) from a byte array and offset
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array representing the archive</param>
|
||||
/// <param name="offset">Offset within the array to parse</param>
|
||||
/// <returns>A PKZIP wrapper on success, null on failure</returns>
|
||||
public static PKZIP? Create(byte[]? data, int offset)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null)
|
||||
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 PKZIP archive (or derived format) from a Stream
|
||||
/// </summary>
|
||||
/// <param name="data">Stream representing the archive</param>
|
||||
/// <returns>A PKZIP wrapper on success, null on failure</returns>
|
||||
public static PKZIP? Create(Stream? data)
|
||||
{
|
||||
// If the data is invalid
|
||||
if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
|
||||
return null;
|
||||
|
||||
var archive = Deserializers.PKZIP.DeserializeStream(data);
|
||||
if (archive == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return new PKZIP(archive, data);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1148,7 +1148,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
var newTypes = new List<object>(types ?? []);
|
||||
|
||||
if (entry.Name?.UnicodeString != null)
|
||||
newTypes.Add(Encoding.UTF8.GetString(entry.Name.UnicodeString));
|
||||
newTypes.Add(Encoding.Unicode.GetString(entry.Name.UnicodeString));
|
||||
else
|
||||
newTypes.Add(entry.IntegerID);
|
||||
|
||||
@@ -1188,68 +1188,68 @@ namespace SabreTools.Serialization.Wrappers
|
||||
{
|
||||
switch ((Models.PortableExecutable.ResourceType)resourceType)
|
||||
{
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_CURSOR:
|
||||
case Models.PortableExecutable.ResourceType.RT_CURSOR:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_BITMAP:
|
||||
case Models.PortableExecutable.ResourceType.RT_BITMAP:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_ICON:
|
||||
case Models.PortableExecutable.ResourceType.RT_ICON:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_MENU:
|
||||
case Models.PortableExecutable.ResourceType.RT_MENU:
|
||||
value = entry.AsMenu();
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_DIALOG:
|
||||
case Models.PortableExecutable.ResourceType.RT_DIALOG:
|
||||
value = entry.AsDialogBox();
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_STRING:
|
||||
case Models.PortableExecutable.ResourceType.RT_STRING:
|
||||
value = entry.AsStringTable();
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_FONTDIR:
|
||||
case Models.PortableExecutable.ResourceType.RT_FONTDIR:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_FONT:
|
||||
case Models.PortableExecutable.ResourceType.RT_FONT:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_ACCELERATOR:
|
||||
case Models.PortableExecutable.ResourceType.RT_ACCELERATOR:
|
||||
value = entry.AsAcceleratorTableResource();
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_RCDATA:
|
||||
case Models.PortableExecutable.ResourceType.RT_RCDATA:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_MESSAGETABLE:
|
||||
case Models.PortableExecutable.ResourceType.RT_MESSAGETABLE:
|
||||
value = entry.AsMessageResourceData();
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_GROUP_CURSOR:
|
||||
case Models.PortableExecutable.ResourceType.RT_GROUP_CURSOR:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_GROUP_ICON:
|
||||
case Models.PortableExecutable.ResourceType.RT_GROUP_ICON:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_VERSION:
|
||||
case Models.PortableExecutable.ResourceType.RT_VERSION:
|
||||
_versionInfo = entry.AsVersionInfo();
|
||||
value = _versionInfo;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_DLGINCLUDE:
|
||||
case Models.PortableExecutable.ResourceType.RT_DLGINCLUDE:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_PLUGPLAY:
|
||||
case Models.PortableExecutable.ResourceType.RT_PLUGPLAY:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_VXD:
|
||||
case Models.PortableExecutable.ResourceType.RT_VXD:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_ANICURSOR:
|
||||
case Models.PortableExecutable.ResourceType.RT_ANICURSOR:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_ANIICON:
|
||||
case Models.PortableExecutable.ResourceType.RT_ANIICON:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_HTML:
|
||||
case Models.PortableExecutable.ResourceType.RT_HTML:
|
||||
value = entry.Data;
|
||||
break;
|
||||
case SabreTools.Models.PortableExecutable.ResourceType.RT_MANIFEST:
|
||||
case Models.PortableExecutable.ResourceType.RT_MANIFEST:
|
||||
_assemblyManifest = entry.AsAssemblyManifest();
|
||||
value = _versionInfo;
|
||||
break;
|
||||
@@ -1335,7 +1335,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <param name="name">Name of the section to check for</param>
|
||||
/// <param name="exact">True to enable exact matching of names, false for starts-with</param>
|
||||
/// <returns>Section data on success, null on error</returns>
|
||||
public SabreTools.Models.PortableExecutable.SectionHeader? GetFirstSection(string? name, bool exact = false)
|
||||
public Models.PortableExecutable.SectionHeader? GetFirstSection(string? name, bool exact = false)
|
||||
{
|
||||
// If we have no sections
|
||||
if (SectionNames == null || !SectionNames.Any() || this.Model.SectionTable == null || !this.Model.SectionTable.Any())
|
||||
@@ -1360,7 +1360,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <param name="name">Name of the section to check for</param>
|
||||
/// <param name="exact">True to enable exact matching of names, false for starts-with</param>
|
||||
/// <returns>Section data on success, null on error</returns>
|
||||
public SabreTools.Models.PortableExecutable.SectionHeader? GetLastSection(string? name, bool exact = false)
|
||||
public Models.PortableExecutable.SectionHeader? GetLastSection(string? name, bool exact = false)
|
||||
{
|
||||
// If we have no sections
|
||||
if (SectionNames == null || !SectionNames.Any() || this.Model.SectionTable == null || !this.Model.SectionTable.Any())
|
||||
@@ -1384,7 +1384,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the section to check for</param>
|
||||
/// <returns>Section data on success, null on error</returns>
|
||||
public SabreTools.Models.PortableExecutable.SectionHeader? GetSection(int index)
|
||||
public Models.PortableExecutable.SectionHeader? GetSection(int index)
|
||||
{
|
||||
// If we have no sections
|
||||
if (this.Model.SectionTable == null || !this.Model.SectionTable.Any())
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace SabreTools.Serialization.Wrappers
|
||||
case WrapperType.PAK: return PAK.Create(data);
|
||||
case WrapperType.PFF: return PFF.Create(data);
|
||||
case WrapperType.PIC: return PIC.Create(data);
|
||||
case WrapperType.PKZIP: return null; // TODO: Implement wrapper
|
||||
case WrapperType.PKZIP: return PKZIP.Create(data);
|
||||
case WrapperType.PlayJAudioFile: return PlayJAudioFile.Create(data);
|
||||
case WrapperType.PlayJPlaylist: return PlayJPlaylist.Create(data);
|
||||
case WrapperType.Quantum: return Quantum.Create(data);
|
||||
@@ -116,15 +116,18 @@ namespace SabreTools.Serialization.Wrappers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the supported file type for a magic string
|
||||
/// Get the supported file type for a magic string and an extension
|
||||
/// </summary>
|
||||
/// <remarks>Recommend sending in 16 bytes to check</remarks>
|
||||
public static WrapperType GetFileType(byte[] magic)
|
||||
public static WrapperType GetFileType(byte[]? magic, string? extension)
|
||||
{
|
||||
// If we have an invalid magic byte array
|
||||
if (magic == null || magic.Length == 0)
|
||||
// If we have an invalid magic byte array and extension
|
||||
if (magic == null || magic.Length == 0 || extension == null)
|
||||
return WrapperType.UNKNOWN;
|
||||
|
||||
// Normalize the extension
|
||||
extension = extension.TrimStart('.').Trim();
|
||||
|
||||
// TODO: For all modelled types, use the constants instead of hardcoded values here
|
||||
#region AACSMediaKeyBlock
|
||||
|
||||
@@ -136,6 +139,15 @@ namespace SabreTools.Serialization.Wrappers
|
||||
if (magic.StartsWith(new byte?[] { 0x10, 0x00, 0x00, 0x0C }))
|
||||
return WrapperType.AACSMediaKeyBlock;
|
||||
|
||||
// Shares an extension with INF setup information so it can't be used accurately
|
||||
// Blu-ray
|
||||
// if (extension.Equals("inf", StringComparison.OrdinalIgnoreCase))
|
||||
// return WrapperType.AACSMediaKeyBlock;
|
||||
|
||||
// HD-DVD
|
||||
if (extension.Equals("aacs", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.AACSMediaKeyBlock;
|
||||
|
||||
#endregion
|
||||
|
||||
#region BDPlusSVM
|
||||
@@ -143,6 +155,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
if (magic.StartsWith(new byte?[] { 0x42, 0x44, 0x53, 0x56, 0x4D, 0x5F, 0x43, 0x43 }))
|
||||
return WrapperType.BDPlusSVM;
|
||||
|
||||
if (extension.Equals("svm", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.BDPlusSVM;
|
||||
|
||||
#endregion
|
||||
|
||||
#region BFPK
|
||||
@@ -154,7 +169,14 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region BSP
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x1e, 0x00, 0x00, 0x00 }))
|
||||
// Shares a first 4 bytes with some .mc files
|
||||
// Shares an extension with VBSP
|
||||
if (magic.StartsWith(new byte?[] { 0x1d, 0x00, 0x00, 0x00 }) && extension.Equals("bsp", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.BSP;
|
||||
|
||||
// Shares a first 4 bytes with some .mc files
|
||||
// Shares an extension with VBSP
|
||||
if (magic.StartsWith(new byte?[] { 0x1e, 0x00, 0x00, 0x00 }) && extension.Equals("bsp", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.BSP;
|
||||
|
||||
#endregion
|
||||
@@ -164,6 +186,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
if (magic.StartsWith(new byte?[] { 0x42, 0x52, 0x68 }))
|
||||
return WrapperType.BZip2;
|
||||
|
||||
if (extension.Equals("bz2", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.BZip2;
|
||||
|
||||
#endregion
|
||||
|
||||
#region CFB
|
||||
@@ -171,11 +196,32 @@ namespace SabreTools.Serialization.Wrappers
|
||||
if (magic.StartsWith(new byte?[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }))
|
||||
return WrapperType.CFB;
|
||||
|
||||
// Installer package
|
||||
if (extension.Equals("msi", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CFB;
|
||||
|
||||
// Merge module
|
||||
else if (extension.Equals("msm", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CFB;
|
||||
|
||||
// Patch Package
|
||||
else if (extension.Equals("msp", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CFB;
|
||||
|
||||
// Transform
|
||||
else if (extension.Equals("mst", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CFB;
|
||||
|
||||
// Patch Creation Properties
|
||||
else if (extension.Equals("pcp", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CFB;
|
||||
|
||||
#endregion
|
||||
|
||||
#region CIA
|
||||
|
||||
// No magic checks for CIA
|
||||
if (extension.Equals("cia", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CIA;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -213,369 +259,6 @@ namespace SabreTools.Serialization.Wrappers
|
||||
return FileTypes.Executable;
|
||||
*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region GCF
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }))
|
||||
return WrapperType.GCF;
|
||||
|
||||
#endregion
|
||||
|
||||
#region GZIP
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x1f, 0x8b }))
|
||||
return WrapperType.GZIP;
|
||||
|
||||
#endregion
|
||||
|
||||
#region IniFile
|
||||
|
||||
// No magic checks for IniFile
|
||||
|
||||
#endregion
|
||||
|
||||
#region InstallShieldArchiveV3
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x13, 0x5D, 0x65, 0x8C }))
|
||||
return WrapperType.InstallShieldArchiveV3;
|
||||
|
||||
#endregion
|
||||
|
||||
#region InstallShieldCAB
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x49, 0x53, 0x63 }))
|
||||
return WrapperType.InstallShieldCAB;
|
||||
|
||||
#endregion
|
||||
|
||||
#region LDSCRYPT
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x4C, 0x44, 0x53, 0x43, 0x52, 0x59, 0x50, 0x54 }))
|
||||
return WrapperType.LDSCRYPT;
|
||||
|
||||
#endregion
|
||||
|
||||
#region MicrosoftCAB
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x4d, 0x53, 0x43, 0x46 }))
|
||||
return WrapperType.MicrosoftCAB;
|
||||
|
||||
#endregion
|
||||
|
||||
#region MicrosoftLZ
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x53, 0x5a, 0x44, 0x44, 0x88, 0xf0, 0x27, 0x33 }))
|
||||
return WrapperType.MicrosoftLZ;
|
||||
|
||||
#endregion
|
||||
|
||||
#region MPQ
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x4d, 0x50, 0x51, 0x1a }))
|
||||
return WrapperType.MoPaQ;
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x4d, 0x50, 0x51, 0x1b }))
|
||||
return WrapperType.MoPaQ;
|
||||
|
||||
#endregion
|
||||
|
||||
#region N3DS
|
||||
|
||||
// No magic checks for N3DS
|
||||
|
||||
#endregion
|
||||
|
||||
#region NCF
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }))
|
||||
return WrapperType.NCF;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Nitro
|
||||
|
||||
// No magic checks for Nitro
|
||||
|
||||
#endregion
|
||||
|
||||
#region PAK
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x41, 0x43, 0x4B }))
|
||||
return WrapperType.PAK;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PFF
|
||||
|
||||
// Version 2
|
||||
if (magic.StartsWith(new byte?[] { 0x14, 0x00, 0x00, 0x00, 0x50, 0x46, 0x46, 0x32 }))
|
||||
return WrapperType.PFF;
|
||||
|
||||
// Version 3
|
||||
if (magic.StartsWith(new byte?[] { 0x14, 0x00, 0x00, 0x00, 0x50, 0x46, 0x46, 0x33 }))
|
||||
return WrapperType.PFF;
|
||||
|
||||
// Version 4
|
||||
if (magic.StartsWith(new byte?[] { 0x14, 0x00, 0x00, 0x00, 0x50, 0x46, 0x46, 0x34 }))
|
||||
return WrapperType.PFF;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PKZIP
|
||||
|
||||
// PKZIP (Unknown)
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x00, 0x00 }))
|
||||
return WrapperType.PKZIP;
|
||||
|
||||
// PKZIP
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x03, 0x04 }))
|
||||
return WrapperType.PKZIP;
|
||||
|
||||
// PKZIP (Empty Archive)
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x05, 0x06 }))
|
||||
return WrapperType.PKZIP;
|
||||
|
||||
// PKZIP (Spanned Archive)
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x07, 0x08 }))
|
||||
return WrapperType.PKZIP;
|
||||
|
||||
#endregion
|
||||
|
||||
#region PLJ
|
||||
|
||||
// https://www.iana.org/assignments/media-types/audio/vnd.everad.plj
|
||||
if (magic.StartsWith(new byte?[] { 0xFF, 0x9D, 0x53, 0x4B }))
|
||||
return WrapperType.PlayJAudioFile;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Quantum
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x44, 0x53 }))
|
||||
return WrapperType.Quantum;
|
||||
|
||||
#endregion
|
||||
|
||||
#region RAR
|
||||
|
||||
// RAR archive version 1.50 onwards
|
||||
if (magic.StartsWith(new byte?[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }))
|
||||
return WrapperType.RAR;
|
||||
|
||||
// RAR archive version 5.0 onwards
|
||||
if (magic.StartsWith(new byte?[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 }))
|
||||
return WrapperType.RAR;
|
||||
|
||||
#endregion
|
||||
|
||||
#region RealArcade
|
||||
|
||||
// RASGI2.0
|
||||
// Found in the ".rgs files in IA item "Nova_RealArcadeCD_USA".
|
||||
if (magic.StartsWith(new byte?[] { 0x52, 0x41, 0x53, 0x47, 0x49, 0x32, 0x2E, 0x30 }))
|
||||
return WrapperType.RealArcadeInstaller;
|
||||
|
||||
// XZip2.0
|
||||
// Found in the ".mez" files in IA item "Nova_RealArcadeCD_USA".
|
||||
if (magic.StartsWith(new byte?[] { 0x58, 0x5A, 0x69, 0x70, 0x32, 0x2E, 0x30 }))
|
||||
return WrapperType.RealArcadeMezzanine;
|
||||
|
||||
#endregion
|
||||
|
||||
#region SevenZip
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c }))
|
||||
return WrapperType.SevenZip;
|
||||
|
||||
#endregion
|
||||
|
||||
#region SFFS
|
||||
|
||||
// Found in Redump entry 81756, confirmed to be "StarForce Filesystem" by PiD.
|
||||
if (magic.StartsWith(new byte?[] { 0x53, 0x46, 0x46, 0x53 }))
|
||||
return WrapperType.SFFS;
|
||||
|
||||
#endregion
|
||||
|
||||
#region SGA
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x5F, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56, 0x45 }))
|
||||
return WrapperType.SGA;
|
||||
|
||||
#endregion
|
||||
|
||||
#region TapeArchive
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 }))
|
||||
return WrapperType.TapeArchive;
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 }))
|
||||
return WrapperType.TapeArchive;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Textfile
|
||||
|
||||
// Not all textfiles can be determined through magic number
|
||||
|
||||
// HTML
|
||||
if (magic.StartsWith(new byte?[] { 0x3c, 0x68, 0x74, 0x6d, 0x6c }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// HTML and XML
|
||||
if (magic.StartsWith(new byte?[] { 0x3c, 0x21, 0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45 }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// InstallShield Compiled Rules
|
||||
if (magic.StartsWith(new byte?[] { 0x61, 0x4C, 0x75, 0x5A }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// Microsoft Office File (old)
|
||||
if (magic.StartsWith(new byte?[] { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// Rich Text File
|
||||
if (magic.StartsWith(new byte?[] { 0x7b, 0x5c, 0x72, 0x74, 0x66, 0x31 }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// Windows Help File
|
||||
if (magic.StartsWith(new byte?[] { 0x3F, 0x5F, 0x03, 0x00 }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// XML
|
||||
// "<?xml"
|
||||
if (magic.StartsWith(new byte?[] { 0x3C, 0x3F, 0x78, 0x6D, 0x6C }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
#endregion
|
||||
|
||||
#region VBSP
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x56, 0x42, 0x53, 0x50 }))
|
||||
return WrapperType.VBSP;
|
||||
|
||||
#endregion
|
||||
|
||||
#region VPK
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x34, 0x12, 0xaa, 0x55 }))
|
||||
return WrapperType.VPK;
|
||||
|
||||
#endregion
|
||||
|
||||
#region WAD
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x57, 0x41, 0x44, 0x33 }))
|
||||
return WrapperType.WAD;
|
||||
|
||||
#endregion
|
||||
|
||||
#region XZ
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00 }))
|
||||
return WrapperType.XZ;
|
||||
|
||||
#endregion
|
||||
|
||||
#region XZP
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x70, 0x69, 0x5A, 0x78 }))
|
||||
return WrapperType.XZP;
|
||||
|
||||
#endregion
|
||||
|
||||
// We couldn't find a supported match
|
||||
return WrapperType.UNKNOWN;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the supported file type for an extension
|
||||
/// </summary>
|
||||
/// <remarks>This is less accurate than a magic string match</remarks>
|
||||
public static WrapperType GetFileType(string extension)
|
||||
{
|
||||
// If we have an invalid extension
|
||||
if (string.IsNullOrEmpty(extension))
|
||||
return WrapperType.UNKNOWN;
|
||||
|
||||
// Normalize the extension
|
||||
extension = extension.TrimStart('.').Trim();
|
||||
|
||||
#region AACSMediaKeyBlock
|
||||
|
||||
// Shares an extension with INF setup information so it can't be used accurately
|
||||
// Blu-ray
|
||||
// if (extension.Equals("inf", StringComparison.OrdinalIgnoreCase))
|
||||
// return WrapperType.AACSMediaKeyBlock;
|
||||
|
||||
// HD-DVD
|
||||
if (extension.Equals("aacs", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.AACSMediaKeyBlock;
|
||||
|
||||
#endregion
|
||||
|
||||
#region BDPlusSVM
|
||||
|
||||
if (extension.Equals("svm", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.BDPlusSVM;
|
||||
|
||||
#endregion
|
||||
|
||||
#region BFPK
|
||||
|
||||
// No extensions registered for BFPK
|
||||
|
||||
#endregion
|
||||
|
||||
#region BSP
|
||||
|
||||
// Shares an extension with VBSP so it can't be used accurately
|
||||
// if (extension.Equals("bsp", StringComparison.OrdinalIgnoreCase))
|
||||
// return WrapperType.BSP;
|
||||
|
||||
#endregion
|
||||
|
||||
#region BZip2
|
||||
|
||||
if (extension.Equals("bz2", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.BZip2;
|
||||
|
||||
#endregion
|
||||
|
||||
#region CFB
|
||||
|
||||
// Installer package
|
||||
if (extension.Equals("msi", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CFB;
|
||||
|
||||
// Merge module
|
||||
else if (extension.Equals("msm", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CFB;
|
||||
|
||||
// Patch Package
|
||||
else if (extension.Equals("msp", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CFB;
|
||||
|
||||
// Transform
|
||||
else if (extension.Equals("mst", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CFB;
|
||||
|
||||
// Patch Creation Properties
|
||||
else if (extension.Equals("pcp", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CFB;
|
||||
|
||||
#endregion
|
||||
|
||||
#region CIA
|
||||
|
||||
if (extension.Equals("cia", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.CIA;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Executable
|
||||
|
||||
// DOS MZ executable file format (and descendants)
|
||||
if (extension.Equals("exe", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.Executable;
|
||||
@@ -588,6 +271,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region GCF
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }))
|
||||
return WrapperType.GCF;
|
||||
|
||||
if (extension.Equals("gcf", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.GCF;
|
||||
|
||||
@@ -595,6 +281,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region GZIP
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x1f, 0x8b }))
|
||||
return WrapperType.GZIP;
|
||||
|
||||
if (extension.Equals("gz", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.GZIP;
|
||||
|
||||
@@ -609,6 +298,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region InstallShieldArchiveV3
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x13, 0x5D, 0x65, 0x8C }))
|
||||
return WrapperType.InstallShieldArchiveV3;
|
||||
|
||||
if (extension.Equals("z", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.InstallShieldArchiveV3;
|
||||
|
||||
@@ -616,19 +308,43 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region InstallShieldCAB
|
||||
|
||||
// No extensions registered for InstallShieldCAB
|
||||
if (magic.StartsWith(new byte?[] { 0x49, 0x53, 0x63 }))
|
||||
return WrapperType.InstallShieldCAB;
|
||||
|
||||
// Both InstallShieldCAB and MicrosoftCAB share the same extension
|
||||
|
||||
#endregion
|
||||
|
||||
#region LDSCRYPT
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x4C, 0x44, 0x53, 0x43, 0x52, 0x59, 0x50, 0x54 }))
|
||||
return WrapperType.LDSCRYPT;
|
||||
|
||||
#endregion
|
||||
|
||||
#region MicrosoftCAB
|
||||
|
||||
// No extensions registered for InstallShieldCAB
|
||||
if (magic.StartsWith(new byte?[] { 0x4d, 0x53, 0x43, 0x46 }))
|
||||
return WrapperType.MicrosoftCAB;
|
||||
|
||||
// Both InstallShieldCAB and MicrosoftCAB share the same extension
|
||||
|
||||
#endregion
|
||||
|
||||
#region MPQ
|
||||
#region MicrosoftLZ
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x53, 0x5a, 0x44, 0x44, 0x88, 0xf0, 0x27, 0x33 }))
|
||||
return WrapperType.MicrosoftLZ;
|
||||
|
||||
#endregion
|
||||
|
||||
#region MoPaQ
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x4d, 0x50, 0x51, 0x1a }))
|
||||
return WrapperType.MoPaQ;
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x4d, 0x50, 0x51, 0x1b }))
|
||||
return WrapperType.MoPaQ;
|
||||
|
||||
if (extension.Equals("mpq", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.MoPaQ;
|
||||
@@ -649,6 +365,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region NCF
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00 }))
|
||||
return WrapperType.NCF;
|
||||
|
||||
if (extension.Equals("ncf", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.NCF;
|
||||
|
||||
@@ -676,7 +395,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region PAK
|
||||
|
||||
// No extensions registered for PAK
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x41, 0x43, 0x4B }))
|
||||
return WrapperType.PAK;
|
||||
|
||||
// Both PAK and Quantum share one extension
|
||||
// if (extension.Equals("pak", StringComparison.OrdinalIgnoreCase))
|
||||
// return WrapperType.PAK;
|
||||
@@ -685,6 +406,18 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region PFF
|
||||
|
||||
// Version 2
|
||||
if (magic.StartsWith(new byte?[] { 0x14, 0x00, 0x00, 0x00, 0x50, 0x46, 0x46, 0x32 }))
|
||||
return WrapperType.PFF;
|
||||
|
||||
// Version 3
|
||||
if (magic.StartsWith(new byte?[] { 0x14, 0x00, 0x00, 0x00, 0x50, 0x46, 0x46, 0x33 }))
|
||||
return WrapperType.PFF;
|
||||
|
||||
// Version 4
|
||||
if (magic.StartsWith(new byte?[] { 0x14, 0x00, 0x00, 0x00, 0x50, 0x46, 0x46, 0x34 }))
|
||||
return WrapperType.PFF;
|
||||
|
||||
if (extension.Equals("pff", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.PFF;
|
||||
|
||||
@@ -692,6 +425,22 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region PKZIP
|
||||
|
||||
// PKZIP (Unknown)
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x00, 0x00 }))
|
||||
return WrapperType.PKZIP;
|
||||
|
||||
// PKZIP
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x03, 0x04 }))
|
||||
return WrapperType.PKZIP;
|
||||
|
||||
// PKZIP (Empty Archive)
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x05, 0x06 }))
|
||||
return WrapperType.PKZIP;
|
||||
|
||||
// PKZIP (Spanned Archive)
|
||||
if (magic.StartsWith(new byte?[] { 0x50, 0x4b, 0x07, 0x08 }))
|
||||
return WrapperType.PKZIP;
|
||||
|
||||
// PKZIP
|
||||
if (extension.Equals("zip", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.PKZIP;
|
||||
@@ -780,6 +529,10 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region PLJ
|
||||
|
||||
// https://www.iana.org/assignments/media-types/audio/vnd.everad.plj
|
||||
if (magic.StartsWith(new byte?[] { 0xFF, 0x9D, 0x53, 0x4B }))
|
||||
return WrapperType.PlayJAudioFile;
|
||||
|
||||
// https://www.iana.org/assignments/media-types/audio/vnd.everad.plj
|
||||
if (extension.Equals("plj", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.PlayJAudioFile;
|
||||
@@ -788,6 +541,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region Quantum
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x44, 0x53 }))
|
||||
return WrapperType.Quantum;
|
||||
|
||||
if (extension.Equals("q", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.Quantum;
|
||||
|
||||
@@ -799,20 +555,56 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region RAR
|
||||
|
||||
// RAR archive version 1.50 onwards
|
||||
if (magic.StartsWith(new byte?[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00 }))
|
||||
return WrapperType.RAR;
|
||||
|
||||
// RAR archive version 5.0 onwards
|
||||
if (magic.StartsWith(new byte?[] { 0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00 }))
|
||||
return WrapperType.RAR;
|
||||
|
||||
if (extension.Equals("rar", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.RAR;
|
||||
|
||||
#endregion
|
||||
|
||||
#region RealArcade
|
||||
|
||||
// RASGI2.0
|
||||
// Found in the ".rgs files in IA item "Nova_RealArcadeCD_USA".
|
||||
if (magic.StartsWith(new byte?[] { 0x52, 0x41, 0x53, 0x47, 0x49, 0x32, 0x2E, 0x30 }))
|
||||
return WrapperType.RealArcadeInstaller;
|
||||
|
||||
// XZip2.0
|
||||
// Found in the ".mez" files in IA item "Nova_RealArcadeCD_USA".
|
||||
if (magic.StartsWith(new byte?[] { 0x58, 0x5A, 0x69, 0x70, 0x32, 0x2E, 0x30 }))
|
||||
return WrapperType.RealArcadeMezzanine;
|
||||
|
||||
#endregion
|
||||
|
||||
#region SevenZip
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c }))
|
||||
return WrapperType.SevenZip;
|
||||
|
||||
if (extension.Equals("7z", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.SevenZip;
|
||||
|
||||
#endregion
|
||||
|
||||
#region SFFS
|
||||
|
||||
// Found in Redump entry 81756, confirmed to be "StarForce Filesystem" by PiD.
|
||||
if (magic.StartsWith(new byte?[] { 0x53, 0x46, 0x46, 0x53 }))
|
||||
return WrapperType.SFFS;
|
||||
|
||||
#endregion
|
||||
|
||||
#region SGA
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x5F, 0x41, 0x52, 0x43, 0x48, 0x49, 0x56, 0x45 }))
|
||||
return WrapperType.SGA;
|
||||
|
||||
if (extension.Equals("sga", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.SGA;
|
||||
|
||||
@@ -820,6 +612,12 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region TapeArchive
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30 }))
|
||||
return WrapperType.TapeArchive;
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x75, 0x73, 0x74, 0x61, 0x72, 0x20, 0x20, 0x00 }))
|
||||
return WrapperType.TapeArchive;
|
||||
|
||||
if (extension.Equals("tar", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.SevenZip;
|
||||
|
||||
@@ -827,6 +625,37 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region Textfile
|
||||
|
||||
// Not all textfiles can be determined through magic number
|
||||
|
||||
// HTML
|
||||
if (magic.StartsWith(new byte?[] { 0x3c, 0x68, 0x74, 0x6d, 0x6c }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// HTML and XML
|
||||
if (magic.StartsWith(new byte?[] { 0x3c, 0x21, 0x44, 0x4f, 0x43, 0x54, 0x59, 0x50, 0x45 }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// InstallShield Compiled Rules
|
||||
if (magic.StartsWith(new byte?[] { 0x61, 0x4C, 0x75, 0x5A }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// Microsoft Office File (old)
|
||||
if (magic.StartsWith(new byte?[] { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// Rich Text File
|
||||
if (magic.StartsWith(new byte?[] { 0x7b, 0x5c, 0x72, 0x74, 0x66, 0x31 }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// Windows Help File
|
||||
if (magic.StartsWith(new byte?[] { 0x3F, 0x5F, 0x03, 0x00 }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// XML
|
||||
// "<?xml"
|
||||
if (magic.StartsWith(new byte?[] { 0x3C, 0x3F, 0x78, 0x6D, 0x6C }))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
// "Description in Zip"
|
||||
if (extension.Equals("diz", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.Textfile;
|
||||
@@ -873,20 +702,24 @@ namespace SabreTools.Serialization.Wrappers
|
||||
if (extension.Equals("xml", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.Textfile;
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region VBSP
|
||||
|
||||
// Shares an extension with BSP so it can't be used accurately
|
||||
// if (extension.Equals("bsp", StringComparison.OrdinalIgnoreCase))
|
||||
// return WrapperType.VBSP;
|
||||
if (magic.StartsWith(new byte?[] { 0x56, 0x42, 0x53, 0x50 }))
|
||||
return WrapperType.VBSP;
|
||||
|
||||
// Shares an extension with BSP
|
||||
if (extension.Equals("bsp", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.VBSP;
|
||||
|
||||
#endregion
|
||||
|
||||
#region VPK
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x34, 0x12, 0xaa, 0x55 }))
|
||||
return WrapperType.VPK;
|
||||
|
||||
// Common extension so this cannot be used accurately
|
||||
// if (extension.Equals("vpk", StringComparison.OrdinalIgnoreCase))
|
||||
// return WrapperType.VPK;
|
||||
@@ -895,6 +728,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region WAD
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x57, 0x41, 0x44, 0x33 }))
|
||||
return WrapperType.WAD;
|
||||
|
||||
// Common extension so this cannot be used accurately
|
||||
// if (extension.Equals("wad", StringComparison.OrdinalIgnoreCase))
|
||||
// return WrapperType.WAD;
|
||||
@@ -903,6 +739,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region XZ
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00 }))
|
||||
return WrapperType.XZ;
|
||||
|
||||
if (extension.Equals("xz", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.XZ;
|
||||
|
||||
@@ -910,6 +749,9 @@ namespace SabreTools.Serialization.Wrappers
|
||||
|
||||
#region XZP
|
||||
|
||||
if (magic.StartsWith(new byte?[] { 0x70, 0x69, 0x5A, 0x78 }))
|
||||
return WrapperType.XZP;
|
||||
|
||||
if (extension.Equals("xzp", StringComparison.OrdinalIgnoreCase))
|
||||
return WrapperType.XZP;
|
||||
|
||||
|
||||
@@ -136,7 +136,6 @@ namespace SabreTools.Serialization.Wrappers
|
||||
/// <summary>
|
||||
/// PKWARE ZIP archive and derivatives
|
||||
/// </summary>
|
||||
/// <remarks>Currently has no IWrapper implementation</remarks>
|
||||
PKZIP,
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Test
|
||||
}
|
||||
|
||||
// Loop through the input paths
|
||||
foreach (string inputPath in args)
|
||||
foreach (string inputPath in options.InputPaths)
|
||||
{
|
||||
#if NETFRAMEWORK
|
||||
PrintPathInfo(inputPath, false, options.Debug);
|
||||
@@ -81,12 +81,8 @@ namespace Test
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
// Get the file type
|
||||
WrapperType ft = WrapperFactory.GetFileType(magic ?? []);
|
||||
if (ft == WrapperType.UNKNOWN)
|
||||
{
|
||||
string extension = Path.GetExtension(file).TrimStart('.');
|
||||
ft = WrapperFactory.GetFileType(extension);
|
||||
}
|
||||
string extension = Path.GetExtension(file).TrimStart('.');
|
||||
WrapperType ft = WrapperFactory.GetFileType(magic ?? [], extension);
|
||||
|
||||
// Print out the file format
|
||||
Console.WriteLine($"File format found: {ft}");
|
||||
|
||||
Reference in New Issue
Block a user