From fa9e9a0b2bf9b5383cb0fb0f8bc2051f2ef191f6 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 16 Dec 2024 23:08:45 -0500 Subject: [PATCH] Be explicit about endianness --- .../Deserializers/AACS.cs | 508 +++++----- .../Deserializers/BDPlus.cs | 4 +- .../Deserializers/BFPK.cs | 35 +- SabreTools.Serialization/Deserializers/BSP.cs | 347 +++++-- SabreTools.Serialization/Deserializers/CFB.cs | 90 +- SabreTools.Serialization/Deserializers/CHD.cs | 207 +++- SabreTools.Serialization/Deserializers/CIA.cs | 312 +++--- SabreTools.Serialization/Deserializers/GCF.cs | 496 +++++++--- SabreTools.Serialization/Deserializers/IRD.cs | 2 +- .../Deserializers/InstallShieldArchiveV3.cs | 90 +- .../Deserializers/InstallShieldCabinet.cs | 582 ++++++----- .../Deserializers/LinearExecutable.cs | 494 ++++++---- .../Deserializers/MSDOS.cs | 93 +- .../Deserializers/MoPaQ.cs | 299 +++--- .../Deserializers/N3DS.cs | 579 ++++++++--- SabreTools.Serialization/Deserializers/NCF.cs | 293 +++++- .../Deserializers/NewExecutable.cs | 490 +++++----- .../Deserializers/Nitro.cs | 261 +++-- SabreTools.Serialization/Deserializers/PAK.cs | 45 +- SabreTools.Serialization/Deserializers/PFF.cs | 111 ++- SabreTools.Serialization/Deserializers/PIC.cs | 101 +- .../Deserializers/PKZIP.cs | 1 + .../Deserializers/PortableExecutable.cs | 922 ++++++++++-------- .../Deserializers/Quantum.cs | 64 +- SabreTools.Serialization/Deserializers/SFB.cs | 28 +- SabreTools.Serialization/Deserializers/SFO.cs | 39 +- .../Deserializers/VBSP.cs | 42 +- SabreTools.Serialization/Deserializers/VPK.cs | 195 ++-- .../Deserializers/WAD3.cs | 193 ++-- SabreTools.Serialization/Deserializers/XZP.cs | 119 ++- .../Extensions.PortableExecutable.cs | 1 + 31 files changed, 4524 insertions(+), 2519 deletions(-) diff --git a/SabreTools.Serialization/Deserializers/AACS.cs b/SabreTools.Serialization/Deserializers/AACS.cs index 6c4c8ae9..4197e8b8 100644 --- a/SabreTools.Serialization/Deserializers/AACS.cs +++ b/SabreTools.Serialization/Deserializers/AACS.cs @@ -17,9 +17,6 @@ namespace SabreTools.Serialization.Deserializers try { - // Create a new media key block to fill - var mediaKeyBlock = new MediaKeyBlock(); - #region Records // Create the records list @@ -49,7 +46,7 @@ namespace SabreTools.Serialization.Deserializers // Set the records if (records.Count > 0) - return new MediaKeyBlock { Records = [.. records ]}; + return new MediaKeyBlock { Records = [.. records] }; return null; } @@ -61,29 +58,29 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a record + /// Parse a Stream into a Record /// /// Stream to parse - /// Filled record on success, null on error + /// Filled Record on success, null on error private static Record? ParseRecord(Stream data) { - // The first 4 bytes make up the type and length + // The first byte is the type RecordType type = (RecordType)data.ReadByteValue(); - uint length = data.ReadUInt24(); + data.Seek(-1, SeekOrigin.Current); // Create a record based on the type return type switch { // Known record types - RecordType.EndOfMediaKeyBlock => ParseEndOfMediaKeyBlockRecord(data, type, length), - RecordType.ExplicitSubsetDifference => ParseExplicitSubsetDifferenceRecord(data, type, length), - RecordType.MediaKeyData => ParseMediaKeyDataRecord(data, type, length), - RecordType.SubsetDifferenceIndex => ParseSubsetDifferenceIndexRecord(data, type, length), - RecordType.TypeAndVersion => ParseTypeAndVersionRecord(data, type, length), - RecordType.DriveRevocationList => ParseDriveRevocationListRecord(data, type, length), - RecordType.HostRevocationList => ParseHostRevocationListRecord(data, type, length), - RecordType.VerifyMediaKey => ParseVerifyMediaKeyRecord(data, type, length), - RecordType.Copyright => ParseCopyrightRecord(data, type, length), + RecordType.EndOfMediaKeyBlock => ParseEndOfMediaKeyBlockRecord(data), + RecordType.ExplicitSubsetDifference => ParseExplicitSubsetDifferenceRecord(data), + RecordType.MediaKeyData => ParseMediaKeyDataRecord(data), + RecordType.SubsetDifferenceIndex => ParseSubsetDifferenceIndexRecord(data), + RecordType.TypeAndVersion => ParseTypeAndVersionRecord(data), + RecordType.DriveRevocationList => ParseDriveRevocationListRecord(data), + RecordType.HostRevocationList => ParseHostRevocationListRecord(data), + RecordType.VerifyMediaKey => ParseVerifyMediaKeyRecord(data), + RecordType.Copyright => ParseCopyrightRecord(data), // Unknown record type _ => null, @@ -91,324 +88,327 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into an end of media key block record + /// Parse a Stream into a CopyrightRecord /// /// Stream to parse - /// Filled end of media key block record on success, null on error - private static EndOfMediaKeyBlockRecord? ParseEndOfMediaKeyBlockRecord(Stream data, RecordType type, uint length) + /// Filled CopyrightRecord on success, null on error + public static CopyrightRecord ParseCopyrightRecord(Stream data) { - // Verify we're calling the right parser - if (type != RecordType.EndOfMediaKeyBlock) - return null; + var obj = new CopyrightRecord(); - var record = new EndOfMediaKeyBlockRecord(); + obj.RecordType = (RecordType)data.ReadByteValue(); + obj.RecordLength = data.ReadUInt24(); + if (obj.RecordLength > 4) + { + byte[] copyright = data.ReadBytes((int)(obj.RecordLength - 4)); + obj.Copyright = Encoding.ASCII.GetString(copyright).TrimEnd('\0'); + } - record.RecordType = type; - record.RecordLength = length; - if (length > 4) - record.SignatureData = data.ReadBytes((int)(length - 4)); + return obj; + } + + /// + /// Parse a Stream into a DriveRevocationListEntry + /// + /// Stream to parse + /// Filled DriveRevocationListEntry on success, null on error + public static DriveRevocationListEntry ParseDriveRevocationListEntry(Stream data) + { + var obj = new DriveRevocationListEntry(); - return record; + obj.Range = data.ReadUInt16BigEndian(); + obj.DriveID = data.ReadBytes(6); + + return obj; } /// - /// Parse a Stream into an explicit subset-difference record + /// Parse a Stream into a DriveRevocationListRecord /// /// Stream to parse - /// Filled explicit subset-difference record on success, null on error - private static ExplicitSubsetDifferenceRecord? ParseExplicitSubsetDifferenceRecord(Stream data, RecordType type, uint length) + /// Filled DriveRevocationListRecord on success, null on error + public static DriveRevocationListRecord ParseDriveRevocationListRecord(Stream data) { - // Verify we're calling the right parser - if (type != RecordType.ExplicitSubsetDifference) - return null; - - var record = new ExplicitSubsetDifferenceRecord(); - - record.RecordType = type; - record.RecordLength = length; - // Cache the current offset - long initialOffset = data.Position - 4; + long initialOffset = data.Position; - // Create the subset difference list - var subsetDifferences = new List(); + var obj = new DriveRevocationListRecord(); + + obj.RecordType = (RecordType)data.ReadByteValue(); + obj.RecordLength = data.ReadUInt24(); + obj.TotalNumberOfEntries = data.ReadUInt32BigEndian(); + + // Try to parse the signature blocks + var blocks = new List(); + uint entryCount = 0; + while (entryCount < obj.TotalNumberOfEntries && data.Position < initialOffset + obj.RecordLength) + { + var block = ParseDriveRevocationSignatureBlock(data); + entryCount += block.NumberOfEntries; + blocks.Add(block); + + // If we have an empty block + if (block.NumberOfEntries == 0) + break; + } + + // Set the signature blocks + obj.SignatureBlocks = [.. blocks]; + + // If there's any data left, discard it + if (data.Position < initialOffset + obj.RecordLength) + _ = data.ReadBytes((int)(initialOffset + obj.RecordLength - data.Position)); + + return obj; + } + + /// + /// Parse a Stream into a DriveRevocationSignatureBlock + /// + /// Stream to parse + /// Filled DriveRevocationSignatureBlock on success, null on error + public static DriveRevocationSignatureBlock ParseDriveRevocationSignatureBlock(Stream data) + { + var obj = new DriveRevocationSignatureBlock(); + + obj.NumberOfEntries = data.ReadUInt32BigEndian(); + obj.EntryFields = new DriveRevocationListEntry[obj.NumberOfEntries]; + for (int i = 0; i < obj.EntryFields.Length; i++) + { + obj.EntryFields[i] = ParseDriveRevocationListEntry(data); + } + + return obj; + } + + /// + /// Parse a Stream into a EndOfMediaKeyBlockRecord + /// + /// Stream to parse + /// Filled EndOfMediaKeyBlockRecord on success, null on error + public static EndOfMediaKeyBlockRecord ParseEndOfMediaKeyBlockRecord(Stream data) + { + var obj = new EndOfMediaKeyBlockRecord(); + + obj.RecordType = (RecordType)data.ReadByteValue(); + obj.RecordLength = data.ReadUInt24(); + if (obj.RecordLength > 4) + obj.SignatureData = data.ReadBytes((int)(obj.RecordLength - 4)); + + return obj; + } + + /// + /// Parse a Stream into a ExplicitSubsetDifferenceRecord + /// + /// Stream to parse + /// Filled ExplicitSubsetDifferenceRecord on success, null on error + public static ExplicitSubsetDifferenceRecord ParseExplicitSubsetDifferenceRecord(Stream data) + { + // Cache the current offset + long initialOffset = data.Position; + + var obj = new ExplicitSubsetDifferenceRecord(); + + obj.RecordType = (RecordType)data.ReadByteValue(); + obj.RecordLength = data.ReadUInt24(); // Try to parse the subset differences - while (data.Position < initialOffset + length - 5) + var subsetDifferences = new List(); + while (data.Position < initialOffset + obj.RecordLength - 5) { - var subsetDifference = new SubsetDifference(); - - subsetDifference.Mask = data.ReadByteValue(); - subsetDifference.Number = data.ReadUInt32BigEndian(); - + var subsetDifference = ParseSubsetDifference(data); subsetDifferences.Add(subsetDifference); } // Set the subset differences - record.SubsetDifferences = [.. subsetDifferences]; + obj.SubsetDifferences = [.. subsetDifferences]; // If there's any data left, discard it - if (data.Position < initialOffset + length) - _ = data.ReadBytes((int)(initialOffset + length - data.Position)); + if (data.Position < initialOffset + obj.RecordLength) + _ = data.ReadBytes((int)(initialOffset + obj.RecordLength - data.Position)); - return record; + return obj; } /// - /// Parse a Stream into a media key data record + /// Parse a Stream into a HostRevocationListEntry /// /// Stream to parse - /// Filled media key data record on success, null on error - private static MediaKeyDataRecord? ParseMediaKeyDataRecord(Stream data, RecordType type, uint length) + /// Filled HostRevocationListEntry on success, null on error + public static HostRevocationListEntry ParseHostRevocationListEntry(Stream data) { - // Verify we're calling the right parser - if (type != RecordType.MediaKeyData) - return null; + var obj = new HostRevocationListEntry(); - var record = new MediaKeyDataRecord(); + obj.Range = data.ReadUInt16BigEndian(); + obj.HostID = data.ReadBytes(6); - record.RecordType = type; - record.RecordLength = length; + return obj; + } + /// + /// Parse a Stream into a HostRevocationListRecord + /// + /// Stream to parse + /// Filled HostRevocationListRecord on success, null on error + public static HostRevocationListRecord ParseHostRevocationListRecord(Stream data) + { // Cache the current offset - long initialOffset = data.Position - 4; + long initialOffset = data.Position; - // Create the media key list - var mediaKeys = new List(); + var obj = new HostRevocationListRecord(); + + obj.RecordType = (RecordType)data.ReadByteValue(); + obj.RecordLength = data.ReadUInt24(); + obj.TotalNumberOfEntries = data.ReadUInt32BigEndian(); + + // Try to parse the signature blocks + var blocks = new List(); + for (uint entryCount = 0; entryCount < obj.TotalNumberOfEntries && data.Position < initialOffset + obj.RecordLength;) + { + var block = ParseHostRevocationSignatureBlock(data); + entryCount += block.NumberOfEntries; + blocks.Add(block); + + // If we have an empty block + if (block.NumberOfEntries == 0) + break; + } + + // Set the signature blocks + obj.SignatureBlocks = [.. blocks]; + + // If there's any data left, discard it + if (data.Position < initialOffset + obj.RecordLength) + _ = data.ReadBytes((int)(initialOffset + obj.RecordLength - data.Position)); + + return obj; + } + + /// + /// Parse a Stream into a HostRevocationSignatureBlock + /// + /// Stream to parse + /// Filled HostRevocationSignatureBlock on success, null on error + public static HostRevocationSignatureBlock ParseHostRevocationSignatureBlock(Stream data) + { + var obj = new HostRevocationSignatureBlock(); + + obj.NumberOfEntries = data.ReadUInt32BigEndian(); + obj.EntryFields = new HostRevocationListEntry[obj.NumberOfEntries]; + for (int i = 0; i < obj.EntryFields.Length; i++) + { + obj.EntryFields[i] = ParseHostRevocationListEntry(data); + } + + return obj; + } + + /// + /// Parse a Stream into a MediaKeyDataRecord + /// + /// Stream to parse + /// Filled MediaKeyDataRecord on success, null on error + public static MediaKeyDataRecord ParseMediaKeyDataRecord(Stream data) + { + // Cache the current offset + long initialOffset = data.Position; + + var obj = new MediaKeyDataRecord(); + + obj.RecordType = (RecordType)data.ReadByteValue(); + obj.RecordLength = data.ReadUInt24(); // Try to parse the media keys - while (data.Position < initialOffset + length) + var mediaKeys = new List(); + while (data.Position < initialOffset + obj.RecordLength) { byte[] mediaKey = data.ReadBytes(0x10); mediaKeys.Add(mediaKey); } // Set the media keys - record.MediaKeyData = [.. mediaKeys]; + obj.MediaKeyData = [.. mediaKeys]; - return record; + return obj; } /// - /// Parse a Stream into a subset-difference index record + /// Parse a Stream into a SubsetDifference /// /// Stream to parse - /// Filled subset-difference index record on success, null on error - private static SubsetDifferenceIndexRecord? ParseSubsetDifferenceIndexRecord(Stream data, RecordType type, uint length) + /// Filled SubsetDifference on success, null on error + public static SubsetDifference ParseSubsetDifference(Stream data) { - // Verify we're calling the right parser - if (type != RecordType.SubsetDifferenceIndex) - return null; + var obj = new SubsetDifference(); - var record = new SubsetDifferenceIndexRecord(); + obj.Mask = data.ReadByteValue(); + obj.Number = data.ReadUInt32BigEndian(); - record.RecordType = type; - record.RecordLength = length; + return obj; + } + /// + /// Parse a Stream into a SubsetDifferenceIndexRecord + /// + /// Stream to parse + /// Filled SubsetDifferenceIndexRecord on success, null on error + public static SubsetDifferenceIndexRecord ParseSubsetDifferenceIndexRecord(Stream data) + { // Cache the current offset - long initialOffset = data.Position - 4; + long initialOffset = data.Position; - record.Span = data.ReadUInt32BigEndian(); + var obj = new SubsetDifferenceIndexRecord(); - // Create the offset list - var offsets = new List(); + obj.RecordType = (RecordType)data.ReadByteValue(); + obj.RecordLength = data.ReadUInt24(); + obj.Span = data.ReadUInt32BigEndian(); // Try to parse the offsets - while (data.Position < initialOffset + length) + var offsets = new List(); + while (data.Position < initialOffset + obj.RecordLength) { uint offset = data.ReadUInt32BigEndian(); offsets.Add(offset); } // Set the offsets - record.Offsets = [.. offsets]; + obj.Offsets = [.. offsets]; - return record; + return obj; } /// - /// Parse a Stream into a type and version record + /// Parse a Stream into a TypeAndVersionRecord /// /// Stream to parse - /// Filled type and version record on success, null on error - private static TypeAndVersionRecord? ParseTypeAndVersionRecord(Stream data, RecordType type, uint length) + /// Filled TypeAndVersionRecord on success, null on error + public static TypeAndVersionRecord ParseTypeAndVersionRecord(Stream data) { - // Verify we're calling the right parser - if (type != RecordType.TypeAndVersion) - return null; + var obj = new TypeAndVersionRecord(); - var record = new TypeAndVersionRecord(); + obj.RecordType = (RecordType)data.ReadByteValue(); + obj.RecordLength = data.ReadUInt24(); + obj.MediaKeyBlockType = (MediaKeyBlockType)data.ReadUInt32BigEndian(); + obj.VersionNumber = data.ReadUInt32BigEndian(); - record.RecordType = type; - record.RecordLength = length; - record.MediaKeyBlockType = (MediaKeyBlockType)data.ReadUInt32BigEndian(); - record.VersionNumber = data.ReadUInt32BigEndian(); - - return record; + return obj; } /// - /// Parse a Stream into a drive revocation list record + /// Parse a Stream into a VerifyMediaKeyRecord /// /// Stream to parse - /// Filled drive revocation list record on success, null on error - private static DriveRevocationListRecord? ParseDriveRevocationListRecord(Stream data, RecordType type, uint length) + /// Filled VerifyMediaKeyRecord on success, null on error + public static VerifyMediaKeyRecord ParseVerifyMediaKeyRecord(Stream data) { - // Verify we're calling the right parser - if (type != RecordType.DriveRevocationList) - return null; + var obj = new VerifyMediaKeyRecord(); - var record = new DriveRevocationListRecord(); + obj.RecordType = (RecordType)data.ReadByteValue(); + obj.RecordLength = data.ReadUInt24(); + obj.CiphertextValue = data.ReadBytes(0x10); - record.RecordType = type; - record.RecordLength = length; - - // Cache the current offset - long initialOffset = data.Position - 4; - - record.TotalNumberOfEntries = data.ReadUInt32BigEndian(); - - // Create the signature blocks list - var blocks = new List(); - - // Try to parse the signature blocks - int entryCount = 0; - while (entryCount < record.TotalNumberOfEntries && data.Position < initialOffset + length) - { - var block = new DriveRevocationSignatureBlock(); - - block.NumberOfEntries = data.ReadUInt32BigEndian(); - block.EntryFields = new DriveRevocationListEntry[block.NumberOfEntries]; - for (int i = 0; i < block.EntryFields.Length; i++) - { - var entry = new DriveRevocationListEntry(); - - entry.Range = data.ReadUInt16BigEndian(); - entry.DriveID = data.ReadBytes(6); - - block.EntryFields[i] = entry; - entryCount++; - } - - blocks.Add(block); - - // If we have an empty block - if (block.NumberOfEntries == 0) - break; - } - - // Set the signature blocks - record.SignatureBlocks = [.. blocks]; - - // If there's any data left, discard it - if (data.Position < initialOffset + length) - _ = data.ReadBytes((int)(initialOffset + length - data.Position)); - - return record; - } - - /// - /// Parse a Stream into a host revocation list record - /// - /// Stream to parse - /// Filled host revocation list record on success, null on error - private static HostRevocationListRecord? ParseHostRevocationListRecord(Stream data, RecordType type, uint length) - { - // Verify we're calling the right parser - if (type != RecordType.HostRevocationList) - return null; - - var record = new HostRevocationListRecord(); - - record.RecordType = type; - record.RecordLength = length; - - // Cache the current offset - long initialOffset = data.Position - 4; - - record.TotalNumberOfEntries = data.ReadUInt32BigEndian(); - - // Create the signature blocks list - var blocks = new List(); - - // Try to parse the signature blocks - int entryCount = 0; - while (entryCount < record.TotalNumberOfEntries && data.Position < initialOffset + length) - { - var block = new HostRevocationSignatureBlock(); - - block.NumberOfEntries = data.ReadUInt32BigEndian(); - block.EntryFields = new HostRevocationListEntry[block.NumberOfEntries]; - for (int i = 0; i < block.EntryFields.Length; i++) - { - var entry = new HostRevocationListEntry(); - - entry.Range = data.ReadUInt16BigEndian(); - entry.HostID = data.ReadBytes(6); - - block.EntryFields[i] = entry; - entryCount++; - } - - blocks.Add(block); - - // If we have an empty block - if (block.NumberOfEntries == 0) - break; - } - - // Set the signature blocks - record.SignatureBlocks = [.. blocks]; - - // If there's any data left, discard it - if (data.Position < initialOffset + length) - _ = data.ReadBytes((int)(initialOffset + length - data.Position)); - - return record; - } - - /// - /// Parse a Stream into a verify media key record - /// - /// Stream to parse - /// Filled verify media key record on success, null on error - private static VerifyMediaKeyRecord? ParseVerifyMediaKeyRecord(Stream data, RecordType type, uint length) - { - // Verify we're calling the right parser - if (type != RecordType.VerifyMediaKey) - return null; - - var record = new VerifyMediaKeyRecord(); - - record.RecordType = type; - record.RecordLength = length; - record.CiphertextValue = data.ReadBytes(0x10); - - return record; - } - - /// - /// Parse a Stream into a copyright record - /// - /// Stream to parse - /// Filled copyright record on success, null on error - private static CopyrightRecord? ParseCopyrightRecord(Stream data, RecordType type, uint length) - { - // Verify we're calling the right parser - if (type != RecordType.Copyright) - return null; - - var record = new CopyrightRecord(); - - record.RecordType = type; - record.RecordLength = length; - if (length > 4) - { - byte[] copyright = data.ReadBytes((int)(length - 4)); - record.Copyright = Encoding.ASCII.GetString(copyright).TrimEnd('\0'); - } - - return record; + return obj; } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/BDPlus.cs b/SabreTools.Serialization/Deserializers/BDPlus.cs index ab1bab52..09f7d945 100644 --- a/SabreTools.Serialization/Deserializers/BDPlus.cs +++ b/SabreTools.Serialization/Deserializers/BDPlus.cs @@ -35,8 +35,8 @@ namespace SabreTools.Serialization.Deserializers if (svm.Day < 1 || svm.Day > 31) return null; - svm.Unknown2 = data.ReadUInt32(); - svm.Length = data.ReadUInt32(); + svm.Unknown2 = data.ReadUInt32LittleEndian(); + svm.Length = data.ReadUInt32LittleEndian(); if (svm.Length > 0) svm.Data = data.ReadBytes((int)svm.Length); diff --git a/SabreTools.Serialization/Deserializers/BFPK.cs b/SabreTools.Serialization/Deserializers/BFPK.cs index 35e0218c..cbd898c9 100644 --- a/SabreTools.Serialization/Deserializers/BFPK.cs +++ b/SabreTools.Serialization/Deserializers/BFPK.cs @@ -23,8 +23,8 @@ namespace SabreTools.Serialization.Deserializers #region Header // Try to parse the header - var header = data.ReadType
(); - if (header?.Magic != SignatureString) + var header = ParseHeader(data); + if (header.Magic != SignatureString) return null; // Set the archive header @@ -58,32 +58,49 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a file entry + /// Parse a Stream into a FileEntry /// /// Stream to parse - /// Filled file entry on success, null on error - private static FileEntry ParseFileEntry(Stream data) + /// Filled FileEntry on success, null on error + public static FileEntry ParseFileEntry(Stream data) { var fileEntry = new FileEntry(); - fileEntry.NameSize = data.ReadInt32(); + fileEntry.NameSize = data.ReadInt32LittleEndian(); if (fileEntry.NameSize > 0) { byte[] name = data.ReadBytes(fileEntry.NameSize); fileEntry.Name = Encoding.ASCII.GetString(name); } - fileEntry.UncompressedSize = data.ReadInt32(); - fileEntry.Offset = data.ReadInt32(); + fileEntry.UncompressedSize = data.ReadInt32LittleEndian(); + fileEntry.Offset = data.ReadInt32LittleEndian(); if (fileEntry.Offset > 0) { long currentOffset = data.Position; data.Seek(fileEntry.Offset, SeekOrigin.Begin); - fileEntry.CompressedSize = data.ReadInt32(); + fileEntry.CompressedSize = data.ReadInt32LittleEndian(); data.Seek(currentOffset, SeekOrigin.Begin); } return fileEntry; } + + /// + /// Parse a Stream into a Header + /// + /// Stream to parse + /// Filled Header on success, null on error + public static Header ParseHeader(Stream data) + { + var obj = new Header(); + + byte[] magic = data.ReadBytes(4); + obj.Magic = Encoding.ASCII.GetString(magic); + obj.Version = data.ReadInt32LittleEndian(); + obj.Files = data.ReadInt32LittleEndian(); + + return obj; + } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/BSP.cs b/SabreTools.Serialization/Deserializers/BSP.cs index f1e69058..b68fc348 100644 --- a/SabreTools.Serialization/Deserializers/BSP.cs +++ b/SabreTools.Serialization/Deserializers/BSP.cs @@ -25,9 +25,7 @@ namespace SabreTools.Serialization.Deserializers #region Header // Try to parse the header - var header = data.ReadType(); - if (header?.Lumps == null || header.Lumps.Length != BSP_HEADER_LUMPS) - return null; + var header = ParseBspHeader(data); if (header.Version < 29 || header.Version > 30) return null; @@ -41,7 +39,7 @@ namespace SabreTools.Serialization.Deserializers for (int l = 0; l < BSP_HEADER_LUMPS; l++) { // Get the next lump entry - var lumpEntry = header.Lumps[l]; + var lumpEntry = header.Lumps![l]; if (lumpEntry == null) continue; if (lumpEntry.Offset == 0 || lumpEntry.Length == 0) @@ -115,6 +113,272 @@ namespace SabreTools.Serialization.Deserializers } } + /// + /// Parse a Stream into BspFace + /// + /// Stream to parse + /// Filled BspFace on success, null on error + public static BspFace ParseBspFace(Stream data) + { + var obj = new BspFace(); + + obj.PlaneIndex = data.ReadUInt16LittleEndian(); + obj.PlaneSideCount = data.ReadUInt16LittleEndian(); + obj.FirstEdgeIndex = data.ReadUInt32LittleEndian(); + obj.NumberOfEdges = data.ReadUInt16LittleEndian(); + obj.TextureInfoIndex = data.ReadUInt16LittleEndian(); + obj.LightingStyles = data.ReadBytes(4); + obj.LightmapOffset = data.ReadInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into BspHeader + /// + /// Stream to parse + /// Filled BspHeader on success, null on error + public static BspHeader ParseBspHeader(Stream data) + { + var obj = new BspHeader(); + + obj.Version = data.ReadInt32LittleEndian(); + obj.Lumps = new BspLumpEntry[BSP_HEADER_LUMPS]; + for (int i = 0; i < BSP_HEADER_LUMPS; i++) + { + obj.Lumps[i] = ParseBspLumpEntry(data); + } + + return obj; + } + + /// + /// Parse a Stream into BspLeaf + /// + /// Stream to parse + /// Filled BspLeaf on success, null on error + public static BspLeaf ParseBspLeaf(Stream data) + { + var obj = new BspLeaf(); + + obj.Contents = (BspContents)data.ReadInt32LittleEndian(); + obj.VisOffset = data.ReadInt32LittleEndian(); + obj.Mins = new short[3]; + for (int i = 0; i < 3; i++) + { + obj.Mins[i] = data.ReadInt16LittleEndian(); + } + obj.Maxs = new short[3]; + for (int i = 0; i < 3; i++) + { + obj.Maxs[i] = data.ReadInt16LittleEndian(); + } + obj.FirstMarkSurfaceIndex = data.ReadUInt16LittleEndian(); + obj.MarkSurfacesCount = data.ReadUInt16LittleEndian(); + obj.AmbientLevels = data.ReadBytes(4); + + return obj; + } + + /// + /// Parse a Stream into BspLumpEntry + /// + /// Stream to parse + /// Filled BspLumpEntry on success, null on error + public static BspLumpEntry ParseBspLumpEntry(Stream data) + { + var obj = new BspLumpEntry(); + + obj.Offset = data.ReadInt32LittleEndian(); + obj.Length = data.ReadInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into BspModel + /// + /// Stream to parse + /// Filled BspModel on success, null on error + public static BspModel ParseBspModel(Stream data) + { + var obj = new BspModel(); + + obj.Mins = ParseVector3D(data); + obj.Maxs = ParseVector3D(data); + obj.OriginVector = ParseVector3D(data); + obj.HeadnodesIndex = new int[MAX_MAP_HULLS]; + for (int i = 0; i < MAX_MAP_HULLS; i++) + { + obj.HeadnodesIndex[i] = data.ReadInt32LittleEndian(); + } + obj.VisLeafsCount = data.ReadInt32LittleEndian(); + obj.FirstFaceIndex = data.ReadInt32LittleEndian(); + obj.FacesCount = data.ReadInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into BspNode + /// + /// Stream to parse + /// Filled BspNode on success, null on error + public static BspNode ParseBspNode(Stream data) + { + var obj = new BspNode(); + + obj.PlaneIndex = data.ReadUInt32LittleEndian(); + obj.Children = new ushort[2]; + for (int i = 0; i < 2; i++) + { + obj.Children[i] = data.ReadUInt16LittleEndian(); + } + obj.Mins = new ushort[3]; + for (int i = 0; i < 3; i++) + { + obj.Mins[i] = data.ReadUInt16LittleEndian(); + } + obj.Maxs = new ushort[3]; + for (int i = 0; i < 3; i++) + { + obj.Maxs[i] = data.ReadUInt16LittleEndian(); + } + obj.FirstFace = data.ReadUInt16LittleEndian(); + obj.FaceCount = data.ReadUInt16LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into BspTexinfo + /// + /// Stream to parse + /// Filled BspTexinfo on success, null on error + public static BspTexinfo ParseBspTexinfo(Stream data) + { + var obj = new BspTexinfo(); + + obj.SVector = ParseVector3D(data); + obj.TextureSShift = data.ReadSingle(); + obj.TVector = ParseVector3D(data); + obj.TextureTShift = data.ReadSingle(); + obj.MiptexIndex = data.ReadUInt32LittleEndian(); + obj.Flags = (TextureFlag)data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into Clipnode + /// + /// Stream to parse + /// Filled Clipnode on success, null on error + public static Clipnode ParseClipnode(Stream data) + { + var obj = new Clipnode(); + + obj.PlaneIndex = data.ReadInt32LittleEndian(); + obj.ChildrenIndices = new short[2]; + for (int i = 0; i < 2; i++) + { + obj.ChildrenIndices[i] = data.ReadInt16LittleEndian(); + } + + return obj; + } + + /// + /// Parse a Stream into Edge + /// + /// Stream to parse + /// Filled Edge on success, null on error + public static Edge ParseEdge(Stream data) + { + var obj = new Edge(); + + obj.VertexIndices = new ushort[2]; + for (int i = 0; i < 2; i++) + { + obj.VertexIndices[i] = data.ReadUInt16LittleEndian(); + } + + return obj; + } + + /// + /// Parse a Stream into MipTexture + /// + /// Stream to parse + /// Filled MipTexture on success, null on error + public static MipTexture ParseMipTexture(Stream data) + { + var obj = new MipTexture(); + + byte[] name = data.ReadBytes(MAXTEXTURENAME); + obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); + obj.Width = data.ReadUInt32LittleEndian(); + obj.Height = data.ReadUInt32LittleEndian(); + obj.Offsets = new uint[MIPLEVELS]; + for (int i = 0; i < MIPLEVELS; i++) + { + obj.Offsets[i] = data.ReadUInt32LittleEndian(); + } + + return obj; + } + + /// + /// Parse a Stream into Plane + /// + /// Stream to parse + /// Filled Plane on success, null on error + public static Plane ParsePlane(Stream data) + { + var obj = new Plane(); + + obj.NormalVector = ParseVector3D(data); + obj.Distance = data.ReadSingle(); + obj.PlaneType = (PlaneType)data.ReadInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a TextureHeader + /// + /// Stream to parse + /// Filled TextureHeader on success, null on error + public static TextureHeader ParseTextureHeader(Stream data) + { + var obj = new TextureHeader(); + + obj.MipTextureCount = data.ReadUInt32LittleEndian(); + obj.Offsets = new int[obj.MipTextureCount]; + for (int i = 0; i < obj.Offsets.Length; i++) + { + obj.Offsets[i] = data.ReadInt16LittleEndian(); + } + + return obj; + } + + /// + /// Parse a Stream into Vector3D + /// + /// Stream to parse + /// Filled Vector3D on success, null on error + public static Vector3D ParseVector3D(Stream data) + { + var obj = new Vector3D(); + + obj.X = data.ReadSingle(); + obj.Y = data.ReadSingle(); + obj.Z = data.ReadSingle(); + + return obj; + } + /// /// Parse a Stream into LUMP_ENTITIES /// @@ -167,9 +431,8 @@ namespace SabreTools.Serialization.Deserializers var planes = new List(); while (data.Position < offset + length) { - var plane = data.ReadType(); - if (plane != null) - planes.Add(plane); + var plane = ParsePlane(data); + planes.Add(plane); } return new PlanesLump { Planes = [.. planes] }; @@ -188,34 +451,14 @@ namespace SabreTools.Serialization.Deserializers var textures = new List(); while (data.Position < offset + length) { - var texture = data.ReadType(); - if (texture != null) - textures.Add(texture); + var texture = ParseMipTexture(data); + textures.Add(texture); } lump.Textures = [.. textures]; return lump; } - /// - /// Parse a Stream into a Half-Life Level texture header - /// - /// Stream to parse - /// Filled Half-Life Level texture header on success, null on error - private static TextureHeader ParseTextureHeader(Stream data) - { - var textureHeader = new TextureHeader(); - - textureHeader.MipTextureCount = data.ReadUInt32(); - textureHeader.Offsets = new int[textureHeader.MipTextureCount]; - for (int i = 0; i < textureHeader.Offsets.Length; i++) - { - textureHeader.Offsets[i] = data.ReadInt32(); - } - - return textureHeader; - } - /// /// Parse a Stream into LUMP_VERTICES /// @@ -226,10 +469,7 @@ namespace SabreTools.Serialization.Deserializers var vertices = new List(); while (data.Position < offset + length) { - var vertex = data.ReadType(); - if (vertex == null) - break; - + var vertex = ParseVector3D(data); vertices.Add(vertex); } @@ -258,7 +498,7 @@ namespace SabreTools.Serialization.Deserializers lump.ByteOffsets[i] = new int[2]; for (int j = 0; j < 2; j++) { - lump.ByteOffsets[i][j] = data.ReadInt32(); + lump.ByteOffsets[i][j] = data.ReadInt32LittleEndian(); } } @@ -275,9 +515,8 @@ namespace SabreTools.Serialization.Deserializers var nodes = new List(); while (data.Position < offset + length) { - var node = data.ReadType(); - if (node != null) - nodes.Add(node); + var node = ParseBspNode(data); + nodes.Add(node); } return new BspNodesLump { Nodes = [.. nodes] }; @@ -293,9 +532,8 @@ namespace SabreTools.Serialization.Deserializers var texinfos = new List(); while (data.Position < offset + length) { - var texinfo = data.ReadType(); - if (texinfo != null) - texinfos.Add(texinfo); + var texinfo = ParseBspTexinfo(data); + texinfos.Add(texinfo); } return new BspTexinfoLump { Texinfos = [.. texinfos] }; @@ -311,9 +549,8 @@ namespace SabreTools.Serialization.Deserializers var faces = new List(); while (data.Position < offset + length) { - var face = data.ReadType(); - if (face != null) - faces.Add(face); + var face = ParseBspFace(data); + faces.Add(face); } return new BspFacesLump { Faces = [.. faces] }; @@ -347,9 +584,8 @@ namespace SabreTools.Serialization.Deserializers var clipnodes = new List(); while (data.Position < offset + length) { - var clipnode = data.ReadType(); - if (clipnode != null) - clipnodes.Add(clipnode); + var clipnode = ParseClipnode(data); + clipnodes.Add(clipnode); } return new ClipnodesLump { Clipnodes = [.. clipnodes] }; @@ -365,9 +601,8 @@ namespace SabreTools.Serialization.Deserializers var leaves = new List(); while (data.Position < offset + length) { - var leaf = data.ReadType(); - if (leaf != null) - leaves.Add(leaf); + var leaf = ParseBspLeaf(data); + leaves.Add(leaf); } return new BspLeavesLump { Leaves = [.. leaves] }; @@ -383,7 +618,7 @@ namespace SabreTools.Serialization.Deserializers var marksurfaces = new List(); while (data.Position < offset + length) { - marksurfaces.Add(data.ReadUInt16()); + marksurfaces.Add(data.ReadUInt16LittleEndian()); } return new MarksurfacesLump { Marksurfaces = [.. marksurfaces] }; @@ -399,9 +634,8 @@ namespace SabreTools.Serialization.Deserializers var edges = new List(); while (data.Position < offset + length) { - var edge = data.ReadType(); - if (edge != null) - edges.Add(edge); + var edge = ParseEdge(data); + edges.Add(edge); } return new EdgesLump { Edges = [.. edges] }; @@ -417,7 +651,7 @@ namespace SabreTools.Serialization.Deserializers var surfedges = new List(); while (data.Position < offset + length) { - surfedges.Add(data.ReadInt32()); + surfedges.Add(data.ReadInt32LittleEndian()); } return new SurfedgesLump { Surfedges = [.. surfedges] }; @@ -433,9 +667,8 @@ namespace SabreTools.Serialization.Deserializers var models = new List(); while (data.Position < offset + length) { - var model = data.ReadType(); - if (model != null) - models.Add(model); + var model = ParseBspModel(data); + models.Add(model); } return new BspModelsLump { Models = [.. models] }; diff --git a/SabreTools.Serialization/Deserializers/CFB.cs b/SabreTools.Serialization/Deserializers/CFB.cs index 8267fb4c..398a58c4 100644 --- a/SabreTools.Serialization/Deserializers/CFB.cs +++ b/SabreTools.Serialization/Deserializers/CFB.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Runtime.InteropServices; +using System.Text; using SabreTools.IO.Extensions; using SabreTools.Models.CFB; using static SabreTools.Models.CFB.Constants; @@ -26,7 +27,17 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the file header var fileHeader = ParseFileHeader(data); - if (fileHeader == null) + if (fileHeader?.Signature != SignatureUInt64) + return null; + if (fileHeader.ByteOrder != 0xFFFE) + return null; + if (fileHeader.MajorVersion == 3 && fileHeader.SectorShift != 0x0009) + return null; + else if (fileHeader.MajorVersion == 4 && fileHeader.SectorShift != 0x000C) + return null; + if (fileHeader.MajorVersion == 3 && fileHeader.NumberOfDirectorySectors != 0) + return null; + if (fileHeader.MiniStreamCutoffSize != 0x00001000) return null; // Set the file header @@ -228,32 +239,69 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a file header + /// Parse a Stream into a DirectoryEntry /// /// Stream to parse - /// Filled file header on success, null on error - private static FileHeader? ParseFileHeader(Stream data) + /// Filled DirectoryEntry on success, null on error + public static DirectoryEntry ParseDirectoryEntry(Stream data) { - var header = data.ReadType(); + var obj = new DirectoryEntry(); - if (header?.Signature != SignatureUInt64) - return null; - if (header.ByteOrder != 0xFFFE) - return null; - if (header.MajorVersion == 3 && header.SectorShift != 0x0009) - return null; - else if (header.MajorVersion == 4 && header.SectorShift != 0x000C) - return null; - if (header.MajorVersion == 3 && header.NumberOfDirectorySectors != 0) - return null; - if (header.MiniStreamCutoffSize != 0x00001000) - return null; + byte[] name = data.ReadBytes(32); + obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); + obj.NameLength = data.ReadUInt16LittleEndian(); + obj.ObjectType = (ObjectType)data.ReadByteValue(); + obj.ColorFlag = (ColorFlag)data.ReadByteValue(); + obj.LeftSiblingID = (StreamID)data.ReadUInt32LittleEndian(); + obj.RightSiblingID = (StreamID)data.ReadUInt32LittleEndian(); + obj.ChildID = (StreamID)data.ReadUInt32LittleEndian(); + obj.CLSID = data.ReadGuid(); + obj.StateBits = data.ReadUInt32LittleEndian(); + obj.CreationTime = data.ReadUInt64LittleEndian(); + obj.ModifiedTime = data.ReadUInt64LittleEndian(); + obj.StartingSectorLocation = data.ReadUInt32LittleEndian(); + obj.StreamSize = data.ReadUInt64LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a FileHeader + /// + /// Stream to parse + /// Filled FileHeader on success, null on error + public static FileHeader ParseFileHeader(Stream data) + { + var obj = new FileHeader(); + + obj.Signature = data.ReadUInt64LittleEndian(); + obj.CLSID = data.ReadGuid(); + obj.MinorVersion = data.ReadUInt16LittleEndian(); + obj.MajorVersion = data.ReadUInt16LittleEndian(); + obj.ByteOrder = data.ReadUInt16LittleEndian(); + obj.SectorShift = data.ReadUInt16LittleEndian(); + obj.MiniSectorShift = data.ReadUInt16LittleEndian(); + obj.Reserved = data.ReadBytes(6); + obj.NumberOfDirectorySectors = data.ReadUInt32LittleEndian(); + obj.NumberOfFATSectors = data.ReadUInt32LittleEndian(); + obj.FirstDirectorySectorLocation = data.ReadUInt32LittleEndian(); + obj.TransactionSignatureNumber = data.ReadUInt32LittleEndian(); + obj.MiniStreamCutoffSize = data.ReadUInt32LittleEndian(); + obj.FirstMiniFATSectorLocation = data.ReadUInt32LittleEndian(); + obj.NumberOfMiniFATSectors = data.ReadUInt32LittleEndian(); + obj.FirstDIFATSectorLocation = data.ReadUInt32LittleEndian(); + obj.NumberOfDIFATSectors = data.ReadUInt32LittleEndian(); + obj.DIFAT = new SectorNumber[109]; + for (int i = 0; i < 109; i++) + { + obj.DIFAT[i] = (SectorNumber)data.ReadUInt32LittleEndian(); + } // Skip rest of sector for version 4 - if (header.MajorVersion == 4) + if (obj.MajorVersion == 4) _ = data.ReadBytes(3584); - return header; + return obj; } /// @@ -294,9 +342,7 @@ namespace SabreTools.Serialization.Deserializers for (int i = 0; i < directoryEntries.Length; i++) { - var directoryEntry = data.ReadType(); - if (directoryEntry == null) - return null; + var directoryEntry = ParseDirectoryEntry(data); // Handle version 3 entries if (majorVersion == 3) diff --git a/SabreTools.Serialization/Deserializers/CHD.cs b/SabreTools.Serialization/Deserializers/CHD.cs index 9dbfd7b7..9f073da2 100644 --- a/SabreTools.Serialization/Deserializers/CHD.cs +++ b/SabreTools.Serialization/Deserializers/CHD.cs @@ -22,15 +22,70 @@ namespace SabreTools.Serialization.Deserializers uint version = GetVersion(data); // Read and return the current CHD - return version switch + switch (version) { - 1 => ParseHeaderV1(data), - 2 => ParseHeaderV2(data), - 3 => ParseHeaderV3(data), - 4 => ParseHeaderV4(data), - 5 => ParseHeaderV5(data), - _ => null, - }; + case 1: + var headerV1 = ParseHeaderV1(data); + + if (headerV1.Tag != Constants.SignatureString) + return null; + if (headerV1.Length != Constants.HeaderV1Size) + return null; + if (headerV1.Compression > CompressionType.CHDCOMPRESSION_ZLIB) + return null; + + return headerV1; + + case 2: + var headerV2 = ParseHeaderV2(data); + + if (headerV2.Tag != Constants.SignatureString) + return null; + if (headerV2.Length != Constants.HeaderV2Size) + return null; + if (headerV2.Compression > CompressionType.CHDCOMPRESSION_ZLIB) + return null; + + return headerV2; + + case 3: + var headerV3 = ParseHeaderV3(data); + + if (headerV3.Tag != Constants.SignatureString) + return null; + if (headerV3.Length != Constants.HeaderV3Size) + return null; + if (headerV3.Compression > CompressionType.CHDCOMPRESSION_ZLIB_PLUS) + return null; + + return headerV3; + + case 4: + var headerV4 = ParseHeaderV1(data); + + if (headerV4.Tag != Constants.SignatureString) + return null; + if (headerV4.Length != Constants.HeaderV4Size) + return null; + if (headerV4.Compression > CompressionType.CHDCOMPRESSION_AV) + return null; + + return headerV4; + + case 5: + var headerV5 = ParseHeaderV1(data); + + if (headerV5.Tag != Constants.SignatureString) + return null; + if (headerV5.Length != Constants.HeaderV5Size) + return null; + + return headerV5; + + default: + return null; + + } } catch { @@ -83,81 +138,129 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a V1 header + /// Parse a Stream into a HeaderV1 /// - private static HeaderV1? ParseHeaderV1(Stream data) + public static HeaderV1 ParseHeaderV1(Stream data) { - var header = data.ReadType(); - if (header?.Tag != Constants.SignatureString) - return null; - if (header.Length != Constants.HeaderV1Size) - return null; - if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB) - return null; + var obj = new HeaderV1(); - return header; + byte[] tag = data.ReadBytes(8); + obj.Tag = Encoding.ASCII.GetString(tag); + obj.Length = data.ReadUInt32LittleEndian(); + obj.Version = data.ReadUInt32LittleEndian(); + obj.Flags = (Flags)data.ReadUInt32LittleEndian(); + obj.Compression = (CompressionType)data.ReadUInt32LittleEndian(); + obj.HunkSize = data.ReadUInt32LittleEndian(); + obj.TotalHunks = data.ReadUInt32LittleEndian(); + obj.Cylinders = data.ReadUInt32LittleEndian(); + obj.Heads = data.ReadUInt32LittleEndian(); + obj.Sectors = data.ReadUInt32LittleEndian(); + obj.MD5 = data.ReadBytes(16); + obj.ParentMD5 = data.ReadBytes(16); + + return obj; } /// /// Parse a Stream into a V2 header /// - private static HeaderV2? ParseHeaderV2(Stream data) + public static HeaderV2 ParseHeaderV2(Stream data) { - var header = data.ReadType(); - if (header?.Tag != Constants.SignatureString) - return null; - if (header.Length != Constants.HeaderV2Size) - return null; - if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB) - return null; + var obj = new HeaderV2(); - return header; + byte[] tag = data.ReadBytes(8); + obj.Tag = Encoding.ASCII.GetString(tag); + obj.Length = data.ReadUInt32LittleEndian(); + obj.Version = data.ReadUInt32LittleEndian(); + obj.Flags = (Flags)data.ReadUInt32LittleEndian(); + obj.Compression = (CompressionType)data.ReadUInt32LittleEndian(); + obj.HunkSize = data.ReadUInt32LittleEndian(); + obj.TotalHunks = data.ReadUInt32LittleEndian(); + obj.Cylinders = data.ReadUInt32LittleEndian(); + obj.Heads = data.ReadUInt32LittleEndian(); + obj.Sectors = data.ReadUInt32LittleEndian(); + obj.MD5 = data.ReadBytes(16); + obj.ParentMD5 = data.ReadBytes(16); + obj.BytesPerSector = data.ReadUInt32LittleEndian(); + + return obj; } /// /// Parse a Stream into a V3 header /// - private static HeaderV3? ParseHeaderV3(Stream data) + public static HeaderV3 ParseHeaderV3(Stream data) { - var header = data.ReadType(); - if (header?.Tag != Constants.SignatureString) - return null; - if (header.Length != Constants.HeaderV3Size) - return null; - if (header.Compression > CompressionType.CHDCOMPRESSION_ZLIB_PLUS) - return null; + var obj = new HeaderV3(); - return header; + byte[] tag = data.ReadBytes(8); + obj.Tag = Encoding.ASCII.GetString(tag); + obj.Length = data.ReadUInt32LittleEndian(); + obj.Version = data.ReadUInt32LittleEndian(); + obj.Flags = (Flags)data.ReadUInt32LittleEndian(); + obj.Compression = (CompressionType)data.ReadUInt32LittleEndian(); + obj.TotalHunks = data.ReadUInt32LittleEndian(); + obj.LogicalBytes = data.ReadUInt64LittleEndian(); + obj.MetaOffset = data.ReadUInt64LittleEndian(); + obj.MD5 = data.ReadBytes(16); + obj.ParentMD5 = data.ReadBytes(16); + obj.HunkBytes = data.ReadUInt32LittleEndian(); + obj.SHA1 = data.ReadBytes(20); + obj.ParentSHA1 = data.ReadBytes(20); + + return obj; } /// /// Parse a Stream into a V4 header /// - private static HeaderV4? ParseHeaderV4(Stream data) + public static HeaderV4? ParseHeaderV4(Stream data) { - var header = data.ReadType(); - if (header?.Tag != Constants.SignatureString) - return null; - if (header.Length != Constants.HeaderV4Size) - return null; - if (header.Compression > CompressionType.CHDCOMPRESSION_AV) - return null; + var obj = new HeaderV4(); - return header; + byte[] tag = data.ReadBytes(8); + obj.Tag = Encoding.ASCII.GetString(tag); + obj.Length = data.ReadUInt32LittleEndian(); + obj.Version = data.ReadUInt32LittleEndian(); + obj.Flags = (Flags)data.ReadUInt32LittleEndian(); + obj.Compression = (CompressionType)data.ReadUInt32LittleEndian(); + obj.TotalHunks = data.ReadUInt32LittleEndian(); + obj.LogicalBytes = data.ReadUInt64LittleEndian(); + obj.MetaOffset = data.ReadUInt64LittleEndian(); + obj.HunkBytes = data.ReadUInt32LittleEndian(); + obj.SHA1 = data.ReadBytes(20); + obj.ParentSHA1 = data.ReadBytes(20); + obj.RawSHA1 = data.ReadBytes(20); + + return obj; } /// /// Parse a Stream into a V5 header /// - private static HeaderV5? ParseHeaderV5(Stream data) + public static HeaderV5 ParseHeaderV5(Stream data) { - var header = data.ReadType(); - if (header?.Tag != Constants.SignatureString) - return null; - if (header.Length != Constants.HeaderV5Size) - return null; + var obj = new HeaderV5(); - return header; + byte[] tag = data.ReadBytes(8); + obj.Tag = Encoding.ASCII.GetString(tag); + obj.Length = data.ReadUInt32LittleEndian(); + obj.Version = data.ReadUInt32LittleEndian(); + obj.Compressors = new CodecType[4]; + for (int i = 0; i < 4; i++) + { + obj.Compressors[i] = (CodecType)data.ReadUInt32LittleEndian(); + } + obj.LogicalBytes = data.ReadUInt64LittleEndian(); + obj.MapOffset = data.ReadUInt64LittleEndian(); + obj.MetaOffset = data.ReadUInt64LittleEndian(); + obj.HunkBytes = data.ReadUInt32LittleEndian(); + obj.UnitBytes = data.ReadUInt32LittleEndian(); + obj.RawSHA1 = data.ReadBytes(20); + obj.SHA1 = data.ReadBytes(20); + obj.ParentSHA1 = data.ReadBytes(20); + + return obj; } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/CIA.cs b/SabreTools.Serialization/Deserializers/CIA.cs index 3c91b6d3..e9134c44 100644 --- a/SabreTools.Serialization/Deserializers/CIA.cs +++ b/SabreTools.Serialization/Deserializers/CIA.cs @@ -22,9 +22,7 @@ namespace SabreTools.Serialization.Deserializers #region CIA Header // Try to parse the header - var header = data.ReadType(); - if (header == null) - return null; + var header = ParseCIAHeader(data); if (header.CertificateChainSize > data.Length) return null; if (header.TicketSize > data.Length) @@ -119,15 +117,7 @@ namespace SabreTools.Serialization.Deserializers // If we have a meta data if (header.MetaSize > 0) - { - // Try to parse the meta - var meta = data.ReadType(); - if (meta == null) - return null; - - // Set the meta - cia.MetaData = meta; - } + cia.MetaData = ParseMetaData(data); #endregion @@ -141,161 +131,235 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a certificate + /// Parse a Stream into a Certificate /// /// Stream to parse - /// Filled certificate on success, null on error + /// Filled Certificate on success, null on error public static Certificate? ParseCertificate(Stream data) { - var certificate = new Certificate(); + var obj = new Certificate(); - certificate.SignatureType = (SignatureType)data.ReadUInt32(); - switch (certificate.SignatureType) + obj.SignatureType = (SignatureType)data.ReadUInt32LittleEndian(); + switch (obj.SignatureType) { case SignatureType.RSA_4096_SHA1: case SignatureType.RSA_4096_SHA256: - certificate.SignatureSize = 0x200; - certificate.PaddingSize = 0x3C; + obj.SignatureSize = 0x200; + obj.PaddingSize = 0x3C; break; case SignatureType.RSA_2048_SHA1: case SignatureType.RSA_2048_SHA256: - certificate.SignatureSize = 0x100; - certificate.PaddingSize = 0x3C; + obj.SignatureSize = 0x100; + obj.PaddingSize = 0x3C; break; case SignatureType.ECDSA_SHA1: case SignatureType.ECDSA_SHA256: - certificate.SignatureSize = 0x3C; - certificate.PaddingSize = 0x40; + obj.SignatureSize = 0x3C; + obj.PaddingSize = 0x40; break; default: return null; } - certificate.Signature = data.ReadBytes(certificate.SignatureSize); - certificate.Padding = data.ReadBytes(certificate.PaddingSize); + obj.Signature = data.ReadBytes(obj.SignatureSize); + obj.Padding = data.ReadBytes(obj.PaddingSize); byte[] issuer = data.ReadBytes(0x40); - certificate.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0'); - certificate.KeyType = (PublicKeyType)data.ReadUInt32(); + obj.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0'); + obj.KeyType = (PublicKeyType)data.ReadUInt32LittleEndian(); byte[] name = data.ReadBytes(0x40); - certificate.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); - certificate.ExpirationTime = data.ReadUInt32(); + obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); + obj.ExpirationTime = data.ReadUInt32LittleEndian(); - switch (certificate.KeyType) + switch (obj.KeyType) { case PublicKeyType.RSA_4096: - certificate.RSAModulus = data.ReadBytes(0x200); - certificate.RSAPublicExponent = data.ReadUInt32(); - certificate.RSAPadding = data.ReadBytes(0x34); + obj.RSAModulus = data.ReadBytes(0x200); + obj.RSAPublicExponent = data.ReadUInt32LittleEndian(); + obj.RSAPadding = data.ReadBytes(0x34); break; case PublicKeyType.RSA_2048: - certificate.RSAModulus = data.ReadBytes(0x100); - certificate.RSAPublicExponent = data.ReadUInt32(); - certificate.RSAPadding = data.ReadBytes(0x34); + obj.RSAModulus = data.ReadBytes(0x100); + obj.RSAPublicExponent = data.ReadUInt32LittleEndian(); + obj.RSAPadding = data.ReadBytes(0x34); break; case PublicKeyType.EllipticCurve: - certificate.ECCPublicKey = data.ReadBytes(0x3C); - certificate.ECCPadding = data.ReadBytes(0x3C); + obj.ECCPublicKey = data.ReadBytes(0x3C); + obj.ECCPadding = data.ReadBytes(0x3C); break; default: return null; } - return certificate; + return obj; } /// - /// Parse a Stream into a ticket + /// Parse a Stream into a CIAHeader + /// + /// Stream to parse + /// Filled CIAHeader on success, null on error + public static CIAHeader ParseCIAHeader(Stream data) + { + var obj = new CIAHeader(); + + obj.HeaderSize = data.ReadUInt32LittleEndian(); + obj.Type = data.ReadUInt16LittleEndian(); + obj.Version = data.ReadUInt16LittleEndian(); + obj.CertificateChainSize = data.ReadUInt32LittleEndian(); + obj.TicketSize = data.ReadUInt32LittleEndian(); + obj.TMDFileSize = data.ReadUInt32LittleEndian(); + obj.MetaSize = data.ReadUInt32LittleEndian(); + obj.ContentSize = data.ReadUInt64LittleEndian(); + obj.ContentIndex = data.ReadBytes(0x2000); + + return obj; + } + + /// + /// Parse a Stream into a ContentChunkRecord + /// + /// Stream to parse + /// Filled ContentChunkRecord on success, null on error + public static ContentChunkRecord ParseContentChunkRecord(Stream data) + { + var obj = new ContentChunkRecord(); + + obj.ContentId = data.ReadUInt32LittleEndian(); + obj.ContentIndex = (ContentIndex)data.ReadUInt16LittleEndian(); + obj.ContentType = (TMDContentType)data.ReadUInt16LittleEndian(); + obj.ContentSize = data.ReadUInt64LittleEndian(); + obj.SHA256Hash = data.ReadBytes(0x20); + + return obj; + } + + /// + /// Parse a Stream into a ContentInfoRecord + /// + /// Stream to parse + /// Filled ContentInfoRecord on success, null on error + public static ContentInfoRecord ParseContentInfoRecord(Stream data) + { + var obj = new ContentInfoRecord(); + + obj.ContentIndexOffset = data.ReadUInt16LittleEndian(); + obj.ContentCommandCount = data.ReadUInt16LittleEndian(); + obj.UnhashedContentRecordsSHA256Hash = data.ReadBytes(0x20); + + return obj; + } + + /// + /// Parse a Stream into a MetaData + /// + /// Stream to parse + /// Filled MetaData on success, null on error + public static MetaData ParseMetaData(Stream data) + { + var obj = new MetaData(); + + obj.TitleIDDependencyList = data.ReadBytes(0x180); + obj.Reserved1 = data.ReadBytes(0x180); + obj.CoreVersion = data.ReadUInt32LittleEndian(); + obj.Reserved2 = data.ReadBytes(0xFC); + obj.IconData = data.ReadBytes(0x36C0); + + return obj; + } + + /// + /// Parse a Stream into a Ticket /// /// Stream to parse /// Indicates if the ticket is from CDN - /// Filled ticket on success, null on error + /// Filled Ticket on success, null on error public static Ticket? ParseTicket(Stream data, bool fromCdn = false) { - var ticket = new Ticket(); + var obj = new Ticket(); - ticket.SignatureType = (SignatureType)data.ReadUInt32(); - switch (ticket.SignatureType) + obj.SignatureType = (SignatureType)data.ReadUInt32LittleEndian(); + switch (obj.SignatureType) { case SignatureType.RSA_4096_SHA1: case SignatureType.RSA_4096_SHA256: - ticket.SignatureSize = 0x200; - ticket.PaddingSize = 0x3C; + obj.SignatureSize = 0x200; + obj.PaddingSize = 0x3C; break; case SignatureType.RSA_2048_SHA1: case SignatureType.RSA_2048_SHA256: - ticket.SignatureSize = 0x100; - ticket.PaddingSize = 0x3C; + obj.SignatureSize = 0x100; + obj.PaddingSize = 0x3C; break; case SignatureType.ECDSA_SHA1: case SignatureType.ECDSA_SHA256: - ticket.SignatureSize = 0x3C; - ticket.PaddingSize = 0x40; + obj.SignatureSize = 0x3C; + obj.PaddingSize = 0x40; break; default: return null; } - ticket.Signature = data.ReadBytes(ticket.SignatureSize); - ticket.Padding = data.ReadBytes(ticket.PaddingSize); + obj.Signature = data.ReadBytes(obj.SignatureSize); + obj.Padding = data.ReadBytes(obj.PaddingSize); byte[] issuer = data.ReadBytes(0x40); - ticket.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0'); - ticket.ECCPublicKey = data.ReadBytes(0x3C); - ticket.Version = data.ReadByteValue(); - ticket.CaCrlVersion = data.ReadByteValue(); - ticket.SignerCrlVersion = data.ReadByteValue(); - ticket.TitleKey = data.ReadBytes(0x10); - ticket.Reserved1 = data.ReadByteValue(); - ticket.TicketID = data.ReadUInt64(); - ticket.ConsoleID = data.ReadUInt32(); - ticket.TitleID = data.ReadUInt64(); - ticket.Reserved2 = data.ReadBytes(2); - ticket.TicketTitleVersion = data.ReadUInt16(); - ticket.Reserved3 = data.ReadBytes(8); - ticket.LicenseType = data.ReadByteValue(); - ticket.CommonKeyYIndex = data.ReadByteValue(); - ticket.Reserved4 = data.ReadBytes(0x2A); - ticket.eShopAccountID = data.ReadUInt32(); - ticket.Reserved5 = data.ReadByteValue(); - ticket.Audit = data.ReadByteValue(); - ticket.Reserved6 = data.ReadBytes(0x42); - ticket.Limits = new uint[0x10]; - for (int i = 0; i < ticket.Limits.Length; i++) + obj.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0'); + obj.ECCPublicKey = data.ReadBytes(0x3C); + obj.Version = data.ReadByteValue(); + obj.CaCrlVersion = data.ReadByteValue(); + obj.SignerCrlVersion = data.ReadByteValue(); + obj.TitleKey = data.ReadBytes(0x10); + obj.Reserved1 = data.ReadByteValue(); + obj.TicketID = data.ReadUInt64LittleEndian(); + obj.ConsoleID = data.ReadUInt32LittleEndian(); + obj.TitleID = data.ReadUInt64LittleEndian(); + obj.Reserved2 = data.ReadBytes(2); + obj.TicketTitleVersion = data.ReadUInt16LittleEndian(); + obj.Reserved3 = data.ReadBytes(8); + obj.LicenseType = data.ReadByteValue(); + obj.CommonKeyYIndex = data.ReadByteValue(); + obj.Reserved4 = data.ReadBytes(0x2A); + obj.eShopAccountID = data.ReadUInt32LittleEndian(); + obj.Reserved5 = data.ReadByteValue(); + obj.Audit = data.ReadByteValue(); + obj.Reserved6 = data.ReadBytes(0x42); + obj.Limits = new uint[0x10]; + for (int i = 0; i < obj.Limits.Length; i++) { - ticket.Limits[i] = data.ReadUInt32(); + obj.Limits[i] = data.ReadUInt32LittleEndian(); } // Seek to the content index size data.Seek(4, SeekOrigin.Current); // Read the size (big-endian) - ticket.ContentIndexSize = data.ReadUInt32BigEndian(); + obj.ContentIndexSize = data.ReadUInt32BigEndian(); // Seek back to the start of the content index data.Seek(-8, SeekOrigin.Current); - ticket.ContentIndex = data.ReadBytes((int)ticket.ContentIndexSize); + obj.ContentIndex = data.ReadBytes((int)obj.ContentIndexSize); // Certificates only exist in standalone CETK files if (fromCdn) { - ticket.CertificateChain = new Certificate[2]; + obj.CertificateChain = new Certificate[2]; for (int i = 0; i < 2; i++) { var certificate = ParseCertificate(data); if (certificate == null) return null; - ticket.CertificateChain[i] = certificate; + obj.CertificateChain[i] = certificate; } } - return ticket; + return obj; } /// @@ -306,90 +370,84 @@ namespace SabreTools.Serialization.Deserializers /// Filled title metadata on success, null on error public static TitleMetadata? ParseTitleMetadata(Stream data, bool fromCdn = false) { - var titleMetadata = new TitleMetadata(); + var obj = new TitleMetadata(); - titleMetadata.SignatureType = (SignatureType)data.ReadUInt32(); - switch (titleMetadata.SignatureType) + obj.SignatureType = (SignatureType)data.ReadUInt32LittleEndian(); + switch (obj.SignatureType) { case SignatureType.RSA_4096_SHA1: case SignatureType.RSA_4096_SHA256: - titleMetadata.SignatureSize = 0x200; - titleMetadata.PaddingSize = 0x3C; + obj.SignatureSize = 0x200; + obj.PaddingSize = 0x3C; break; case SignatureType.RSA_2048_SHA1: case SignatureType.RSA_2048_SHA256: - titleMetadata.SignatureSize = 0x100; - titleMetadata.PaddingSize = 0x3C; + obj.SignatureSize = 0x100; + obj.PaddingSize = 0x3C; break; case SignatureType.ECDSA_SHA1: case SignatureType.ECDSA_SHA256: - titleMetadata.SignatureSize = 0x3C; - titleMetadata.PaddingSize = 0x40; + obj.SignatureSize = 0x3C; + obj.PaddingSize = 0x40; break; default: return null; } - titleMetadata.Signature = data.ReadBytes(titleMetadata.SignatureSize); - titleMetadata.Padding1 = data.ReadBytes(titleMetadata.PaddingSize); + obj.Signature = data.ReadBytes(obj.SignatureSize); + obj.Padding1 = data.ReadBytes(obj.PaddingSize); byte[] issuer = data.ReadBytes(0x40); - titleMetadata.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0'); - titleMetadata.Version = data.ReadByteValue(); - titleMetadata.CaCrlVersion = data.ReadByteValue(); - titleMetadata.SignerCrlVersion = data.ReadByteValue(); - titleMetadata.Reserved1 = data.ReadByteValue(); - titleMetadata.SystemVersion = data.ReadUInt64(); - titleMetadata.TitleID = data.ReadUInt64(); - titleMetadata.TitleType = data.ReadUInt32(); - titleMetadata.GroupID = data.ReadUInt16(); - titleMetadata.SaveDataSize = data.ReadUInt32(); - titleMetadata.SRLPrivateSaveDataSize = data.ReadUInt32(); - titleMetadata.Reserved2 = data.ReadBytes(4); - titleMetadata.SRLFlag = data.ReadByteValue(); - titleMetadata.Reserved3 = data.ReadBytes(0x31); - titleMetadata.AccessRights = data.ReadUInt32(); - titleMetadata.TitleVersion = data.ReadUInt16(); - titleMetadata.ContentCount = data.ReadUInt16BigEndian(); - titleMetadata.BootContent = data.ReadUInt16(); - titleMetadata.Padding2 = data.ReadBytes(2); - titleMetadata.SHA256HashContentInfoRecords = data.ReadBytes(0x20); - titleMetadata.ContentInfoRecords = new ContentInfoRecord[64]; + obj.Issuer = Encoding.ASCII.GetString(issuer).TrimEnd('\0'); + obj.Version = data.ReadByteValue(); + obj.CaCrlVersion = data.ReadByteValue(); + obj.SignerCrlVersion = data.ReadByteValue(); + obj.Reserved1 = data.ReadByteValue(); + obj.SystemVersion = data.ReadUInt64LittleEndian(); + obj.TitleID = data.ReadUInt64LittleEndian(); + obj.TitleType = data.ReadUInt32LittleEndian(); + obj.GroupID = data.ReadUInt16LittleEndian(); + obj.SaveDataSize = data.ReadUInt32LittleEndian(); + obj.SRLPrivateSaveDataSize = data.ReadUInt32LittleEndian(); + obj.Reserved2 = data.ReadBytes(4); + obj.SRLFlag = data.ReadByteValue(); + obj.Reserved3 = data.ReadBytes(0x31); + obj.AccessRights = data.ReadUInt32LittleEndian(); + obj.TitleVersion = data.ReadUInt16LittleEndian(); + obj.ContentCount = data.ReadUInt16BigEndian(); + obj.BootContent = data.ReadUInt16LittleEndian(); + obj.Padding2 = data.ReadBytes(2); + obj.SHA256HashContentInfoRecords = data.ReadBytes(0x20); + obj.ContentInfoRecords = new ContentInfoRecord[64]; for (int i = 0; i < 64; i++) { - var contentInfoRecord = data.ReadType(); - if (contentInfoRecord == null) - return null; - - titleMetadata.ContentInfoRecords[i] = contentInfoRecord; + var contentInfoRecord = ParseContentInfoRecord(data); + obj.ContentInfoRecords[i] = contentInfoRecord; } - titleMetadata.ContentChunkRecords = new ContentChunkRecord[titleMetadata.ContentCount]; - for (int i = 0; i < titleMetadata.ContentCount; i++) + obj.ContentChunkRecords = new ContentChunkRecord[obj.ContentCount]; + for (int i = 0; i < obj.ContentCount; i++) { - var contentChunkRecord = data.ReadType(); - if (contentChunkRecord == null) - return null; - - titleMetadata.ContentChunkRecords[i] = contentChunkRecord; + var contentChunkRecord = ParseContentChunkRecord(data); + obj.ContentChunkRecords[i] = contentChunkRecord; } // Certificates only exist in standalone TMD files if (fromCdn) { - titleMetadata.CertificateChain = new Certificate[2]; + obj.CertificateChain = new Certificate[2]; for (int i = 0; i < 2; i++) { var certificate = ParseCertificate(data); if (certificate == null) return null; - titleMetadata.CertificateChain[i] = certificate; + obj.CertificateChain[i] = certificate; } } - return titleMetadata; + return obj; } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/GCF.cs b/SabreTools.Serialization/Deserializers/GCF.cs index 9af84b0d..94656513 100644 --- a/SabreTools.Serialization/Deserializers/GCF.cs +++ b/SabreTools.Serialization/Deserializers/GCF.cs @@ -22,10 +22,10 @@ namespace SabreTools.Serialization.Deserializers #region Header // Try to parse the header - var header = data.ReadType
(); - if (header?.Dummy0 != 0x00000001) + var header = ParseHeader(data); + if (header.Dummy0 != 0x00000001) return null; - if (header?.MajorVersion != 0x00000001) + if (header.MajorVersion != 0x00000001) return null; if (header.MinorVersion != 3 && header.MinorVersion != 5 && header.MinorVersion != 6) return null; @@ -37,74 +37,49 @@ namespace SabreTools.Serialization.Deserializers #region Block Entry Header - // Try to parse the block entry header - var blockEntryHeader = data.ReadType(); - if (blockEntryHeader == null) - return null; - // Set the game cache block entry header - file.BlockEntryHeader = blockEntryHeader; + file.BlockEntryHeader = ParseBlockEntryHeader(data); #endregion #region Block Entries // Create the block entry array - file.BlockEntries = new BlockEntry[blockEntryHeader.BlockCount]; + file.BlockEntries = new BlockEntry[file.BlockEntryHeader.BlockCount]; // Try to parse the block entries - for (int i = 0; i < blockEntryHeader.BlockCount; i++) + for (int i = 0; i < file.BlockEntryHeader.BlockCount; i++) { - var blockEntry = data.ReadType(); - if (blockEntry == null) - return null; - - file.BlockEntries[i] = blockEntry; + file.BlockEntries[i] = ParseBlockEntry(data); } #endregion #region Fragmentation Map Header - // Try to parse the fragmentation map header - var fragmentationMapHeader = data.ReadType(); - if (fragmentationMapHeader == null) - return null; - // Set the game cache fragmentation map header - file.FragmentationMapHeader = fragmentationMapHeader; + file.FragmentationMapHeader = ParseFragmentationMapHeader(data); #endregion #region Fragmentation Maps // Create the fragmentation map array - file.FragmentationMaps = new FragmentationMap[fragmentationMapHeader.BlockCount]; + file.FragmentationMaps = new FragmentationMap[file.FragmentationMapHeader.BlockCount]; // Try to parse the fragmentation maps - for (int i = 0; i < fragmentationMapHeader.BlockCount; i++) + for (int i = 0; i < file.FragmentationMapHeader.BlockCount; i++) { - var fragmentationMap = data.ReadType(); - if (fragmentationMap == null) - return null; - - file.FragmentationMaps[i] = fragmentationMap; + file.FragmentationMaps[i] = ParseFragmentationMap(data); } #endregion #region Block Entry Map Header + // Set the game cache block entry map header if (header.MinorVersion < 6) - { - // Try to parse the block entry map header - var blockEntryMapHeader = data.ReadType(); - if (blockEntryMapHeader == null) - return null; - - // Set the game cache block entry map header - file.BlockEntryMapHeader = blockEntryMapHeader; - } + file.BlockEntryMapHeader = ParseBlockEntryMapHeader(data); #endregion @@ -118,11 +93,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the block entry maps for (int i = 0; i < file.BlockEntryMapHeader.BlockCount; i++) { - var blockEntryMap = data.ReadType(); - if (blockEntryMap == null) - return null; - - file.BlockEntryMaps[i] = blockEntryMap; + file.BlockEntryMaps[i] = ParseBlockEntryMap(data); ; } } @@ -133,42 +104,33 @@ namespace SabreTools.Serialization.Deserializers #region Directory Header - // Try to parse the directory header - var directoryHeader = data.ReadType(); - if (directoryHeader == null) - return null; - // Set the game cache directory header - file.DirectoryHeader = directoryHeader; + file.DirectoryHeader = ParseDirectoryHeader(data); #endregion #region Directory Entries // Create the directory entry array - file.DirectoryEntries = new DirectoryEntry[directoryHeader.ItemCount]; + file.DirectoryEntries = new DirectoryEntry[file.DirectoryHeader.ItemCount]; // Try to parse the directory entries - for (int i = 0; i < directoryHeader.ItemCount; i++) + for (int i = 0; i < file.DirectoryHeader.ItemCount; i++) { - var directoryEntry = data.ReadType(); - if (directoryEntry == null) - return null; - - file.DirectoryEntries[i] = directoryEntry; + file.DirectoryEntries[i] = ParseDirectoryEntry(data); } #endregion #region Directory Names - if (directoryHeader.NameSize > 0) + if (file.DirectoryHeader.NameSize > 0) { // Get the current offset for adjustment long directoryNamesStart = data.Position; // Get the ending offset - long directoryNamesEnd = data.Position + directoryHeader.NameSize; + long directoryNamesEnd = data.Position + file.DirectoryHeader.NameSize; // Create the string dictionary file.DirectoryNames = []; @@ -194,16 +156,12 @@ namespace SabreTools.Serialization.Deserializers #region Directory Info 1 Entries // Create the directory info 1 entry array - file.DirectoryInfo1Entries = new DirectoryInfo1Entry[directoryHeader.Info1Count]; + file.DirectoryInfo1Entries = new DirectoryInfo1Entry[file.DirectoryHeader.Info1Count]; // Try to parse the directory info 1 entries - for (int i = 0; i < directoryHeader.Info1Count; i++) + for (int i = 0; i < file.DirectoryHeader.Info1Count; i++) { - var directoryInfo1Entry = data.ReadType(); - if (directoryInfo1Entry == null) - return null; - - file.DirectoryInfo1Entries[i] = directoryInfo1Entry; + file.DirectoryInfo1Entries[i] = ParseDirectoryInfo1Entry(data); } #endregion @@ -211,16 +169,12 @@ namespace SabreTools.Serialization.Deserializers #region Directory Info 2 Entries // Create the directory info 2 entry array - file.DirectoryInfo2Entries = new DirectoryInfo2Entry[directoryHeader.ItemCount]; + file.DirectoryInfo2Entries = new DirectoryInfo2Entry[file.DirectoryHeader.ItemCount]; // Try to parse the directory info 2 entries - for (int i = 0; i < directoryHeader.ItemCount; i++) + for (int i = 0; i < file.DirectoryHeader.ItemCount; i++) { - var directoryInfo2Entry = data.ReadType(); - if (directoryInfo2Entry == null) - return null; - - file.DirectoryInfo2Entries[i] = directoryInfo2Entry; + file.DirectoryInfo2Entries[i] = ParseDirectoryInfo2Entry(data); } #endregion @@ -228,16 +182,12 @@ namespace SabreTools.Serialization.Deserializers #region Directory Copy Entries // Create the directory copy entry array - file.DirectoryCopyEntries = new DirectoryCopyEntry[directoryHeader.CopyCount]; + file.DirectoryCopyEntries = new DirectoryCopyEntry[file.DirectoryHeader.CopyCount]; // Try to parse the directory copy entries - for (int i = 0; i < directoryHeader.CopyCount; i++) + for (int i = 0; i < file.DirectoryHeader.CopyCount; i++) { - var directoryCopyEntry = data.ReadType(); - if (directoryCopyEntry == null) - return null; - - file.DirectoryCopyEntries[i] = directoryCopyEntry; + file.DirectoryCopyEntries[i] = ParseDirectoryCopyEntry(data); } #endregion @@ -245,32 +195,28 @@ namespace SabreTools.Serialization.Deserializers #region Directory Local Entries // Create the directory local entry array - file.DirectoryLocalEntries = new DirectoryLocalEntry[directoryHeader.LocalCount]; + file.DirectoryLocalEntries = new DirectoryLocalEntry[file.DirectoryHeader.LocalCount]; // Try to parse the directory local entries - for (int i = 0; i < directoryHeader.LocalCount; i++) + for (int i = 0; i < file.DirectoryHeader.LocalCount; i++) { - var directoryLocalEntry = data.ReadType(); - if (directoryLocalEntry == null) - return null; - - file.DirectoryLocalEntries[i] = directoryLocalEntry; + file.DirectoryLocalEntries[i] = ParseDirectoryLocalEntry(data); } #endregion // Seek to end of directory section, just in case - data.Seek(initialOffset + directoryHeader.DirectorySize, SeekOrigin.Begin); + data.Seek(initialOffset + file.DirectoryHeader.DirectorySize, SeekOrigin.Begin); #region Directory Map Header if (header.MinorVersion >= 5) { // Try to parse the directory map header - var directoryMapHeader = data.ReadType(); - if (directoryMapHeader?.Dummy0 != 0x00000001) + var directoryMapHeader = ParseDirectoryMapHeader(data); + if (directoryMapHeader.Dummy0 != 0x00000001) return null; - if (directoryMapHeader?.Dummy1 != 0x00000000) + if (directoryMapHeader.Dummy1 != 0x00000000) return null; // Set the game cache directory map header @@ -282,16 +228,12 @@ namespace SabreTools.Serialization.Deserializers #region Directory Map Entries // Create the directory map entry array - file.DirectoryMapEntries = new DirectoryMapEntry[directoryHeader.ItemCount]; + file.DirectoryMapEntries = new DirectoryMapEntry[file.DirectoryHeader.ItemCount]; // Try to parse the directory map entries - for (int i = 0; i < directoryHeader.ItemCount; i++) + for (int i = 0; i < file.DirectoryHeader.ItemCount; i++) { - var directoryMapEntry = data.ReadType(); - if (directoryMapEntry == null) - return null; - - file.DirectoryMapEntries[i] = directoryMapEntry; + file.DirectoryMapEntries[i] = ParseDirectoryMapEntry(data); } #endregion @@ -299,7 +241,7 @@ namespace SabreTools.Serialization.Deserializers #region Checksum Header // Try to parse the checksum header - var checksumHeader = data.ReadType(); + var checksumHeader = ParseChecksumHeader(data); if (checksumHeader?.Dummy0 != 0x00000001) return null; @@ -314,7 +256,7 @@ namespace SabreTools.Serialization.Deserializers #region Checksum Map Header // Try to parse the checksum map header - var checksumMapHeader = data.ReadType(); + var checksumMapHeader = ParseChecksumMapHeader(data); if (checksumMapHeader?.Dummy0 != 0x14893721) return null; if (checksumMapHeader?.Dummy1 != 0x00000001) @@ -333,11 +275,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the checksum map entries for (int i = 0; i < checksumMapHeader.ItemCount; i++) { - var checksumMapEntry = data.ReadType(); - if (checksumMapEntry == null) - return null; - - file.ChecksumMapEntries[i] = checksumMapEntry; + file.ChecksumMapEntries[i] = ParseChecksumMapEntry(data); } #endregion @@ -350,11 +288,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the checksum entries for (int i = 0; i < checksumMapHeader.ChecksumCount; i++) { - var checksumEntry = data.ReadType(); - if (checksumEntry == null) - return null; - - file.ChecksumEntries[i] = checksumEntry; + file.ChecksumEntries[i] = ParseChecksumEntry(data); } #endregion @@ -384,26 +318,348 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a Half-Life Game Cache data block header + /// Parse a Stream into a BlockEntry + /// + /// Stream to parse + /// Filled BlockEntry on success, null on error + public static BlockEntry ParseBlockEntry(Stream data) + { + var obj = new BlockEntry(); + + obj.EntryFlags = data.ReadUInt32LittleEndian(); + obj.FileDataOffset = data.ReadUInt32LittleEndian(); + obj.FileDataSize = data.ReadUInt32LittleEndian(); + obj.FirstDataBlockIndex = data.ReadUInt32LittleEndian(); + obj.NextBlockEntryIndex = data.ReadUInt32LittleEndian(); + obj.PreviousBlockEntryIndex = data.ReadUInt32LittleEndian(); + obj.DirectoryIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a BlockEntryHeader + /// + /// Stream to parse + /// Filled BlockEntryHeader on success, null on error + public static BlockEntryHeader ParseBlockEntryHeader(Stream data) + { + var obj = new BlockEntryHeader(); + + obj.BlockCount = data.ReadUInt32LittleEndian(); + obj.BlocksUsed = data.ReadUInt32LittleEndian(); + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.Dummy1 = data.ReadUInt32LittleEndian(); + obj.Dummy2 = data.ReadUInt32LittleEndian(); + obj.Dummy3 = data.ReadUInt32LittleEndian(); + obj.Dummy4 = data.ReadUInt32LittleEndian(); + obj.Checksum = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a BlockEntryMap + /// + /// Stream to parse + /// Filled BlockEntryMap on success, null on error + public static BlockEntryMap ParseBlockEntryMap(Stream data) + { + var obj = new BlockEntryMap(); + + obj.PreviousBlockEntryIndex = data.ReadUInt32LittleEndian(); + obj.NextBlockEntryIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a BlockEntryMapHeader + /// + /// Stream to parse + /// Filled BlockEntryMapHeader on success, null on error + public static BlockEntryMapHeader ParseBlockEntryMapHeader(Stream data) + { + var obj = new BlockEntryMapHeader(); + + obj.BlockCount = data.ReadUInt32LittleEndian(); + obj.FirstBlockEntryIndex = data.ReadUInt32LittleEndian(); + obj.LastBlockEntryIndex = data.ReadUInt32LittleEndian(); + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.Checksum = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a ChecksumEntry + /// + /// Stream to parse + /// Filled ChecksumEntry on success, null on error + public static ChecksumEntry ParseChecksumEntry(Stream data) + { + var obj = new ChecksumEntry(); + + obj.Checksum = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a ChecksumHeader + /// + /// Stream to parse + /// Filled ChecksumHeader on success, null on error + public static ChecksumHeader ParseChecksumHeader(Stream data) + { + var obj = new ChecksumHeader(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.ChecksumSize = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a ChecksumMapEntry + /// + /// Stream to parse + /// Filled ChecksumMapEntry on success, null on error + public static ChecksumMapEntry ParseChecksumMapEntry(Stream data) + { + var obj = new ChecksumMapEntry(); + + obj.ChecksumCount = data.ReadUInt32LittleEndian(); + obj.FirstChecksumIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a ChecksumMapHeader + /// + /// Stream to parse + /// Filled ChecksumMapHeader on success, null on error + public static ChecksumMapHeader ParseChecksumMapHeader(Stream data) + { + var obj = new ChecksumMapHeader(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.Dummy1 = data.ReadUInt32LittleEndian(); + obj.ItemCount = data.ReadUInt32LittleEndian(); + obj.ChecksumCount = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DataBlockHeader /// /// Stream to parse /// Minor version field from the header - /// Filled Half-Life Game Cache data block header on success, null on error - private static DataBlockHeader? ParseDataBlockHeader(Stream data, uint minorVersion) + /// Filled DataBlockHeader on success, null on error + public static DataBlockHeader ParseDataBlockHeader(Stream data, uint minorVersion) { - var dataBlockHeader = new DataBlockHeader(); + var obj = new DataBlockHeader(); // In version 3 the DataBlockHeader is missing the LastVersionPlayed field. if (minorVersion >= 5) - dataBlockHeader.LastVersionPlayed = data.ReadUInt32(); + obj.LastVersionPlayed = data.ReadUInt32LittleEndian(); - dataBlockHeader.BlockCount = data.ReadUInt32(); - dataBlockHeader.BlockSize = data.ReadUInt32(); - dataBlockHeader.FirstBlockOffset = data.ReadUInt32(); - dataBlockHeader.BlocksUsed = data.ReadUInt32(); - dataBlockHeader.Checksum = data.ReadUInt32(); + obj.BlockCount = data.ReadUInt32LittleEndian(); + obj.BlockSize = data.ReadUInt32LittleEndian(); + obj.FirstBlockOffset = data.ReadUInt32LittleEndian(); + obj.BlocksUsed = data.ReadUInt32LittleEndian(); + obj.Checksum = data.ReadUInt32LittleEndian(); - return dataBlockHeader; + return obj; + } + + /// + /// Parse a Stream into a DirectoryCopyEntry + /// + /// Stream to parse + /// Filled DirectoryCopyEntry on success, null on error + public static DirectoryCopyEntry ParseDirectoryCopyEntry(Stream data) + { + var obj = new DirectoryCopyEntry(); + + obj.DirectoryIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryEntry + /// + /// Stream to parse + /// Filled DirectoryEntry on success, null on error + public static DirectoryEntry ParseDirectoryEntry(Stream data) + { + var obj = new DirectoryEntry(); + + obj.NameOffset = data.ReadUInt32LittleEndian(); + obj.ItemSize = data.ReadUInt32LittleEndian(); + obj.ChecksumIndex = data.ReadUInt32LittleEndian(); + obj.DirectoryFlags = (HL_GCF_FLAG)data.ReadUInt32LittleEndian(); + obj.ParentIndex = data.ReadUInt32LittleEndian(); + obj.NextIndex = data.ReadUInt32LittleEndian(); + obj.FirstIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryHeader + /// + /// Stream to parse + /// Filled DirectoryHeader on success, null on error + public static DirectoryHeader ParseDirectoryHeader(Stream data) + { + var obj = new DirectoryHeader(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.CacheID = data.ReadUInt32LittleEndian(); + obj.LastVersionPlayed = data.ReadUInt32LittleEndian(); + obj.ItemCount = data.ReadUInt32LittleEndian(); + obj.FileCount = data.ReadUInt32LittleEndian(); + obj.Dummy1 = data.ReadUInt32LittleEndian(); + obj.DirectorySize = data.ReadUInt32LittleEndian(); + obj.NameSize = data.ReadUInt32LittleEndian(); + obj.Info1Count = data.ReadUInt32LittleEndian(); + obj.CopyCount = data.ReadUInt32LittleEndian(); + obj.LocalCount = data.ReadUInt32LittleEndian(); + obj.Dummy2 = data.ReadUInt32LittleEndian(); + obj.Dummy3 = data.ReadUInt32LittleEndian(); + obj.Checksum = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryInfo1Entry + /// + /// Stream to parse + /// Filled DirectoryInfo1Entry on success, null on error + public static DirectoryInfo1Entry ParseDirectoryInfo1Entry(Stream data) + { + var obj = new DirectoryInfo1Entry(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryInfo2Entry + /// + /// Stream to parse + /// Filled DirectoryInfo2Entry on success, null on error + public static DirectoryInfo2Entry ParseDirectoryInfo2Entry(Stream data) + { + var obj = new DirectoryInfo2Entry(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryLocalEntry + /// + /// Stream to parse + /// Filled DirectoryLocalEntry on success, null on error + public static DirectoryLocalEntry ParseDirectoryLocalEntry(Stream data) + { + var obj = new DirectoryLocalEntry(); + + obj.DirectoryIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryMapEntry + /// + /// Stream to parse + /// Filled DirectoryMapEntry on success, null on error + public static DirectoryMapEntry ParseDirectoryMapEntry(Stream data) + { + var obj = new DirectoryMapEntry(); + + obj.FirstBlockIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryMapHeader + /// + /// Stream to parse + /// Filled DirectoryMapHeader on success, null on error + public static DirectoryMapHeader ParseDirectoryMapHeader(Stream data) + { + var obj = new DirectoryMapHeader(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.Dummy1 = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a FragmentationMap + /// + /// Stream to parse + /// Filled FragmentationMap on success, null on error + public static FragmentationMap ParseFragmentationMap(Stream data) + { + var obj = new FragmentationMap(); + + obj.NextDataBlockIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a FragmentationMapHeader + /// + /// Stream to parse + /// Filled FragmentationMapHeader on success, null on error + public static FragmentationMapHeader ParseFragmentationMapHeader(Stream data) + { + var obj = new FragmentationMapHeader(); + + obj.BlockCount = data.ReadUInt32LittleEndian(); + obj.FirstUnusedEntry = data.ReadUInt32LittleEndian(); + obj.Terminator = data.ReadUInt32LittleEndian(); + obj.Checksum = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a Header + /// + /// Stream to parse + /// Filled Header on success, null on error + public static Header ParseHeader(Stream data) + { + var obj = new Header(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.MajorVersion = data.ReadUInt32LittleEndian(); + obj.MinorVersion = data.ReadUInt32LittleEndian(); + obj.CacheID = data.ReadUInt32LittleEndian(); + obj.LastVersionPlayed = data.ReadUInt32LittleEndian(); + obj.Dummy1 = data.ReadUInt32LittleEndian(); + obj.Dummy2 = data.ReadUInt32LittleEndian(); + obj.FileSize = data.ReadUInt32LittleEndian(); + obj.BlockSize = data.ReadUInt32LittleEndian(); + obj.BlockCount = data.ReadUInt32LittleEndian(); + obj.Dummy3 = data.ReadUInt32LittleEndian(); + + return obj; } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/IRD.cs b/SabreTools.Serialization/Deserializers/IRD.cs index a93afe2f..b75406a7 100644 --- a/SabreTools.Serialization/Deserializers/IRD.cs +++ b/SabreTools.Serialization/Deserializers/IRD.cs @@ -15,7 +15,7 @@ namespace SabreTools.Serialization.Deserializers try { - // Create a new IRD to fill + // Deserialize the IRD var ird = new Models.IRD.File(); ird.Magic = data.ReadBytes(4); diff --git a/SabreTools.Serialization/Deserializers/InstallShieldArchiveV3.cs b/SabreTools.Serialization/Deserializers/InstallShieldArchiveV3.cs index 63df856e..f4a721c4 100644 --- a/SabreTools.Serialization/Deserializers/InstallShieldArchiveV3.cs +++ b/SabreTools.Serialization/Deserializers/InstallShieldArchiveV3.cs @@ -22,8 +22,8 @@ namespace SabreTools.Serialization.Deserializers #region Header // Try to parse the header - var header = data.ReadType
(); - if (header?.Signature1 != Constants.HeaderSignature) + var header = ParseHeader(data); + if (header.Signature1 != Constants.HeaderSignature) return null; if (header.TocAddress >= data.Length) return null; @@ -48,11 +48,8 @@ namespace SabreTools.Serialization.Deserializers for (int i = 0; i < header.DirCount; i++) { var directory = ParseDirectory(data); - if (directory?.Name == null) - return null; - directories.Add(directory); - data.Seek(directory.ChunkSize - directory.Name.Length - 6, SeekOrigin.Current); + data.Seek(directory.ChunkSize - directory.Name!.Length - 6, SeekOrigin.Current); } // Set the directories @@ -69,12 +66,9 @@ namespace SabreTools.Serialization.Deserializers var directory = archive.Directories[i]; for (int j = 0; j < directory.FileCount; j++) { - var file = data.ReadType(); - if (file?.Name == null) - return null; - + var file = ParseFile(data); files.Add(file); - data.Seek(file.ChunkSize - file.Name.Length - 30, SeekOrigin.Current); + data.Seek(file.ChunkSize - file.Name!.Length - 30, SeekOrigin.Current); } } @@ -93,22 +87,80 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a directory + /// Parse a Stream into a Directory /// /// Stream to parse - /// Filled directory on success, null on error - public static Models.InstallShieldArchiveV3.Directory? ParseDirectory(Stream data) + /// Filled Directory on success, null on error + public static Models.InstallShieldArchiveV3.Directory ParseDirectory(Stream data) { - var directory = new Models.InstallShieldArchiveV3.Directory(); + var obj = new Models.InstallShieldArchiveV3.Directory(); - directory.FileCount = data.ReadUInt16(); - directory.ChunkSize = data.ReadUInt16(); + obj.FileCount = data.ReadUInt16(); + obj.ChunkSize = data.ReadUInt16(); ushort nameLength = data.ReadUInt16(); byte[] nameBytes = data.ReadBytes(nameLength); - directory.Name = Encoding.ASCII.GetString(nameBytes); + obj.Name = Encoding.ASCII.GetString(nameBytes); - return directory; + return obj; + } + + /// + /// Parse a Stream into a File + /// + /// Stream to parse + /// Filled File on success, null on error + public static Models.InstallShieldArchiveV3.File ParseFile(Stream data) + { + var obj = new Models.InstallShieldArchiveV3.File(); + + obj.VolumeEnd = data.ReadByteValue(); + obj.Index = data.ReadUInt16LittleEndian(); + obj.UncompressedSize = data.ReadUInt32LittleEndian(); + obj.CompressedSize = data.ReadUInt32LittleEndian(); + obj.Offset = data.ReadUInt32LittleEndian(); + obj.DateTime = data.ReadUInt32LittleEndian(); + obj.Reserved0 = data.ReadUInt32LittleEndian(); + obj.ChunkSize = data.ReadUInt16LittleEndian(); + obj.Attrib = (Attributes)data.ReadByteValue(); + obj.IsSplit = data.ReadByteValue(); + obj.Reserved1 = data.ReadByteValue(); + obj.VolumeStart = data.ReadByteValue(); + obj.Name = data.ReadPrefixedAnsiString(); + + return obj; + } + + /// + /// Parse a Stream into a Header + /// + /// Stream to parse + /// Filled Header on success, null on error + public static Header ParseHeader(Stream data) + { + var obj = new Header(); + + obj.Signature1 = data.ReadUInt32LittleEndian(); + obj.Signature2 = data.ReadUInt32LittleEndian(); + obj.Reserved0 = data.ReadUInt16LittleEndian(); + obj.IsMultivolume = data.ReadUInt16LittleEndian(); + obj.FileCount = data.ReadUInt16LittleEndian(); + obj.DateTime = data.ReadUInt32LittleEndian(); + obj.CompressedSize = data.ReadUInt32LittleEndian(); + obj.UncompressedSize = data.ReadUInt32LittleEndian(); + obj.Reserved1 = data.ReadUInt32LittleEndian(); + obj.VolumeTotal = data.ReadByteValue(); + obj.VolumeNumber = data.ReadByteValue(); + obj.Reserved2 = data.ReadByteValue(); + obj.SplitBeginAddress = data.ReadUInt32LittleEndian(); + obj.SplitEndAddress = data.ReadUInt32LittleEndian(); + obj.TocAddress = data.ReadUInt32LittleEndian(); + obj.Reserved3 = data.ReadUInt32LittleEndian(); + obj.DirCount = data.ReadUInt16LittleEndian(); + obj.Reserved4 = data.ReadUInt32LittleEndian(); + obj.Reserved5 = data.ReadUInt32LittleEndian(); + + return obj; } } } diff --git a/SabreTools.Serialization/Deserializers/InstallShieldCabinet.cs b/SabreTools.Serialization/Deserializers/InstallShieldCabinet.cs index 6c2b676d..0a658f26 100644 --- a/SabreTools.Serialization/Deserializers/InstallShieldCabinet.cs +++ b/SabreTools.Serialization/Deserializers/InstallShieldCabinet.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using SabreTools.IO.Extensions; using SabreTools.Models.InstallShieldCabinet; using static SabreTools.Models.InstallShieldCabinet.Constants; @@ -25,8 +26,8 @@ namespace SabreTools.Serialization.Deserializers #region Common Header // Try to parse the cabinet header - var commonHeader = data.ReadType(); - if (commonHeader?.Signature != SignatureString) + var commonHeader = ParseCommonHeader(data); + if (commonHeader.Signature != SignatureString) return null; // Set the cabinet header @@ -34,15 +35,13 @@ namespace SabreTools.Serialization.Deserializers #endregion + // Get the major version + int majorVersion = GetMajorVersion(commonHeader); + #region Volume Header - // Try to parse the volume header - var volumeHeader = ParseVolumeHeader(data, GetMajorVersion(commonHeader)); - if (volumeHeader == null) - return null; - // Set the volume header - cabinet.VolumeHeader = volumeHeader; + cabinet.VolumeHeader = ParseVolumeHeader(data, majorVersion); #endregion @@ -56,20 +55,15 @@ namespace SabreTools.Serialization.Deserializers // Seek to the descriptor data.Seek(descriptorOffset, SeekOrigin.Begin); - // Try to parse the descriptor - var descriptor = data.ReadType(); - if (descriptor == null) - return null; - // Set the descriptor - cabinet.Descriptor = descriptor; + cabinet.Descriptor = ParseDescriptor(data); #endregion #region File Descriptor Offsets // Get the file table offset - uint fileTableOffset = commonHeader.DescriptorOffset + descriptor.FileTableOffset; + uint fileTableOffset = commonHeader.DescriptorOffset + cabinet.Descriptor.FileTableOffset; if (fileTableOffset < 0 || fileTableOffset >= data.Length) return null; @@ -78,16 +72,16 @@ namespace SabreTools.Serialization.Deserializers // Get the number of file table items uint fileTableItems; - if (GetMajorVersion(commonHeader) <= 5) - fileTableItems = descriptor.DirectoryCount + descriptor.FileCount; + if (majorVersion <= 5) + fileTableItems = cabinet.Descriptor.DirectoryCount + cabinet.Descriptor.FileCount; else - fileTableItems = descriptor.DirectoryCount; + fileTableItems = cabinet.Descriptor.DirectoryCount; // Create and fill the file table cabinet.FileDescriptorOffsets = new uint[fileTableItems]; for (int i = 0; i < cabinet.FileDescriptorOffsets.Length; i++) { - cabinet.FileDescriptorOffsets[i] = data.ReadUInt32(); + cabinet.FileDescriptorOffsets[i] = data.ReadUInt32LittleEndian(); } #endregion @@ -95,12 +89,12 @@ namespace SabreTools.Serialization.Deserializers #region Directory Descriptors // Create and fill the directory descriptors - cabinet.DirectoryNames = new string[descriptor.DirectoryCount]; - for (int i = 0; i < descriptor.DirectoryCount; i++) + cabinet.DirectoryNames = new string[cabinet.Descriptor.DirectoryCount]; + for (int i = 0; i < cabinet.Descriptor.DirectoryCount; i++) { // Get the directory descriptor offset uint offset = descriptorOffset - + descriptor.FileTableOffset + + cabinet.Descriptor.FileTableOffset + cabinet.FileDescriptorOffsets[i]; // If we have an invalid offset @@ -111,7 +105,7 @@ namespace SabreTools.Serialization.Deserializers data.Seek(offset, SeekOrigin.Begin); // Create and add the file descriptor - string? directoryName = ParseDirectoryName(data, GetMajorVersion(commonHeader)); + string? directoryName = ParseDirectoryName(data, majorVersion); if (directoryName != null) cabinet.DirectoryNames[i] = directoryName; } @@ -121,22 +115,22 @@ namespace SabreTools.Serialization.Deserializers #region File Descriptors // Create and fill the file descriptors - cabinet.FileDescriptors = new FileDescriptor[descriptor.FileCount]; - for (int i = 0; i < descriptor.FileCount; i++) + cabinet.FileDescriptors = new FileDescriptor[cabinet.Descriptor.FileCount]; + for (int i = 0; i < cabinet.Descriptor.FileCount; i++) { // Get the file descriptor offset uint offset; - if (GetMajorVersion(commonHeader) <= 5) + if (majorVersion <= 5) { offset = descriptorOffset - + descriptor.FileTableOffset - + cabinet.FileDescriptorOffsets[descriptor.DirectoryCount + i]; + + cabinet.Descriptor.FileTableOffset + + cabinet.FileDescriptorOffsets[cabinet.Descriptor.DirectoryCount + i]; } else { offset = descriptorOffset - + descriptor.FileTableOffset - + descriptor.FileTableOffset2 + + cabinet.Descriptor.FileTableOffset + + cabinet.Descriptor.FileTableOffset2 + (uint)(i * 0x57); } @@ -148,8 +142,9 @@ namespace SabreTools.Serialization.Deserializers data.Seek(offset, SeekOrigin.Begin); // Create and add the file descriptor - FileDescriptor fileDescriptor = ParseFileDescriptor(data, GetMajorVersion(commonHeader), descriptorOffset + descriptor.FileTableOffset); - cabinet.FileDescriptors[i] = fileDescriptor; + cabinet.FileDescriptors[i] = ParseFileDescriptor(data, + majorVersion, + descriptorOffset + cabinet.Descriptor.FileTableOffset); } #endregion @@ -158,10 +153,10 @@ namespace SabreTools.Serialization.Deserializers // Create and fill the file group offsets cabinet.FileGroupOffsets = new Dictionary(); - for (int i = 0; i < (descriptor.FileGroupOffsets?.Length ?? 0); i++) + for (int i = 0; i < (cabinet.Descriptor.FileGroupOffsets?.Length ?? 0); i++) { // Get the file group offset - uint offset = descriptor.FileGroupOffsets![i]; + uint offset = cabinet.Descriptor.FileGroupOffsets![i]; if (offset == 0) continue; @@ -174,8 +169,8 @@ namespace SabreTools.Serialization.Deserializers data.Seek(offset, SeekOrigin.Begin); // Create and add the offset - OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset); - cabinet.FileGroupOffsets[descriptor.FileGroupOffsets[i]] = offsetList; + OffsetList offsetList = ParseOffsetList(data, majorVersion, descriptorOffset); + cabinet.FileGroupOffsets[offset] = offsetList; // If we have a nonzero next offset uint nextOffset = offsetList.NextOffset; @@ -188,7 +183,7 @@ namespace SabreTools.Serialization.Deserializers data.Seek(internalOffset, SeekOrigin.Begin); // Create and add the offset - offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset); + offsetList = ParseOffsetList(data, majorVersion, descriptorOffset); cabinet.FileGroupOffsets[nextOffset] = offsetList; // Set the next offset @@ -225,13 +220,8 @@ namespace SabreTools.Serialization.Deserializers /// Seek to the file group data.Seek(list.DescriptorOffset + descriptorOffset, SeekOrigin.Begin); - // Try to parse the file group - var fileGroup = ParseFileGroup(data, GetMajorVersion(commonHeader), descriptorOffset); - if (fileGroup == null) - return null; - // Add the file group - cabinet.FileGroups[fileGroupId++] = fileGroup; + cabinet.FileGroups[fileGroupId++] = ParseFileGroup(data, majorVersion, descriptorOffset); } #endregion @@ -240,10 +230,10 @@ namespace SabreTools.Serialization.Deserializers // Create and fill the component offsets cabinet.ComponentOffsets = new Dictionary(); - for (int i = 0; i < (descriptor.ComponentOffsets?.Length ?? 0); i++) + for (int i = 0; i < (cabinet.Descriptor.ComponentOffsets?.Length ?? 0); i++) { // Get the component offset - uint offset = descriptor.ComponentOffsets![i]; + uint offset = cabinet.Descriptor.ComponentOffsets![i]; if (offset == 0) continue; @@ -256,8 +246,8 @@ namespace SabreTools.Serialization.Deserializers data.Seek(offset, SeekOrigin.Begin); // Create and add the offset - OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset); - cabinet.ComponentOffsets[descriptor.ComponentOffsets[i]] = offsetList; + OffsetList offsetList = ParseOffsetList(data, majorVersion, descriptorOffset); + cabinet.ComponentOffsets[cabinet.Descriptor.ComponentOffsets[i]] = offsetList; // If we have a nonzero next offset uint nextOffset = offsetList.NextOffset; @@ -270,7 +260,7 @@ namespace SabreTools.Serialization.Deserializers data.Seek(internalOffset, SeekOrigin.Begin); // Create and add the offset - offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), descriptorOffset); + offsetList = ParseOffsetList(data, majorVersion, descriptorOffset); cabinet.ComponentOffsets[nextOffset] = offsetList; // Set the next offset @@ -307,13 +297,8 @@ namespace SabreTools.Serialization.Deserializers // Seek to the component data.Seek(list.DescriptorOffset + descriptorOffset, SeekOrigin.Begin); - // Try to parse the component - var component = ParseComponent(data, GetMajorVersion(commonHeader), descriptorOffset); - if (component == null) - return null; - // Add the component - cabinet.Components[componentId++] = component; + cabinet.Components[componentId++] = ParseComponent(data, majorVersion, descriptorOffset); } #endregion @@ -330,251 +315,132 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a volume header + /// Parse a Stream into a CommonHeader /// /// Stream to parse - /// Major version of the cabinet - /// Filled volume header on success, null on error - public static VolumeHeader ParseVolumeHeader(Stream data, int majorVersion) + /// Filled CommonHeader on success, null on error + public static CommonHeader ParseCommonHeader(Stream data) { - var volumeHeader = new VolumeHeader(); + var obj = new CommonHeader(); - // Read the descriptor based on version - if (majorVersion <= 5) - { - volumeHeader.DataOffset = data.ReadUInt32(); - _ = data.ReadBytes(0x04); // Skip 0x04 bytes, unknown data? - volumeHeader.FirstFileIndex = data.ReadUInt32(); - volumeHeader.LastFileIndex = data.ReadUInt32(); - volumeHeader.FirstFileOffset = data.ReadUInt32(); - volumeHeader.FirstFileSizeExpanded = data.ReadUInt32(); - volumeHeader.FirstFileSizeCompressed = data.ReadUInt32(); - volumeHeader.LastFileOffset = data.ReadUInt32(); - volumeHeader.LastFileSizeExpanded = data.ReadUInt32(); - volumeHeader.LastFileSizeCompressed = data.ReadUInt32(); - } - else - { - volumeHeader.DataOffset = data.ReadUInt32(); - volumeHeader.DataOffsetHigh = data.ReadUInt32(); - volumeHeader.FirstFileIndex = data.ReadUInt32(); - volumeHeader.LastFileIndex = data.ReadUInt32(); - volumeHeader.FirstFileOffset = data.ReadUInt32(); - volumeHeader.FirstFileOffsetHigh = data.ReadUInt32(); - volumeHeader.FirstFileSizeExpanded = data.ReadUInt32(); - volumeHeader.FirstFileSizeExpandedHigh = data.ReadUInt32(); - volumeHeader.FirstFileSizeCompressed = data.ReadUInt32(); - volumeHeader.FirstFileSizeCompressedHigh = data.ReadUInt32(); - volumeHeader.LastFileOffset = data.ReadUInt32(); - volumeHeader.LastFileOffsetHigh = data.ReadUInt32(); - volumeHeader.LastFileSizeExpanded = data.ReadUInt32(); - volumeHeader.LastFileSizeExpandedHigh = data.ReadUInt32(); - volumeHeader.LastFileSizeCompressed = data.ReadUInt32(); - volumeHeader.LastFileSizeCompressedHigh = data.ReadUInt32(); - } + byte[] signature = data.ReadBytes(4); + obj.Signature = Encoding.ASCII.GetString(signature); + obj.Version = data.ReadUInt32LittleEndian(); + obj.VolumeInfo = data.ReadUInt32LittleEndian(); + obj.DescriptorOffset = data.ReadUInt32LittleEndian(); + obj.DescriptorSize = data.ReadUInt32LittleEndian(); - return volumeHeader; + return obj; } /// - /// Parse a Stream into an offset list + /// Parse a Stream into a Component /// /// Stream to parse /// Major version of the cabinet /// Offset of the cabinet descriptor - /// Filled offset list on success, null on error - public static OffsetList ParseOffsetList(Stream data, int majorVersion, uint descriptorOffset) - { - var offsetList = new OffsetList(); - - offsetList.NameOffset = data.ReadUInt32(); - offsetList.DescriptorOffset = data.ReadUInt32(); - offsetList.NextOffset = data.ReadUInt32(); - - // Cache the current offset - long currentOffset = data.Position; - - // Seek to the name offset - data.Seek(offsetList.NameOffset + descriptorOffset, SeekOrigin.Begin); - - // Read the string - if (majorVersion >= 17) - offsetList.Name = data.ReadNullTerminatedUnicodeString(); - else - offsetList.Name = data.ReadNullTerminatedAnsiString(); - - // Seek back to the correct offset - data.Seek(currentOffset, SeekOrigin.Begin); - - return offsetList; - } - - /// - /// Parse a Stream into a file group - /// - /// Stream to parse - /// Major version of the cabinet - /// Offset of the cabinet descriptor - /// Filled file group on success, null on error - public static FileGroup ParseFileGroup(Stream data, int majorVersion, uint descriptorOffset) - { - var fileGroup = new FileGroup(); - - fileGroup.NameOffset = data.ReadUInt32(); - fileGroup.ExpandedSize = data.ReadUInt32(); - fileGroup.CompressedSize = data.ReadUInt32(); - fileGroup.Attributes = (FileGroupAttributes)data.ReadUInt16(); - - // TODO: Figure out what data lives in this area for V5 and below - if (majorVersion <= 5) - data.Seek(0x36, SeekOrigin.Current); - - fileGroup.FirstFile = data.ReadUInt32(); - fileGroup.LastFile = data.ReadUInt32(); - fileGroup.UnknownStringOffset = data.ReadUInt32(); - fileGroup.OperatingSystemOffset = data.ReadUInt32(); - fileGroup.LanguageOffset = data.ReadUInt32(); - fileGroup.HTTPLocationOffset = data.ReadUInt32(); - fileGroup.FTPLocationOffset = data.ReadUInt32(); - fileGroup.MiscOffset = data.ReadUInt32(); - fileGroup.TargetDirectoryOffset = data.ReadUInt32(); - fileGroup.OverwriteFlags = (FileGroupFlags)data.ReadUInt32(); - fileGroup.Reserved = new uint[4]; - for (int i = 0; i < fileGroup.Reserved.Length; i++) - { - fileGroup.Reserved[i] = data.ReadUInt32(); - } - - // Cache the current position - long currentPosition = data.Position; - - // Read the name, if possible - if (fileGroup.NameOffset != 0) - { - // Seek to the name - data.Seek(fileGroup.NameOffset + descriptorOffset, SeekOrigin.Begin); - - // Read the string - if (majorVersion >= 17) - fileGroup.Name = data.ReadNullTerminatedUnicodeString(); - else - fileGroup.Name = data.ReadNullTerminatedAnsiString(); - } - - // Seek back to the correct offset - data.Seek(currentPosition, SeekOrigin.Begin); - - return fileGroup; - } - - /// - /// Parse a Stream into a component - /// - /// Stream to parse - /// Major version of the cabinet - /// Offset of the cabinet descriptor - /// Filled component on success, null on error + /// Filled Component on success, null on error public static Component ParseComponent(Stream data, int majorVersion, uint descriptorOffset) { - var component = new Component(); + var obj = new Component(); - component.IdentifierOffset = data.ReadUInt32(); - component.DescriptorOffset = data.ReadUInt32(); - component.DisplayNameOffset = data.ReadUInt32(); - component.Status = (ComponentStatus)data.ReadUInt16(); - component.PasswordOffset = data.ReadUInt32(); - component.MiscOffset = data.ReadUInt32(); - component.ComponentIndex = data.ReadUInt16(); - component.NameOffset = data.ReadUInt32(); - component.CDRomFolderOffset = data.ReadUInt32(); - component.HTTPLocationOffset = data.ReadUInt32(); - component.FTPLocationOffset = data.ReadUInt32(); - component.Guid = new Guid[2]; - for (int i = 0; i < component.Guid.Length; i++) + obj.IdentifierOffset = data.ReadUInt32LittleEndian(); + obj.DescriptorOffset = data.ReadUInt32LittleEndian(); + obj.DisplayNameOffset = data.ReadUInt32LittleEndian(); + obj.Status = (ComponentStatus)data.ReadUInt16LittleEndian(); + obj.PasswordOffset = data.ReadUInt32LittleEndian(); + obj.MiscOffset = data.ReadUInt32LittleEndian(); + obj.ComponentIndex = data.ReadUInt16LittleEndian(); + obj.NameOffset = data.ReadUInt32LittleEndian(); + obj.CDRomFolderOffset = data.ReadUInt32LittleEndian(); + obj.HTTPLocationOffset = data.ReadUInt32LittleEndian(); + obj.FTPLocationOffset = data.ReadUInt32LittleEndian(); + obj.Guid = new Guid[2]; + for (int i = 0; i < obj.Guid.Length; i++) { - component.Guid[i] = data.ReadGuid(); + obj.Guid[i] = data.ReadGuid(); } - component.CLSIDOffset = data.ReadUInt32(); - component.Reserved2 = data.ReadBytes(28); - component.Reserved3 = data.ReadBytes(majorVersion <= 5 ? 2 : 1); - component.DependsCount = data.ReadUInt16(); - component.DependsOffset = data.ReadUInt32(); - component.FileGroupCount = data.ReadUInt16(); - component.FileGroupNamesOffset = data.ReadUInt32(); - component.X3Count = data.ReadUInt16(); - component.X3Offset = data.ReadUInt32(); - component.SubComponentsCount = data.ReadUInt16(); - component.SubComponentsOffset = data.ReadUInt32(); - component.NextComponentOffset = data.ReadUInt32(); - component.OnInstallingOffset = data.ReadUInt32(); - component.OnInstalledOffset = data.ReadUInt32(); - component.OnUninstallingOffset = data.ReadUInt32(); - component.OnUninstalledOffset = data.ReadUInt32(); + obj.CLSIDOffset = data.ReadUInt32LittleEndian(); + obj.Reserved2 = data.ReadBytes(28); + obj.Reserved3 = data.ReadBytes(majorVersion <= 5 ? 2 : 1); + obj.DependsCount = data.ReadUInt16LittleEndian(); + obj.DependsOffset = data.ReadUInt32LittleEndian(); + obj.FileGroupCount = data.ReadUInt16LittleEndian(); + obj.FileGroupNamesOffset = data.ReadUInt32LittleEndian(); + obj.X3Count = data.ReadUInt16LittleEndian(); + obj.X3Offset = data.ReadUInt32LittleEndian(); + obj.SubComponentsCount = data.ReadUInt16LittleEndian(); + obj.SubComponentsOffset = data.ReadUInt32LittleEndian(); + obj.NextComponentOffset = data.ReadUInt32LittleEndian(); + obj.OnInstallingOffset = data.ReadUInt32LittleEndian(); + obj.OnInstalledOffset = data.ReadUInt32LittleEndian(); + obj.OnUninstallingOffset = data.ReadUInt32LittleEndian(); + obj.OnUninstalledOffset = data.ReadUInt32LittleEndian(); // Cache the current position long currentPosition = data.Position; // Read the identifier, if possible - if (component.IdentifierOffset != 0) + if (obj.IdentifierOffset != 0) { // Seek to the identifier - data.Seek(component.IdentifierOffset + descriptorOffset, SeekOrigin.Begin); + data.Seek(obj.IdentifierOffset + descriptorOffset, SeekOrigin.Begin); // Read the string if (majorVersion >= 17) - component.Identifier = data.ReadNullTerminatedUnicodeString(); + obj.Identifier = data.ReadNullTerminatedUnicodeString(); else - component.Identifier = data.ReadNullTerminatedAnsiString(); + obj.Identifier = data.ReadNullTerminatedAnsiString(); } // Read the display name, if possible - if (component.DisplayNameOffset != 0) + if (obj.DisplayNameOffset != 0) { // Seek to the name - data.Seek(component.DisplayNameOffset + descriptorOffset, SeekOrigin.Begin); + data.Seek(obj.DisplayNameOffset + descriptorOffset, SeekOrigin.Begin); // Read the string if (majorVersion >= 17) - component.DisplayName = data.ReadNullTerminatedUnicodeString(); + obj.DisplayName = data.ReadNullTerminatedUnicodeString(); else - component.DisplayName = data.ReadNullTerminatedAnsiString(); + obj.DisplayName = data.ReadNullTerminatedAnsiString(); } // Read the name, if possible - if (component.NameOffset != 0) + if (obj.NameOffset != 0) { // Seek to the name - data.Seek(component.NameOffset + descriptorOffset, SeekOrigin.Begin); + data.Seek(obj.NameOffset + descriptorOffset, SeekOrigin.Begin); // Read the string if (majorVersion >= 17) - component.Name = data.ReadNullTerminatedUnicodeString(); + obj.Name = data.ReadNullTerminatedUnicodeString(); else - component.Name = data.ReadNullTerminatedAnsiString(); + obj.Name = data.ReadNullTerminatedAnsiString(); } // Read the CLSID, if possible - if (component.CLSIDOffset != 0) + if (obj.CLSIDOffset != 0) { // Seek to the CLSID - data.Seek(component.CLSIDOffset + descriptorOffset, SeekOrigin.Begin); + data.Seek(obj.CLSIDOffset + descriptorOffset, SeekOrigin.Begin); // Read the GUID - component.CLSID = data.ReadGuid(); + obj.CLSID = data.ReadGuid(); } // Read the file group names, if possible - if (component.FileGroupCount != 0 && component.FileGroupNamesOffset != 0) + if (obj.FileGroupCount != 0 && obj.FileGroupNamesOffset != 0) { // Seek to the file group table offset - data.Seek(component.FileGroupNamesOffset + descriptorOffset, SeekOrigin.Begin); + data.Seek(obj.FileGroupNamesOffset + descriptorOffset, SeekOrigin.Begin); // Read the file group names table - component.FileGroupNames = new string[component.FileGroupCount]; - for (int j = 0; j < component.FileGroupCount; j++) + obj.FileGroupNames = new string[obj.FileGroupCount]; + for (int j = 0; j < obj.FileGroupCount; j++) { // Get the name offset - uint nameOffset = data.ReadUInt32(); + uint nameOffset = data.ReadUInt32LittleEndian(); // Cache the current offset long preNameOffset = data.Position; @@ -583,9 +449,9 @@ namespace SabreTools.Serialization.Deserializers data.Seek(nameOffset + descriptorOffset, SeekOrigin.Begin); if (majorVersion >= 17) - component.FileGroupNames[j] = data.ReadNullTerminatedUnicodeString() ?? string.Empty; + obj.FileGroupNames[j] = data.ReadNullTerminatedUnicodeString() ?? string.Empty; else - component.FileGroupNames[j] = data.ReadNullTerminatedAnsiString() ?? string.Empty; + obj.FileGroupNames[j] = data.ReadNullTerminatedAnsiString() ?? string.Empty; // Seek back to the original position data.Seek(preNameOffset, SeekOrigin.Begin); @@ -595,7 +461,51 @@ namespace SabreTools.Serialization.Deserializers // Seek back to the correct offset data.Seek(currentPosition, SeekOrigin.Begin); - return component; + return obj; + } + + /// + /// Parse a Stream into a Descriptor + /// + /// Stream to parse + /// Filled Descriptor on success, null on error + public static Descriptor ParseDescriptor(Stream data) + { + var obj = new Descriptor(); + + obj.StringsOffset = data.ReadUInt32LittleEndian(); + obj.Reserved0 = data.ReadUInt32LittleEndian(); + obj.ComponentListOffset = data.ReadUInt32LittleEndian(); + obj.FileTableOffset = data.ReadUInt32LittleEndian(); + obj.Reserved1 = data.ReadUInt32LittleEndian(); + obj.FileTableSize = data.ReadUInt32LittleEndian(); + obj.FileTableSize2 = data.ReadUInt32LittleEndian(); + obj.DirectoryCount = data.ReadUInt16LittleEndian(); + obj.Reserved2 = data.ReadUInt32LittleEndian(); + obj.Reserved3 = data.ReadUInt16LittleEndian(); + obj.Reserved4 = data.ReadUInt32LittleEndian(); + obj.FileCount = data.ReadUInt32LittleEndian(); + obj.FileTableOffset2 = data.ReadUInt32LittleEndian(); + obj.ComponentTableInfoCount = data.ReadUInt16LittleEndian(); + obj.ComponentTableOffset = data.ReadUInt32LittleEndian(); + obj.Reserved5 = data.ReadUInt32LittleEndian(); + obj.Reserved6 = data.ReadUInt32LittleEndian(); + obj.FileGroupOffsets = new uint[71]; + for (int i = 0; i < 71; i++) + { + obj.FileGroupOffsets[i] = data.ReadUInt32LittleEndian(); + } + obj.ComponentOffsets = new uint[71]; + for (int i = 0; i < 71; i++) + { + obj.ComponentOffsets[i] = data.ReadUInt32LittleEndian(); + } + obj.SetupTypesOffset = data.ReadUInt32LittleEndian(); + obj.SetupTableOffset = data.ReadUInt32LittleEndian(); + obj.Reserved7 = data.ReadUInt32LittleEndian(); + obj.Reserved8 = data.ReadUInt32LittleEndian(); + + return obj; } /// @@ -614,68 +524,206 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a file descriptor + /// Parse a Stream into a FileDescriptor /// /// Stream to parse /// Major version of the cabinet /// Offset of the cabinet descriptor - /// Filled file descriptor on success, null on error + /// Filled FileDescriptor on success, null on error public static FileDescriptor ParseFileDescriptor(Stream data, int majorVersion, uint descriptorOffset) { - var fileDescriptor = new FileDescriptor(); + var obj = new FileDescriptor(); // Read the descriptor based on version if (majorVersion <= 5) { - fileDescriptor.Volume = 0xFFFF; // Set by the header index - fileDescriptor.NameOffset = data.ReadUInt32(); - fileDescriptor.DirectoryIndex = data.ReadUInt32(); - fileDescriptor.Flags = (FileFlags)data.ReadUInt16(); - fileDescriptor.ExpandedSize = data.ReadUInt32(); - fileDescriptor.CompressedSize = data.ReadUInt32(); + obj.Volume = 0xFFFF; // Set by the header index + obj.NameOffset = data.ReadUInt32LittleEndian(); + obj.DirectoryIndex = data.ReadUInt32LittleEndian(); + obj.Flags = (FileFlags)data.ReadUInt16LittleEndian(); + obj.ExpandedSize = data.ReadUInt32LittleEndian(); + obj.CompressedSize = data.ReadUInt32LittleEndian(); _ = data.ReadBytes(0x14); // Skip 0x14 bytes, unknown data? - fileDescriptor.DataOffset = data.ReadUInt32(); + obj.DataOffset = data.ReadUInt32LittleEndian(); if (majorVersion == 5) - fileDescriptor.MD5 = data.ReadBytes(0x10); + obj.MD5 = data.ReadBytes(0x10); } else { - fileDescriptor.Flags = (FileFlags)data.ReadUInt16(); - fileDescriptor.ExpandedSize = data.ReadUInt64(); - fileDescriptor.CompressedSize = data.ReadUInt64(); - fileDescriptor.DataOffset = data.ReadUInt64(); - fileDescriptor.MD5 = data.ReadBytes(0x10); + obj.Flags = (FileFlags)data.ReadUInt16LittleEndian(); + obj.ExpandedSize = data.ReadUInt64LittleEndian(); + obj.CompressedSize = data.ReadUInt64LittleEndian(); + obj.DataOffset = data.ReadUInt64LittleEndian(); + obj.MD5 = data.ReadBytes(0x10); _ = data.ReadBytes(0x10); // Skip 0x10 bytes, unknown data? - fileDescriptor.NameOffset = data.ReadUInt32(); - fileDescriptor.DirectoryIndex = data.ReadUInt16(); + obj.NameOffset = data.ReadUInt32LittleEndian(); + obj.DirectoryIndex = data.ReadUInt16LittleEndian(); _ = data.ReadBytes(0x0C); // Skip 0x0C bytes, unknown data? - fileDescriptor.LinkPrevious = data.ReadUInt32(); - fileDescriptor.LinkNext = data.ReadUInt32(); - fileDescriptor.LinkFlags = (LinkFlags)data.ReadByteValue(); - fileDescriptor.Volume = data.ReadUInt16(); + obj.LinkPrevious = data.ReadUInt32LittleEndian(); + obj.LinkNext = data.ReadUInt32LittleEndian(); + obj.LinkFlags = (LinkFlags)data.ReadByteValue(); + obj.Volume = data.ReadUInt16LittleEndian(); } // Cache the current position long currentPosition = data.Position; // Read the name, if possible - if (fileDescriptor.NameOffset != 0) + if (obj.NameOffset != 0) { // Seek to the name - data.Seek(fileDescriptor.NameOffset + descriptorOffset, SeekOrigin.Begin); + data.Seek(obj.NameOffset + descriptorOffset, SeekOrigin.Begin); // Read the string if (majorVersion >= 17) - fileDescriptor.Name = data.ReadNullTerminatedUnicodeString(); + obj.Name = data.ReadNullTerminatedUnicodeString(); else - fileDescriptor.Name = data.ReadNullTerminatedAnsiString(); + obj.Name = data.ReadNullTerminatedAnsiString(); } // Seek back to the correct offset data.Seek(currentPosition, SeekOrigin.Begin); - return fileDescriptor; + return obj; + } + + /// + /// Parse a Stream into a FileGroup + /// + /// Stream to parse + /// Major version of the cabinet + /// Offset of the cabinet descriptor + /// Filled FileGroup on success, null on error + public static FileGroup ParseFileGroup(Stream data, int majorVersion, uint descriptorOffset) + { + var obj = new FileGroup(); + + obj.NameOffset = data.ReadUInt32LittleEndian(); + obj.ExpandedSize = data.ReadUInt32LittleEndian(); + obj.CompressedSize = data.ReadUInt32LittleEndian(); + obj.Attributes = (FileGroupAttributes)data.ReadUInt16LittleEndian(); + + // TODO: Figure out what data lives in this area for V5 and below + if (majorVersion <= 5) + data.Seek(0x36, SeekOrigin.Current); + + obj.FirstFile = data.ReadUInt32LittleEndian(); + obj.LastFile = data.ReadUInt32LittleEndian(); + obj.UnknownStringOffset = data.ReadUInt32LittleEndian(); + obj.OperatingSystemOffset = data.ReadUInt32LittleEndian(); + obj.LanguageOffset = data.ReadUInt32LittleEndian(); + obj.HTTPLocationOffset = data.ReadUInt32LittleEndian(); + obj.FTPLocationOffset = data.ReadUInt32LittleEndian(); + obj.MiscOffset = data.ReadUInt32LittleEndian(); + obj.TargetDirectoryOffset = data.ReadUInt32LittleEndian(); + obj.OverwriteFlags = (FileGroupFlags)data.ReadUInt32LittleEndian(); + obj.Reserved = new uint[4]; + for (int i = 0; i < obj.Reserved.Length; i++) + { + obj.Reserved[i] = data.ReadUInt32LittleEndian(); + } + + // Cache the current position + long currentPosition = data.Position; + + // Read the name, if possible + if (obj.NameOffset != 0) + { + // Seek to the name + data.Seek(obj.NameOffset + descriptorOffset, SeekOrigin.Begin); + + // Read the string + if (majorVersion >= 17) + obj.Name = data.ReadNullTerminatedUnicodeString(); + else + obj.Name = data.ReadNullTerminatedAnsiString(); + } + + // Seek back to the correct offset + data.Seek(currentPosition, SeekOrigin.Begin); + + return obj; + } + + /// + /// Parse a Stream into an OffsetList + /// + /// Stream to parse + /// Major version of the cabinet + /// Offset of the cabinet descriptor + /// Filled OffsetList on success, null on error + public static OffsetList ParseOffsetList(Stream data, int majorVersion, uint descriptorOffset) + { + var obj = new OffsetList(); + + obj.NameOffset = data.ReadUInt32LittleEndian(); + obj.DescriptorOffset = data.ReadUInt32LittleEndian(); + obj.NextOffset = data.ReadUInt32LittleEndian(); + + // Cache the current offset + long currentOffset = data.Position; + + // Seek to the name offset + data.Seek(obj.NameOffset + descriptorOffset, SeekOrigin.Begin); + + // Read the string + if (majorVersion >= 17) + obj.Name = data.ReadNullTerminatedUnicodeString(); + else + obj.Name = data.ReadNullTerminatedAnsiString(); + + // Seek back to the correct offset + data.Seek(currentOffset, SeekOrigin.Begin); + + return obj; + } + + /// + /// Parse a Stream into a VolumeHeader + /// + /// Stream to parse + /// Major version of the cabinet + /// Filled VolumeHeader on success, null on error + public static VolumeHeader ParseVolumeHeader(Stream data, int majorVersion) + { + var obj = new VolumeHeader(); + + // Read the descriptor based on version + if (majorVersion <= 5) + { + obj.DataOffset = data.ReadUInt32LittleEndian(); + _ = data.ReadBytes(0x04); // Skip 0x04 bytes, unknown data? + obj.FirstFileIndex = data.ReadUInt32LittleEndian(); + obj.LastFileIndex = data.ReadUInt32LittleEndian(); + obj.FirstFileOffset = data.ReadUInt32LittleEndian(); + obj.FirstFileSizeExpanded = data.ReadUInt32LittleEndian(); + obj.FirstFileSizeCompressed = data.ReadUInt32LittleEndian(); + obj.LastFileOffset = data.ReadUInt32LittleEndian(); + obj.LastFileSizeExpanded = data.ReadUInt32LittleEndian(); + obj.LastFileSizeCompressed = data.ReadUInt32LittleEndian(); + } + else + { + obj.DataOffset = data.ReadUInt32LittleEndian(); + obj.DataOffsetHigh = data.ReadUInt32LittleEndian(); + obj.FirstFileIndex = data.ReadUInt32LittleEndian(); + obj.LastFileIndex = data.ReadUInt32LittleEndian(); + obj.FirstFileOffset = data.ReadUInt32LittleEndian(); + obj.FirstFileOffsetHigh = data.ReadUInt32LittleEndian(); + obj.FirstFileSizeExpanded = data.ReadUInt32LittleEndian(); + obj.FirstFileSizeExpandedHigh = data.ReadUInt32LittleEndian(); + obj.FirstFileSizeCompressed = data.ReadUInt32LittleEndian(); + obj.FirstFileSizeCompressedHigh = data.ReadUInt32LittleEndian(); + obj.LastFileOffset = data.ReadUInt32LittleEndian(); + obj.LastFileOffsetHigh = data.ReadUInt32LittleEndian(); + obj.LastFileSizeExpanded = data.ReadUInt32LittleEndian(); + obj.LastFileSizeExpandedHigh = data.ReadUInt32LittleEndian(); + obj.LastFileSizeCompressed = data.ReadUInt32LittleEndian(); + obj.LastFileSizeCompressedHigh = data.ReadUInt32LittleEndian(); + } + + return obj; } #region Helpers diff --git a/SabreTools.Serialization/Deserializers/LinearExecutable.cs b/SabreTools.Serialization/Deserializers/LinearExecutable.cs index 63cd7bcb..723551c3 100644 --- a/SabreTools.Serialization/Deserializers/LinearExecutable.cs +++ b/SabreTools.Serialization/Deserializers/LinearExecutable.cs @@ -40,8 +40,8 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the executable header data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin); - var informationBlock = data.ReadType(); - if (informationBlock?.Signature != LESignatureString && informationBlock?.Signature != LXSignatureString) + var informationBlock = ParseInformationBlock(data); + if (informationBlock.Signature != LESignatureString && informationBlock.Signature != LXSignatureString) return null; // Set the executable header @@ -64,11 +64,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the object table for (int i = 0; i < executable.ObjectTable.Length; i++) { - var entry = data.ReadType(); - if (entry == null) - return null; - - executable.ObjectTable[i] = entry; + executable.ObjectTable[i] = ParseObjectTableEntry(data); } } @@ -89,11 +85,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the object page map for (int i = 0; i < executable.ObjectPageMap.Length; i++) { - var entry = data.ReadType(); - if (entry == null) - return null; - - executable.ObjectPageMap[i] = entry; + executable.ObjectPageMap[i] = ParseObjectPageMapEntry(data); } } @@ -129,11 +121,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the resource table for (int i = 0; i < executable.ResourceTable.Length; i++) { - var entry = data.ReadType(); - if (entry == null) - return null; - - executable.ResourceTable[i] = entry; + executable.ResourceTable[i] = ParseResourceTableEntry(data); } } @@ -213,11 +201,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the module format directives table for (int i = 0; i < executable.ModuleFormatDirectivesTable.Length; i++) { - var entry = data.ReadType(); - if (entry == null) - return null; - - executable.ModuleFormatDirectivesTable[i] = entry; + executable.ModuleFormatDirectivesTable[i] = ParseModuleFormatDirectivesTableEntry(data); } } @@ -246,11 +230,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the fix-up page table for (int i = 0; i < executable.FixupPageTable.Length; i++) { - var entry = data.ReadType(); - if (entry == null) - return null; - - executable.FixupPageTable[i] = entry; + executable.FixupPageTable[i] = ParseFixupPageTableEntry(data); } } @@ -296,11 +276,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the imported module name table for (int i = 0; i < executable.ImportModuleNameTable.Length; i++) { - var entry = ParseImportModuleNameTableEntry(data); - if (entry == null) - return null; - - executable.ImportModuleNameTable[i] = entry; + executable.ImportModuleNameTable[i] = ParseImportModuleNameTableEntry(data); } } @@ -327,9 +303,6 @@ namespace SabreTools.Serialization.Deserializers while (data.Position < offset + tableSize) { var entry = ParseImportModuleProcedureNameTableEntry(data); - if (entry == null) - return null; - importModuleProcedureNameTable.Add(entry); } @@ -354,11 +327,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the per-page checksum name table for (int i = 0; i < executable.PerPageChecksumTable.Length; i++) { - var entry = data.ReadType(); - if (entry == null) - return null; - - executable.PerPageChecksumTable[i] = entry; + executable.PerPageChecksumTable[i] = ParsePerPageChecksumTableEntry(data); } } @@ -404,7 +373,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the debug information var debugInformation = ParseDebugInformation(data, informationBlock.DebugInformationLength); - if (debugInformation == null) + if (debugInformation.Signature != DebugInformationSignatureString) return null; // Set the debug information @@ -423,276 +392,288 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a resident names table entry + /// Parse a Stream into a DebugInformation /// /// Stream to parse - /// Filled resident names table entry on success, null on error - public static ResidentNamesTableEntry ParseResidentNamesTableEntry(Stream data) + /// Total size of the debug information + /// Filled DebugInformation on success, null on error + public static DebugInformation ParseDebugInformation(Stream data, long size) { - var entry = new ResidentNamesTableEntry(); + var obj = new DebugInformation(); - entry.Length = data.ReadByteValue(); - if (entry.Length > 0) - { - byte[] name = data.ReadBytes(entry.Length); - entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); - } + byte[] signature = data.ReadBytes(3); + obj.Signature = Encoding.ASCII.GetString(signature); + obj.FormatType = (DebugFormatType)data.ReadByteValue(); + obj.DebuggerData = data.ReadBytes((int)(size - 4)); - entry.OrdinalNumber = data.ReadUInt16(); - return entry; + return obj; } /// - /// Parse a Stream into an entry table bundle + /// Parse a Stream into an EntryTableBundle /// /// Stream to parse - /// Filled entry table bundle on success, null on error + /// Filled EntryTableBundle on success, null on error public static EntryTableBundle? ParseEntryTableBundle(Stream data) { - var bundle = new EntryTableBundle(); + var obj = new EntryTableBundle(); - bundle.Entries = data.ReadByteValue(); - if (bundle.Entries == 0) - return bundle; + obj.Entries = data.ReadByteValue(); + if (obj.Entries == 0) + return obj; - bundle.BundleType = (BundleType)data.ReadByteValue(); - bundle.TableEntries = new EntryTableEntry[bundle.Entries]; - for (int i = 0; i < bundle.Entries; i++) + obj.BundleType = (BundleType)data.ReadByteValue(); + obj.TableEntries = new EntryTableEntry[obj.Entries]; + for (int i = 0; i < obj.Entries; i++) { var entry = new EntryTableEntry(); - switch (bundle.BundleType & ~BundleType.ParameterTypingInformationPresent) + switch (obj.BundleType & ~BundleType.ParameterTypingInformationPresent) { case BundleType.UnusedEntry: // Empty entry with no information break; case BundleType.SixteenBitEntry: - entry.SixteenBitObjectNumber = data.ReadUInt16(); + entry.SixteenBitObjectNumber = data.ReadUInt16LittleEndian(); entry.SixteenBitEntryFlags = (EntryFlags)data.ReadByteValue(); - entry.SixteenBitOffset = data.ReadUInt16(); + entry.SixteenBitOffset = data.ReadUInt16LittleEndian(); break; case BundleType.TwoEightySixCallGateEntry: - entry.TwoEightySixObjectNumber = data.ReadUInt16(); + entry.TwoEightySixObjectNumber = data.ReadUInt16LittleEndian(); entry.TwoEightySixEntryFlags = (EntryFlags)data.ReadByteValue(); - entry.TwoEightySixOffset = data.ReadUInt16(); - entry.TwoEightySixCallgate = data.ReadUInt16(); + entry.TwoEightySixOffset = data.ReadUInt16LittleEndian(); + entry.TwoEightySixCallgate = data.ReadUInt16LittleEndian(); break; case BundleType.ThirtyTwoBitEntry: - entry.ThirtyTwoBitObjectNumber = data.ReadUInt16(); + entry.ThirtyTwoBitObjectNumber = data.ReadUInt16LittleEndian(); entry.ThirtyTwoBitEntryFlags = (EntryFlags)data.ReadByteValue(); - entry.ThirtyTwoBitOffset = data.ReadUInt32(); + entry.ThirtyTwoBitOffset = data.ReadUInt32LittleEndian(); break; case BundleType.ForwarderEntry: - entry.ForwarderReserved = data.ReadUInt16(); + entry.ForwarderReserved = data.ReadUInt16LittleEndian(); entry.ForwarderFlags = (ForwarderFlags)data.ReadByteValue(); - entry.ForwarderModuleOrdinalNumber = data.ReadUInt16(); - entry.ProcedureNameOffset = data.ReadUInt32(); - entry.ImportOrdinalNumber = data.ReadUInt32(); + entry.ForwarderModuleOrdinalNumber = data.ReadUInt16LittleEndian(); + entry.ProcedureNameOffset = data.ReadUInt32LittleEndian(); + entry.ImportOrdinalNumber = data.ReadUInt32LittleEndian(); break; default: return null; } - bundle.TableEntries[i] = entry; + obj.TableEntries[i] = entry; } - return bundle; + return obj; } /// - /// Parse a Stream into a fix-up record table entry + /// Parse a Stream into an FixupPageTableEntry /// /// Stream to parse - /// Filled fix-up record table entry on success, null on error + /// Filled FixupPageTableEntry on success, null on error + public static FixupPageTableEntry ParseFixupPageTableEntry(Stream data) + { + var obj = new FixupPageTableEntry(); + + obj.Offset = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a FixupRecordTableEntry + /// + /// Stream to parse + /// Filled FixupRecordTableEntry on success, null on error public static FixupRecordTableEntry? ParseFixupRecordTableEntry(Stream data) { - var entry = new FixupRecordTableEntry(); + var obj = new FixupRecordTableEntry(); - entry.SourceType = (FixupRecordSourceType)data.ReadByteValue(); - entry.TargetFlags = (FixupRecordTargetFlags)data.ReadByteValue(); + obj.SourceType = (FixupRecordSourceType)data.ReadByteValue(); + obj.TargetFlags = (FixupRecordTargetFlags)data.ReadByteValue(); // Source list flag #if NET20 || NET35 - if ((entry.SourceType & FixupRecordSourceType.SourceListFlag) != 0) + if ((obj.SourceType & FixupRecordSourceType.SourceListFlag) != 0) #else - if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag)) + if (obj.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag)) #endif - entry.SourceOffsetListCount = data.ReadByteValue(); + obj.SourceOffsetListCount = data.ReadByteValue(); else - entry.SourceOffset = data.ReadUInt16(); + obj.SourceOffset = data.ReadUInt16LittleEndian(); // OBJECT / TRGOFF #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.InternalReference) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.InternalReference) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReference)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReference)) #endif { // 16-bit Object Number/Module Ordinal Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) #endif - entry.TargetObjectNumberWORD = data.ReadUInt16(); + obj.TargetObjectNumberWORD = data.ReadUInt16LittleEndian(); else - entry.TargetObjectNumberByte = data.ReadByteValue(); + obj.TargetObjectNumberByte = data.ReadByteValue(); // 16-bit Selector fixup #if NET20 || NET35 - if ((entry.SourceType & FixupRecordSourceType.SixteenBitSelectorFixup) == 0) + if ((obj.SourceType & FixupRecordSourceType.SixteenBitSelectorFixup) == 0) #else - if (!entry.SourceType.HasFlag(FixupRecordSourceType.SixteenBitSelectorFixup)) + if (!obj.SourceType.HasFlag(FixupRecordSourceType.SixteenBitSelectorFixup)) #endif { // 32-bit Target Offset Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) #endif - entry.TargetOffsetDWORD = data.ReadUInt32(); + obj.TargetOffsetDWORD = data.ReadUInt32LittleEndian(); else - entry.TargetOffsetWORD = data.ReadUInt16(); + obj.TargetOffsetWORD = data.ReadUInt16LittleEndian(); } } // MOD ORD# / IMPORT ORD / ADDITIVE #if NET20 || NET35 - else if ((entry.TargetFlags & FixupRecordTargetFlags.ImportedReferenceByOrdinal) != 0) + else if ((obj.TargetFlags & FixupRecordTargetFlags.ImportedReferenceByOrdinal) != 0) #else - else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByOrdinal)) + else if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByOrdinal)) #endif { // 16-bit Object Number/Module Ordinal Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) #endif - entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16(); + obj.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16LittleEndian(); else - entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue(); + obj.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue(); // 8-bit Ordinal Flag & 32-bit Target Offset Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.EightBitOrdinalFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.EightBitOrdinalFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.EightBitOrdinalFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.EightBitOrdinalFlag)) #endif - entry.ImportedOrdinalNumberByte = data.ReadByteValue(); + obj.ImportedOrdinalNumberByte = data.ReadByteValue(); #if NET20 || NET35 - else if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0) + else if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0) #else - else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) + else if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) #endif - entry.ImportedOrdinalNumberDWORD = data.ReadUInt32(); + obj.ImportedOrdinalNumberDWORD = data.ReadUInt32LittleEndian(); else - entry.ImportedOrdinalNumberWORD = data.ReadUInt16(); + obj.ImportedOrdinalNumberWORD = data.ReadUInt16LittleEndian(); // Additive Fixup Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) #endif { // 32-bit Additive Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) #endif - entry.AdditiveFixupValueDWORD = data.ReadUInt32(); + obj.AdditiveFixupValueDWORD = data.ReadUInt32LittleEndian(); else - entry.AdditiveFixupValueWORD = data.ReadUInt16(); + obj.AdditiveFixupValueWORD = data.ReadUInt16LittleEndian(); } } // MOD ORD# / PROCEDURE NAME OFFSET / ADDITIVE #if NET20 || NET35 - else if ((entry.TargetFlags & FixupRecordTargetFlags.ImportedReferenceByName) != 0) + else if ((obj.TargetFlags & FixupRecordTargetFlags.ImportedReferenceByName) != 0) #else - else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByName)) + else if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ImportedReferenceByName)) #endif { // 16-bit Object Number/Module Ordinal Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) #endif - entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16(); + obj.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16LittleEndian(); else - entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue(); + obj.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue(); // 32-bit Target Offset Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitTargetOffsetFlag)) #endif - entry.OffsetImportProcedureNameTableDWORD = data.ReadUInt32(); + obj.OffsetImportProcedureNameTableDWORD = data.ReadUInt32LittleEndian(); else - entry.OffsetImportProcedureNameTableWORD = data.ReadUInt16(); + obj.OffsetImportProcedureNameTableWORD = data.ReadUInt16LittleEndian(); // Additive Fixup Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) #endif { // 32-bit Additive Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) #endif - entry.AdditiveFixupValueDWORD = data.ReadUInt32(); + obj.AdditiveFixupValueDWORD = data.ReadUInt32LittleEndian(); else - entry.AdditiveFixupValueWORD = data.ReadUInt16(); + obj.AdditiveFixupValueWORD = data.ReadUInt16LittleEndian(); } } // ORD # / ADDITIVE #if NET20 || NET35 - else if ((entry.TargetFlags & FixupRecordTargetFlags.InternalReferenceViaEntryTable) != 0) + else if ((obj.TargetFlags & FixupRecordTargetFlags.InternalReferenceViaEntryTable) != 0) #else - else if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReferenceViaEntryTable)) + else if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.InternalReferenceViaEntryTable)) #endif { // 16-bit Object Number/Module Ordinal Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.SixteenBitObjectNumberModuleOrdinalFlag)) #endif - entry.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16(); + obj.OrdinalIndexImportModuleNameTableWORD = data.ReadUInt16LittleEndian(); else - entry.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue(); + obj.OrdinalIndexImportModuleNameTableByte = data.ReadByteValue(); // Additive Fixup Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.AdditiveFixupFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.AdditiveFixupFlag)) #endif { // 32-bit Additive Flag #if NET20 || NET35 - if ((entry.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0) + if ((obj.TargetFlags & FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag) != 0) #else - if (entry.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) + if (obj.TargetFlags.HasFlag(FixupRecordTargetFlags.ThirtyTwoBitAdditiveFixupFlag)) #endif - entry.AdditiveFixupValueDWORD = data.ReadUInt32(); + obj.AdditiveFixupValueDWORD = data.ReadUInt32LittleEndian(); else - entry.AdditiveFixupValueWORD = data.ReadUInt16(); + obj.AdditiveFixupValueWORD = data.ReadUInt16LittleEndian(); } } @@ -705,59 +686,135 @@ namespace SabreTools.Serialization.Deserializers #region SCROFFn #if NET20 || NET35 - if ((entry.SourceType & FixupRecordSourceType.SourceListFlag) != 0) + if ((obj.SourceType & FixupRecordSourceType.SourceListFlag) != 0) #else - if (entry.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag)) + if (obj.SourceType.HasFlag(FixupRecordSourceType.SourceListFlag)) #endif { - entry.SourceOffsetList = new ushort[entry.SourceOffsetListCount]; - for (int i = 0; i < entry.SourceOffsetList.Length; i++) + obj.SourceOffsetList = new ushort[obj.SourceOffsetListCount]; + for (int i = 0; i < obj.SourceOffsetList.Length; i++) { - entry.SourceOffsetList[i] = data.ReadUInt16(); + obj.SourceOffsetList[i] = data.ReadUInt16LittleEndian(); } } #endregion - return entry; + return obj; } /// - /// Parse a Stream into a import module name table entry + /// Parse a Stream into an ImportModuleNameTableEntry /// /// Stream to parse - /// Filled import module name table entry on success, null on error + /// Filled ImportModuleNameTableEntry on success, null on error public static ImportModuleNameTableEntry ParseImportModuleNameTableEntry(Stream data) { - var entry = new ImportModuleNameTableEntry(); + var obj = new ImportModuleNameTableEntry(); - entry.Length = data.ReadByteValue(); - if (entry.Length > 0) + obj.Length = data.ReadByteValue(); + if (obj.Length > 0) { - byte[] name = data.ReadBytes(entry.Length); - entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); + byte[] name = data.ReadBytes(obj.Length); + obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); } - return entry; + return obj; } /// - /// Parse a Stream into a import module name table entry + /// Parse a Stream into an ImportModuleProcedureNameTableEntry /// /// Stream to parse - /// Filled import module name table entry on success, null on error + /// Filled ImportModuleProcedureNameTableEntry on success, null on error public static ImportModuleProcedureNameTableEntry ParseImportModuleProcedureNameTableEntry(Stream data) { - var entry = new ImportModuleProcedureNameTableEntry(); + var obj = new ImportModuleProcedureNameTableEntry(); - entry.Length = data.ReadByteValue(); - if (entry.Length > 0) + obj.Length = data.ReadByteValue(); + if (obj.Length > 0) { - byte[] name = data.ReadBytes(entry.Length); - entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); + byte[] name = data.ReadBytes(obj.Length); + obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); } - return entry; + return obj; + } + + /// + /// Parse a Stream into a InformationBlock + /// + /// Stream to parse + /// Filled InformationBlock on success, null on error + public static InformationBlock ParseInformationBlock(Stream data) + { + var obj = new InformationBlock(); + + byte[] signature = data.ReadBytes(2); + obj.Signature = Encoding.ASCII.GetString(signature); + obj.ByteOrder = (ByteOrder)data.ReadByteValue(); + obj.WordOrder = (WordOrder)data.ReadByteValue(); + obj.ExecutableFormatLevel = data.ReadUInt32LittleEndian(); + obj.CPUType = (CPUType)data.ReadUInt16LittleEndian(); + obj.ModuleOS = (OperatingSystem)data.ReadUInt16LittleEndian(); + obj.ModuleVersion = data.ReadUInt32LittleEndian(); + obj.ModuleTypeFlags = (ModuleFlags)data.ReadUInt32LittleEndian(); + obj.ModuleNumberPages = data.ReadUInt32LittleEndian(); + obj.InitialObjectCS = data.ReadUInt32LittleEndian(); + obj.InitialEIP = data.ReadUInt32LittleEndian(); + obj.InitialObjectSS = data.ReadUInt32LittleEndian(); + obj.InitialESP = data.ReadUInt32LittleEndian(); + obj.MemoryPageSize = data.ReadUInt32LittleEndian(); + obj.BytesOnLastPage = data.ReadUInt32LittleEndian(); + obj.FixupSectionSize = data.ReadUInt32LittleEndian(); + obj.FixupSectionChecksum = data.ReadUInt32LittleEndian(); + obj.LoaderSectionSize = data.ReadUInt32LittleEndian(); + obj.LoaderSectionChecksum = data.ReadUInt32LittleEndian(); + obj.ObjectTableOffset = data.ReadUInt32LittleEndian(); + obj.ObjectTableCount = data.ReadUInt32LittleEndian(); + obj.ObjectPageMapOffset = data.ReadUInt32LittleEndian(); + obj.ObjectIterateDataMapOffset = data.ReadUInt32LittleEndian(); + obj.ResourceTableOffset = data.ReadUInt32LittleEndian(); + obj.ResourceTableCount = data.ReadUInt32LittleEndian(); + obj.ResidentNamesTableOffset = data.ReadUInt32LittleEndian(); + obj.EntryTableOffset = data.ReadUInt32LittleEndian(); + obj.ModuleDirectivesTableOffset = data.ReadUInt32LittleEndian(); + obj.ModuleDirectivesCount = data.ReadUInt32LittleEndian(); + obj.FixupPageTableOffset = data.ReadUInt32LittleEndian(); + obj.FixupRecordTableOffset = data.ReadUInt32LittleEndian(); + obj.ImportedModulesNameTableOffset = data.ReadUInt32LittleEndian(); + obj.ImportedModulesCount = data.ReadUInt32LittleEndian(); + obj.ImportProcedureNameTableOffset = data.ReadUInt32LittleEndian(); + obj.PerPageChecksumTableOffset = data.ReadUInt32LittleEndian(); + obj.DataPagesOffset = data.ReadUInt32LittleEndian(); + obj.PreloadPageCount = data.ReadUInt32LittleEndian(); + obj.NonResidentNamesTableOffset = data.ReadUInt32LittleEndian(); + obj.NonResidentNamesTableLength = data.ReadUInt32LittleEndian(); + obj.NonResidentNamesTableChecksum = data.ReadUInt32LittleEndian(); + obj.AutomaticDataObject = data.ReadUInt32LittleEndian(); + obj.DebugInformationOffset = data.ReadUInt32LittleEndian(); + obj.DebugInformationLength = data.ReadUInt32LittleEndian(); + obj.PreloadInstancePagesNumber = data.ReadUInt32LittleEndian(); + obj.DemandInstancePagesNumber = data.ReadUInt32LittleEndian(); + obj.ExtraHeapAllocation = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a ModuleFormatDirectivesTableEntry + /// + /// Stream to parse + /// Filled ModuleFormatDirectivesTableEntry on success, null on error + public static ModuleFormatDirectivesTableEntry ParseModuleFormatDirectivesTableEntry(Stream data) + { + var obj = new ModuleFormatDirectivesTableEntry(); + + obj.DirectiveNumber = (DirectiveNumber)data.ReadUInt16LittleEndian(); + obj.DirectiveDataLength = data.ReadUInt16LittleEndian(); + obj.DirectiveDataOffset = data.ReadUInt32LittleEndian(); + + return obj; } /// @@ -767,38 +824,105 @@ namespace SabreTools.Serialization.Deserializers /// Filled non-resident names table entry on success, null on error public static NonResidentNamesTableEntry ParseNonResidentNameTableEntry(Stream data) { - var entry = new NonResidentNamesTableEntry(); + var obj = new NonResidentNamesTableEntry(); - entry.Length = data.ReadByteValue(); - if (entry.Length > 0) + obj.Length = data.ReadByteValue(); + if (obj.Length > 0) { - byte[] name = data.ReadBytes(entry.Length); - entry.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); + byte[] name = data.ReadBytes(obj.Length); + obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); } - entry.OrdinalNumber = data.ReadUInt16(); - return entry; + obj.OrdinalNumber = data.ReadUInt16LittleEndian(); + + return obj; } /// - /// Parse a Stream into a debug information + /// Parse a Stream into a ObjectPageMapEntry /// /// Stream to parse - /// Total size of the debug information - /// Filled debug information on success, null on error - public static DebugInformation? ParseDebugInformation(Stream data, long size) + /// Filled ObjectPageMapEntry on success, null on error + public static ObjectPageMapEntry ParseObjectPageMapEntry(Stream data) { - var debugInformation = new DebugInformation(); + var obj = new ObjectPageMapEntry(); - byte[] signature = data.ReadBytes(3); - debugInformation.Signature = Encoding.ASCII.GetString(signature); - if (debugInformation.Signature != DebugInformationSignatureString) - return null; + obj.PageDataOffset = data.ReadUInt32LittleEndian(); + obj.DataSize = data.ReadUInt16LittleEndian(); + obj.Flags = (ObjectPageFlags)data.ReadUInt16LittleEndian(); - debugInformation.FormatType = (DebugFormatType)data.ReadByteValue(); - debugInformation.DebuggerData = data.ReadBytes((int)(size - 4)); + return obj; + } - return debugInformation; + /// + /// Parse a Stream into a ObjectTableEntry + /// + /// Stream to parse + /// Filled ObjectTableEntry on success, null on error + public static ObjectTableEntry ParseObjectTableEntry(Stream data) + { + var obj = new ObjectTableEntry(); + + obj.RelocationBaseAddress = data.ReadUInt32LittleEndian(); + obj.ObjectFlags = (ObjectFlags)data.ReadUInt16LittleEndian(); + obj.PageTableIndex = data.ReadUInt32LittleEndian(); + obj.PageTableEntries = data.ReadUInt32LittleEndian(); + obj.Reserved = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a PerPageChecksumTableEntry + /// + /// Stream to parse + /// Filled PerPageChecksumTableEntry on success, null on error + public static PerPageChecksumTableEntry ParsePerPageChecksumTableEntry(Stream data) + { + var obj = new PerPageChecksumTableEntry(); + + obj.Checksum = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a ResourceTableEntry + /// + /// Stream to parse + /// Filled ResourceTableEntry on success, null on error + public static ResourceTableEntry ParseResourceTableEntry(Stream data) + { + var obj = new ResourceTableEntry(); + + obj.TypeID = (ResourceTableEntryType)data.ReadUInt32LittleEndian(); + obj.NameID = data.ReadUInt16LittleEndian(); + obj.ResourceSize = data.ReadUInt32LittleEndian(); + obj.ObjectNumber = data.ReadUInt16LittleEndian(); + obj.Offset = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a ResidentNamesTableEntry + /// + /// Stream to parse + /// Filled ResidentNamesTableEntry on success, null on error + public static ResidentNamesTableEntry ParseResidentNamesTableEntry(Stream data) + { + var obj = new ResidentNamesTableEntry(); + + obj.Length = data.ReadByteValue(); + if (obj.Length > 0) + { + byte[] name = data.ReadBytes(obj.Length); + obj.Name = Encoding.ASCII.GetString(name).TrimEnd('\0'); + } + + obj.OrdinalNumber = data.ReadUInt16LittleEndian(); + + return obj; } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/MSDOS.cs b/SabreTools.Serialization/Deserializers/MSDOS.cs index 9f9e1e5f..c8780c35 100644 --- a/SabreTools.Serialization/Deserializers/MSDOS.cs +++ b/SabreTools.Serialization/Deserializers/MSDOS.cs @@ -27,7 +27,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the executable header var executableHeader = ParseExecutableHeader(data); - if (executableHeader == null) + if (executableHeader.Magic != SignatureString) return null; // Set the executable header @@ -44,12 +44,13 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the relocation table data.Seek(tableAddress, SeekOrigin.Begin); - var relocationTable = ParseRelocationTable(data, executableHeader.RelocationItems); - if (relocationTable == null) - return null; // Set the relocation table - executable.RelocationTable = relocationTable; + executable.RelocationTable = new RelocationEntry[executableHeader.RelocationItems]; + for (int i = 0; i < executableHeader.RelocationItems; i++) + { + executable.RelocationTable[i] = ParseRelocationEntry(data); + } #endregion @@ -64,82 +65,72 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into an MS-DOS executable header + /// Parse a Stream into an ExecutableHeader /// /// Stream to parse - /// Filled executable header on success, null on error - private static ExecutableHeader? ParseExecutableHeader(Stream data) + /// Filled ExecutableHeader on success, null on error + public static ExecutableHeader ParseExecutableHeader(Stream data) { - var header = new ExecutableHeader(); + var obj = new ExecutableHeader(); #region Standard Fields byte[] magic = data.ReadBytes(2); - header.Magic = Encoding.ASCII.GetString(magic); - if (header.Magic != SignatureString) - return null; - - header.LastPageBytes = data.ReadUInt16(); - header.Pages = data.ReadUInt16(); - header.RelocationItems = data.ReadUInt16(); - header.HeaderParagraphSize = data.ReadUInt16(); - header.MinimumExtraParagraphs = data.ReadUInt16(); - header.MaximumExtraParagraphs = data.ReadUInt16(); - header.InitialSSValue = data.ReadUInt16(); - header.InitialSPValue = data.ReadUInt16(); - header.Checksum = data.ReadUInt16(); - header.InitialIPValue = data.ReadUInt16(); - header.InitialCSValue = data.ReadUInt16(); - header.RelocationTableAddr = data.ReadUInt16(); - header.OverlayNumber = data.ReadUInt16(); + obj.Magic = Encoding.ASCII.GetString(magic); + obj.LastPageBytes = data.ReadUInt16LittleEndian(); + obj.Pages = data.ReadUInt16LittleEndian(); + obj.RelocationItems = data.ReadUInt16LittleEndian(); + obj.HeaderParagraphSize = data.ReadUInt16LittleEndian(); + obj.MinimumExtraParagraphs = data.ReadUInt16LittleEndian(); + obj.MaximumExtraParagraphs = data.ReadUInt16LittleEndian(); + obj.InitialSSValue = data.ReadUInt16LittleEndian(); + obj.InitialSPValue = data.ReadUInt16LittleEndian(); + obj.Checksum = data.ReadUInt16LittleEndian(); + obj.InitialIPValue = data.ReadUInt16LittleEndian(); + obj.InitialCSValue = data.ReadUInt16LittleEndian(); + obj.RelocationTableAddr = data.ReadUInt16LittleEndian(); + obj.OverlayNumber = data.ReadUInt16LittleEndian(); #endregion // If we don't have enough data for PE extensions if (data.Position >= data.Length || data.Length - data.Position < 36) - return header; + return obj; #region PE Extensions - header.Reserved1 = new ushort[4]; - for (int i = 0; i < header.Reserved1.Length; i++) + obj.Reserved1 = new ushort[4]; + for (int i = 0; i < obj.Reserved1.Length; i++) { - header.Reserved1[i] = data.ReadUInt16(); + obj.Reserved1[i] = data.ReadUInt16LittleEndian(); } - header.OEMIdentifier = data.ReadUInt16(); - header.OEMInformation = data.ReadUInt16(); - header.Reserved2 = new ushort[10]; - for (int i = 0; i < header.Reserved2.Length; i++) + obj.OEMIdentifier = data.ReadUInt16LittleEndian(); + obj.OEMInformation = data.ReadUInt16LittleEndian(); + obj.Reserved2 = new ushort[10]; + for (int i = 0; i < obj.Reserved2.Length; i++) { - header.Reserved2[i] = data.ReadUInt16(); + obj.Reserved2[i] = data.ReadUInt16LittleEndian(); } - header.NewExeHeaderAddr = data.ReadUInt32(); + obj.NewExeHeaderAddr = data.ReadUInt32LittleEndian(); #endregion - return header; + return obj; } /// - /// Parse a Stream into a relocation table + /// Parse a Stream into an ExecutableHeader /// /// Stream to parse - /// Number of relocation table entries to read - /// Filled relocation table on success, null on error - private static RelocationEntry[]? ParseRelocationTable(Stream data, int count) + /// Filled ExecutableHeader on success, null on error + public static RelocationEntry ParseRelocationEntry(Stream data) { - var relocationTable = new RelocationEntry[count]; + var obj = new RelocationEntry(); - for (int i = 0; i < count; i++) - { - var entry = data.ReadType(); - if (entry == null) - return null; + obj.Offset = data.ReadUInt16LittleEndian(); + obj.Segment = data.ReadUInt16LittleEndian(); - relocationTable[i] = entry; - } - - return relocationTable; + return obj; } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/MoPaQ.cs b/SabreTools.Serialization/Deserializers/MoPaQ.cs index 9a7d41d6..7e0ff2e0 100644 --- a/SabreTools.Serialization/Deserializers/MoPaQ.cs +++ b/SabreTools.Serialization/Deserializers/MoPaQ.cs @@ -25,7 +25,7 @@ namespace SabreTools.Serialization.Deserializers #region User Data // Check for User Data - uint possibleSignature = data.ReadUInt32(); + uint possibleSignature = data.ReadUInt32LittleEndian(); data.Seek(-4, SeekOrigin.Current); if (possibleSignature == UserDataSignatureUInt32) { @@ -33,7 +33,7 @@ namespace SabreTools.Serialization.Deserializers long basePtr = data.Position; // Deserialize the user data, returning null if invalid - var userData = data.ReadType(); + var userData = ParseUserData(data); if (userData?.Signature != UserDataSignatureString) return null; @@ -49,13 +49,13 @@ namespace SabreTools.Serialization.Deserializers #region Archive Header // Check for the Header - possibleSignature = data.ReadUInt32(); + possibleSignature = data.ReadUInt32LittleEndian(); data.Seek(-4, SeekOrigin.Current); if (possibleSignature == ArchiveHeaderSignatureUInt32) { // Try to parse the archive header var archiveHeader = ParseArchiveHeader(data); - if (archiveHeader == null) + if (archiveHeader.Signature != ArchiveHeaderSignatureString) return null; // Set the archive header @@ -90,10 +90,7 @@ namespace SabreTools.Serialization.Deserializers while (data.Position < hashTableEnd) { - var hashEntry = data.ReadType(); - if (hashEntry == null) - return null; - + var hashEntry = ParseHashEntry(data); hashTable.Add(hashEntry); } @@ -120,10 +117,7 @@ namespace SabreTools.Serialization.Deserializers while (data.Position < hashTableEnd) { - var hashEntry = data.ReadType(); - if (hashEntry == null) - return null; - + var hashEntry = ParseHashEntry(data); hashTable.Add(hashEntry); } @@ -149,10 +143,7 @@ namespace SabreTools.Serialization.Deserializers while (data.Position < hashTableEnd) { - var hashEntry = data.ReadType(); - if (hashEntry == null) - return null; - + var hashEntry = ParseHashEntry(data); hashTable.Add(hashEntry); } @@ -182,10 +173,7 @@ namespace SabreTools.Serialization.Deserializers while (data.Position < blockTableEnd) { - var blockEntry = data.ReadType(); - if (blockEntry == null) - return null; - + var blockEntry = ParseBlockEntry(data); blockTable.Add(blockEntry); } @@ -212,10 +200,7 @@ namespace SabreTools.Serialization.Deserializers while (data.Position < blockTableEnd) { - var blockEntry = data.ReadType(); - if (blockEntry == null) - return null; - + var blockEntry = ParseBlockEntry(data); blockTable.Add(blockEntry); } @@ -241,10 +226,7 @@ namespace SabreTools.Serialization.Deserializers while (data.Position < blockTableEnd) { - var blockEntry = data.ReadType(); - if (blockEntry == null) - return null; - + var blockEntry = ParseBlockEntry(data); blockTable.Add(blockEntry); } @@ -271,7 +253,7 @@ namespace SabreTools.Serialization.Deserializers for (int i = 0; i < (archive.BlockTable?.Length ?? 0); i++) { - short hiBlockEntry = data.ReadInt16(); + short hiBlockEntry = data.ReadInt16LittleEndian(); hiBlockTable.Add(hiBlockEntry); } @@ -295,7 +277,7 @@ namespace SabreTools.Serialization.Deserializers // Read in the BET table var betTable = ParseBetTable(data); - if (betTable != null) + if (betTable.Signature != BetTableSignatureString) return null; archive.BetTable = betTable; @@ -318,7 +300,7 @@ namespace SabreTools.Serialization.Deserializers // Read in the HET table var hetTable = ParseHetTable(data); - if (hetTable != null) + if (hetTable.Signature != HetTableSignatureString) return null; archive.HetTable = hetTable; @@ -337,150 +319,195 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a archive header + /// Parse a Stream into an ArchiveHeader /// /// Stream to parse - /// Filled archive header on success, null on error - private static ArchiveHeader? ParseArchiveHeader(Stream data) + /// Filled ArchiveHeader on success, null on error + public static ArchiveHeader ParseArchiveHeader(Stream data) { - ArchiveHeader archiveHeader = new ArchiveHeader(); + var obj = new ArchiveHeader(); // V1 - Common byte[] signature = data.ReadBytes(4); - archiveHeader.Signature = Encoding.ASCII.GetString(signature); - if (archiveHeader.Signature != ArchiveHeaderSignatureString) - return null; + obj.Signature = Encoding.ASCII.GetString(signature); - archiveHeader.HeaderSize = data.ReadUInt32(); - archiveHeader.ArchiveSize = data.ReadUInt32(); - archiveHeader.FormatVersion = (FormatVersion)data.ReadUInt16(); - archiveHeader.BlockSize = data.ReadUInt16(); - archiveHeader.HashTablePosition = data.ReadUInt32(); - archiveHeader.BlockTablePosition = data.ReadUInt32(); - archiveHeader.HashTableSize = data.ReadUInt32(); - archiveHeader.BlockTableSize = data.ReadUInt32(); + obj.HeaderSize = data.ReadUInt32LittleEndian(); + obj.ArchiveSize = data.ReadUInt32LittleEndian(); + obj.FormatVersion = (FormatVersion)data.ReadUInt16LittleEndian(); + obj.BlockSize = data.ReadUInt16LittleEndian(); + obj.HashTablePosition = data.ReadUInt32LittleEndian(); + obj.BlockTablePosition = data.ReadUInt32LittleEndian(); + obj.HashTableSize = data.ReadUInt32LittleEndian(); + obj.BlockTableSize = data.ReadUInt32LittleEndian(); // V2 - if (archiveHeader.FormatVersion >= FormatVersion.Format2) + if (obj.FormatVersion >= FormatVersion.Format2) { - archiveHeader.HiBlockTablePosition = data.ReadUInt64(); - archiveHeader.HashTablePositionHi = data.ReadUInt16(); - archiveHeader.BlockTablePositionHi = data.ReadUInt16(); + obj.HiBlockTablePosition = data.ReadUInt64LittleEndian(); + obj.HashTablePositionHi = data.ReadUInt16LittleEndian(); + obj.BlockTablePositionHi = data.ReadUInt16LittleEndian(); } // V3 - if (archiveHeader.FormatVersion >= FormatVersion.Format3) + if (obj.FormatVersion >= FormatVersion.Format3) { - archiveHeader.ArchiveSizeLong = data.ReadUInt64(); - archiveHeader.BetTablePosition = data.ReadUInt64(); - archiveHeader.HetTablePosition = data.ReadUInt64(); + obj.ArchiveSizeLong = data.ReadUInt64LittleEndian(); + obj.BetTablePosition = data.ReadUInt64LittleEndian(); + obj.HetTablePosition = data.ReadUInt64LittleEndian(); } // V4 - if (archiveHeader.FormatVersion >= FormatVersion.Format4) + if (obj.FormatVersion >= FormatVersion.Format4) { - archiveHeader.HashTableSizeLong = data.ReadUInt64(); - archiveHeader.BlockTableSizeLong = data.ReadUInt64(); - archiveHeader.HiBlockTableSize = data.ReadUInt64(); - archiveHeader.HetTableSize = data.ReadUInt64(); - archiveHeader.BetTablesize = data.ReadUInt64(); - archiveHeader.RawChunkSize = data.ReadUInt32(); + obj.HashTableSizeLong = data.ReadUInt64LittleEndian(); + obj.BlockTableSizeLong = data.ReadUInt64LittleEndian(); + obj.HiBlockTableSize = data.ReadUInt64LittleEndian(); + obj.HetTableSize = data.ReadUInt64LittleEndian(); + obj.BetTablesize = data.ReadUInt64LittleEndian(); + obj.RawChunkSize = data.ReadUInt32LittleEndian(); - archiveHeader.BlockTableMD5 = data.ReadBytes(0x10); - archiveHeader.HashTableMD5 = data.ReadBytes(0x10); - archiveHeader.HiBlockTableMD5 = data.ReadBytes(0x10); - archiveHeader.BetTableMD5 = data.ReadBytes(0x10); - archiveHeader.HetTableMD5 = data.ReadBytes(0x10); - archiveHeader.HetTableMD5 = data.ReadBytes(0x10); + obj.BlockTableMD5 = data.ReadBytes(0x10); + obj.HashTableMD5 = data.ReadBytes(0x10); + obj.HiBlockTableMD5 = data.ReadBytes(0x10); + obj.BetTableMD5 = data.ReadBytes(0x10); + obj.HetTableMD5 = data.ReadBytes(0x10); + obj.HetTableMD5 = data.ReadBytes(0x10); } - return archiveHeader; + return obj; } /// - /// Parse a Stream into a HET table + /// Parse a Stream into a BetTable /// /// Stream to parse - /// Filled HET table on success, null on error - private static HetTable? ParseHetTable(Stream data) + /// Filled BetTable on success, null on error + public static BetTable ParseBetTable(Stream data) { - var hetTable = new HetTable(); + var obj = new BetTable(); // Common Headers byte[] signature = data.ReadBytes(4); - hetTable.Signature = Encoding.ASCII.GetString(signature); - if (hetTable.Signature != HetTableSignatureString) - return null; - - hetTable.Version = data.ReadUInt32(); - hetTable.DataSize = data.ReadUInt32(); - - // HET-Specific - hetTable.TableSize = data.ReadUInt32(); - hetTable.MaxFileCount = data.ReadUInt32(); - hetTable.HashTableSize = data.ReadUInt32(); - hetTable.TotalIndexSize = data.ReadUInt32(); - hetTable.IndexSizeExtra = data.ReadUInt32(); - hetTable.IndexSize = data.ReadUInt32(); - hetTable.BlockTableSize = data.ReadUInt32(); - hetTable.HashTable = data.ReadBytes((int)hetTable.HashTableSize); - - // TODO: Populate the file indexes array - hetTable.FileIndexes = new byte[(int)hetTable.HashTableSize][]; - - return hetTable; - } - - /// - /// Parse a Stream into a BET table - /// - /// Stream to parse - /// Filled BET table on success, null on error - private static BetTable? ParseBetTable(Stream data) - { - var betTable = new BetTable(); - - // Common Headers - byte[] signature = data.ReadBytes(4); - betTable.Signature = Encoding.ASCII.GetString(signature); - if (betTable.Signature != BetTableSignatureString) - return null; - - betTable.Version = data.ReadUInt32(); - betTable.DataSize = data.ReadUInt32(); + obj.Signature = Encoding.ASCII.GetString(signature); + obj.Version = data.ReadUInt32LittleEndian(); + obj.DataSize = data.ReadUInt32LittleEndian(); // BET-Specific - betTable.TableSize = data.ReadUInt32(); - betTable.FileCount = data.ReadUInt32(); - betTable.Unknown = data.ReadUInt32(); - betTable.TableEntrySize = data.ReadUInt32(); + obj.TableSize = data.ReadUInt32LittleEndian(); + obj.FileCount = data.ReadUInt32LittleEndian(); + obj.Unknown = data.ReadUInt32LittleEndian(); + obj.TableEntrySize = data.ReadUInt32LittleEndian(); - betTable.FilePositionBitIndex = data.ReadUInt32(); - betTable.FileSizeBitIndex = data.ReadUInt32(); - betTable.CompressedSizeBitIndex = data.ReadUInt32(); - betTable.FlagIndexBitIndex = data.ReadUInt32(); - betTable.UnknownBitIndex = data.ReadUInt32(); + obj.FilePositionBitIndex = data.ReadUInt32LittleEndian(); + obj.FileSizeBitIndex = data.ReadUInt32LittleEndian(); + obj.CompressedSizeBitIndex = data.ReadUInt32LittleEndian(); + obj.FlagIndexBitIndex = data.ReadUInt32LittleEndian(); + obj.UnknownBitIndex = data.ReadUInt32LittleEndian(); - betTable.FilePositionBitCount = data.ReadUInt32(); - betTable.FileSizeBitCount = data.ReadUInt32(); - betTable.CompressedSizeBitCount = data.ReadUInt32(); - betTable.FlagIndexBitCount = data.ReadUInt32(); - betTable.UnknownBitCount = data.ReadUInt32(); + obj.FilePositionBitCount = data.ReadUInt32LittleEndian(); + obj.FileSizeBitCount = data.ReadUInt32LittleEndian(); + obj.CompressedSizeBitCount = data.ReadUInt32LittleEndian(); + obj.FlagIndexBitCount = data.ReadUInt32LittleEndian(); + obj.UnknownBitCount = data.ReadUInt32LittleEndian(); - betTable.TotalBetHashSize = data.ReadUInt32(); - betTable.BetHashSizeExtra = data.ReadUInt32(); - betTable.BetHashSize = data.ReadUInt32(); - betTable.BetHashArraySize = data.ReadUInt32(); - betTable.FlagCount = data.ReadUInt32(); + obj.TotalBetHashSize = data.ReadUInt32LittleEndian(); + obj.BetHashSizeExtra = data.ReadUInt32LittleEndian(); + obj.BetHashSize = data.ReadUInt32LittleEndian(); + obj.BetHashArraySize = data.ReadUInt32LittleEndian(); + obj.FlagCount = data.ReadUInt32LittleEndian(); - betTable.FlagsArray = new uint[betTable.FlagCount]; - byte[] flagsArray = data.ReadBytes((int)betTable.FlagCount * 4); - Buffer.BlockCopy(flagsArray, 0, betTable.FlagsArray, 0, (int)betTable.FlagCount * 4); + obj.FlagsArray = new uint[obj.FlagCount]; + byte[] flagsArray = data.ReadBytes((int)obj.FlagCount * 4); + Buffer.BlockCopy(flagsArray, 0, obj.FlagsArray, 0, (int)obj.FlagCount * 4); // TODO: Populate the file table // TODO: Populate the hash table - return betTable; + return obj; + } + + /// + /// Parse a Stream into an BlockEntry + /// + /// Stream to parse + /// Filled BlockEntry on success, null on error + public static BlockEntry ParseBlockEntry(Stream data) + { + var obj = new BlockEntry(); + + obj.FilePosition = data.ReadUInt32LittleEndian(); + obj.CompressedSize = data.ReadUInt32LittleEndian(); + obj.UncompressedSize = data.ReadUInt32LittleEndian(); + obj.Flags = (FileFlags)data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into an HashEntry + /// + /// Stream to parse + /// Filled HashEntry on success, null on error + public static HashEntry ParseHashEntry(Stream data) + { + var obj = new HashEntry(); + + obj.NameHashPartA = data.ReadUInt32LittleEndian(); + obj.NameHashPartB = data.ReadUInt32LittleEndian(); + obj.Locale = (Locale)data.ReadInt16LittleEndian(); + obj.Platform = data.ReadUInt16LittleEndian(); + obj.BlockIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a HetTable + /// + /// Stream to parse + /// Filled HetTable on success, null on error + public static HetTable ParseHetTable(Stream data) + { + var obj = new HetTable(); + + // Common Headers + byte[] signature = data.ReadBytes(4); + obj.Signature = Encoding.ASCII.GetString(signature); + obj.Version = data.ReadUInt32LittleEndian(); + obj.DataSize = data.ReadUInt32LittleEndian(); + + // HET-Specific + obj.TableSize = data.ReadUInt32LittleEndian(); + obj.MaxFileCount = data.ReadUInt32LittleEndian(); + obj.HashTableSize = data.ReadUInt32LittleEndian(); + obj.TotalIndexSize = data.ReadUInt32LittleEndian(); + obj.IndexSizeExtra = data.ReadUInt32LittleEndian(); + obj.IndexSize = data.ReadUInt32LittleEndian(); + obj.BlockTableSize = data.ReadUInt32LittleEndian(); + obj.HashTable = data.ReadBytes((int)obj.HashTableSize); + + // TODO: Populate the file indexes array + obj.FileIndexes = new byte[(int)obj.HashTableSize][]; + + return obj; + } + + /// + /// Parse a Stream into a UserData + /// + /// Stream to parse + /// Filled UserData on success, null on error + public static UserData ParseUserData(Stream data) + { + var obj = new UserData(); + + byte[] signature = data.ReadBytes(4); + obj.Signature = Encoding.ASCII.GetString(signature); + obj.UserDataSize = data.ReadUInt32LittleEndian(); + obj.HeaderOffset = data.ReadUInt32LittleEndian(); + obj.UserDataHeaderSize = data.ReadUInt32LittleEndian(); + + return obj; } #region Helpers @@ -488,7 +515,7 @@ namespace SabreTools.Serialization.Deserializers /// /// Buffer for encryption and decryption /// - private uint[] _stormBuffer = new uint[STORM_BUFFER_SIZE]; + private readonly uint[] _stormBuffer = new uint[STORM_BUFFER_SIZE]; /// /// Prepare the encryption table diff --git a/SabreTools.Serialization/Deserializers/N3DS.cs b/SabreTools.Serialization/Deserializers/N3DS.cs index 6b597906..2451cbc3 100644 --- a/SabreTools.Serialization/Deserializers/N3DS.cs +++ b/SabreTools.Serialization/Deserializers/N3DS.cs @@ -25,7 +25,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the header var header = ParseNCSDHeader(data); - if (header == null) + if (header.MagicNumber != NCSDMagicNumber) return null; // Set the cart image header @@ -35,25 +35,15 @@ namespace SabreTools.Serialization.Deserializers #region Card Info Header - // Try to parse the card info header - var cardInfoHeader = ParseCardInfoHeader(data); - if (cardInfoHeader == null) - return null; - // Set the card info header - cart.CardInfoHeader = cardInfoHeader; + cart.CardInfoHeader = ParseCardInfoHeader(data); #endregion #region Development Card Info Header - // Try to parse the development card info header - var developmentCardInfoHeader = data.ReadType(); - if (developmentCardInfoHeader == null) - return null; - // Set the development card info header - cart.DevelopmentCardInfoHeader = developmentCardInfoHeader; + cart.DevelopmentCardInfoHeader = ParseDevelopmentCardInfoHeader(data); #endregion @@ -92,11 +82,7 @@ namespace SabreTools.Serialization.Deserializers // Handle the extended header, if it exists if (partition.ExtendedHeaderSizeInBytes > 0) - { - var extendedHeader = data.ReadType(); - if (extendedHeader != null) - cart.ExtendedHeaders[i] = extendedHeader; - } + cart.ExtendedHeaders[i] = ParseNCCHExtendedHeader(data); // Handle the ExeFS, if it exists if (partition.ExeFSSizeInMediaUnits > 0) @@ -117,10 +103,10 @@ namespace SabreTools.Serialization.Deserializers long offset = partition.RomFSOffsetInMediaUnits * mediaUnitSize; data.Seek(partitionOffset + offset, SeekOrigin.Begin); - var romFsHeader = data.ReadType(); - if (romFsHeader?.MagicString != RomFSMagicNumber) + var romFsHeader = ParseRomFSHeader(data); + if (romFsHeader.MagicString != RomFSMagicNumber) continue; - if (romFsHeader?.MagicNumber != RomFSSecondMagicNumber) + if (romFsHeader.MagicNumber != RomFSSecondMagicNumber) continue; cart.RomFSHeaders[i] = romFsHeader; @@ -139,85 +125,193 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into an NCSD header + /// Parse a Stream into a AccessControlInfo /// /// Stream to parse - /// Filled NCSD header on success, null on error - public static NCSDHeader? ParseNCSDHeader(Stream data) + /// Filled AccessControlInfo on success, null on error + public static AccessControlInfo ParseAccessControlInfo(Stream data) { - var header = new NCSDHeader(); + var obj = new AccessControlInfo(); - header.RSA2048Signature = data.ReadBytes(0x100); - byte[] magicNumber = data.ReadBytes(4); - header.MagicNumber = Encoding.ASCII.GetString(magicNumber).TrimEnd('\0'); ; - if (header.MagicNumber != NCSDMagicNumber) - return null; + obj.ARM11LocalSystemCapabilities = ParseARM11LocalSystemCapabilities(data); + obj.ARM11KernelCapabilities = ParseARM11KernelCapabilities(data); + obj.ARM9AccessControl = ParseARM9AccessControl(data); - header.ImageSizeInMediaUnits = data.ReadUInt32(); - header.MediaId = data.ReadBytes(8); - header.PartitionsFSType = (FilesystemType)data.ReadUInt64(); - header.PartitionsCryptType = data.ReadBytes(8); - - header.PartitionsTable = new PartitionTableEntry[8]; - for (int i = 0; i < 8; i++) - { - var partitionTableEntry = data.ReadType(); - if (partitionTableEntry == null) - return null; - - header.PartitionsTable[i] = partitionTableEntry; - } - - if (header.PartitionsFSType == FilesystemType.Normal || header.PartitionsFSType == FilesystemType.None) - { - header.ExheaderHash = data.ReadBytes(0x20); - header.AdditionalHeaderSize = data.ReadUInt32(); - header.SectorZeroOffset = data.ReadUInt32(); - header.PartitionFlags = data.ReadBytes(8); - - header.PartitionIdTable = new ulong[8]; - for (int i = 0; i < 8; i++) - { - header.PartitionIdTable[i] = data.ReadUInt64(); - } - - header.Reserved1 = data.ReadBytes(0x20); - header.Reserved2 = data.ReadBytes(0x0E); - header.FirmUpdateByte1 = data.ReadByteValue(); - header.FirmUpdateByte2 = data.ReadByteValue(); - } - else if (header.PartitionsFSType == FilesystemType.FIRM) - { - header.Unknown = data.ReadBytes(0x5E); - header.EncryptedMBR = data.ReadBytes(0x42); - } - - return header; + return obj; } /// - /// Parse a Stream into a card info header + /// Parse a Stream into a ARM9AccessControl /// /// Stream to parse - /// Filled card info header on success, null on error - public static CardInfoHeader? ParseCardInfoHeader(Stream data) + /// Filled ARM9AccessControl on success, null on error + public static ARM9AccessControl ParseARM9AccessControl(Stream data) { - var header = new CardInfoHeader(); + var obj = new ARM9AccessControl(); - header.WritableAddressMediaUnits = data.ReadUInt32(); - header.CardInfoBitmask = data.ReadUInt32(); - header.Reserved1 = data.ReadBytes(0xF8); - header.FilledSize = data.ReadUInt32(); - header.Reserved2 = data.ReadBytes(0x0C); - header.TitleVersion = data.ReadUInt16(); - header.CardRevision = data.ReadUInt16(); - header.Reserved3 = data.ReadBytes(0x0C); - header.CVerTitleID = data.ReadBytes(0x08); - header.CVerVersionNumber = data.ReadUInt16(); - header.Reserved4 = data.ReadBytes(0xCD6); - header.InitialData = ParseInitialData(data); + obj.Descriptors = new ARM9AccessControlDescriptors[15]; + for (int i = 0; i < 15; i++) + { + obj.Descriptors[i] = (ARM9AccessControlDescriptors)data.ReadByteValue(); + } + obj.DescriptorVersion = data.ReadByteValue(); - return header; + return obj; + } + + /// + /// Parse a Stream into a ARM11KernelCapabilities + /// + /// Stream to parse + /// Filled ARM11KernelCapabilities on success, null on error + public static ARM11KernelCapabilities ParseARM11KernelCapabilities(Stream data) + { + var obj = new ARM11KernelCapabilities(); + + obj.Descriptors = new uint[28]; + for (int i = 0; i < 28; i++) + { + obj.Descriptors[i] = data.ReadUInt32LittleEndian(); + } + obj.Reserved = data.ReadBytes(0x10); + + return obj; + } + + /// + /// Parse a Stream into a ARM11LocalSystemCapabilities + /// + /// Stream to parse + /// Filled ARM11LocalSystemCapabilities on success, null on error + public static ARM11LocalSystemCapabilities ParseARM11LocalSystemCapabilities(Stream data) + { + var obj = new ARM11LocalSystemCapabilities(); + + obj.ProgramID = data.ReadUInt64LittleEndian(); + obj.CoreVersion = data.ReadUInt32LittleEndian(); + obj.Flag1 = (ARM11LSCFlag1)data.ReadByteValue(); + obj.Flag2 = (ARM11LSCFlag2)data.ReadByteValue(); + obj.Flag0 = (ARM11LSCFlag0)data.ReadByteValue(); + obj.Priority = data.ReadByteValue(); + obj.ResourceLimitDescriptors = new ushort[16]; + for (int i = 0; i < 16; i++) + { + obj.ResourceLimitDescriptors[i] = data.ReadUInt16LittleEndian(); + } + obj.StorageInfo = ParseStorageInfo(data); + obj.ServiceAccessControl = new ulong[32]; + for (int i = 0; i < 32; i++) + { + obj.ServiceAccessControl[i] = data.ReadUInt64LittleEndian(); + } + obj.ExtendedServiceAccessControl = new ulong[2]; + for (int i = 0; i < 2; i++) + { + obj.ExtendedServiceAccessControl[i] = data.ReadUInt64LittleEndian(); + } + obj.Reserved = data.ReadBytes(0x0F); + obj.ResourceLimitCategory = (ResourceLimitCategory)data.ReadByteValue(); + + return obj; + } + + /// + /// Parse a Stream into a CardInfoHeader + /// + /// Stream to parse + /// Filled CardInfoHeader on success, null on error + public static CardInfoHeader ParseCardInfoHeader(Stream data) + { + var obj = new CardInfoHeader(); + + obj.WritableAddressMediaUnits = data.ReadUInt32LittleEndian(); + obj.CardInfoBitmask = data.ReadUInt32LittleEndian(); + obj.Reserved1 = data.ReadBytes(0xF8); + obj.FilledSize = data.ReadUInt32LittleEndian(); + obj.Reserved2 = data.ReadBytes(0x0C); + obj.TitleVersion = data.ReadUInt16LittleEndian(); + obj.CardRevision = data.ReadUInt16LittleEndian(); + obj.Reserved3 = data.ReadBytes(0x0C); + obj.CVerTitleID = data.ReadBytes(0x08); + obj.CVerVersionNumber = data.ReadUInt16LittleEndian(); + obj.Reserved4 = data.ReadBytes(0xCD6); + obj.InitialData = ParseInitialData(data); + + return obj; + } + + /// + /// Parse a Stream into a CodeSetInfo + /// + /// Stream to parse + /// Filled CodeSetInfo on success, null on error + public static CodeSetInfo ParseCodeSetInfo(Stream data) + { + var obj = new CodeSetInfo(); + + obj.Address = data.ReadUInt32LittleEndian(); + obj.PhysicalRegionSizeInPages = data.ReadUInt32LittleEndian(); + obj.SizeInBytes = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DevelopmentCardInfoHeader + /// + /// Stream to parse + /// Filled DevelopmentCardInfoHeader on success, null on error + public static DevelopmentCardInfoHeader ParseDevelopmentCardInfoHeader(Stream data) + { + var obj = new DevelopmentCardInfoHeader(); + + obj.CardDeviceReserved1 = data.ReadBytes(0x200); + obj.TitleKey = data.ReadBytes(0x10); + obj.CardDeviceReserved2 = data.ReadBytes(0x1BF0); + obj.TestData = ParseTestData(data); + + return obj; + } + + /// + /// Parse a Stream into a ExeFSFileHeader + /// + /// Stream to parse + /// Filled ExeFSFileHeader on success, null on error + public static ExeFSFileHeader ParseExeFSFileHeader(Stream data) + { + var obj = new ExeFSFileHeader(); + + byte[] fileName = data.ReadBytes(8); + obj.FileName = Encoding.ASCII.GetString(fileName).TrimEnd('\0'); + obj.FileOffset = data.ReadUInt32LittleEndian(); + obj.FileSize = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into an ExeFSHeader + /// + /// Stream to parse + /// Filled ExeFSHeader on success, null on error + public static ExeFSHeader ParseExeFSHeader(Stream data) + { + var obj = new ExeFSHeader(); + + obj.FileHeaders = new ExeFSFileHeader[10]; + for (int i = 0; i < 10; i++) + { + obj.FileHeaders[i] = ParseExeFSFileHeader(data); + } + obj.Reserved = data.ReadBytes(0x20); + obj.FileHashes = new byte[10][]; + for (int i = 0; i < 10; i++) + { + obj.FileHashes[i] = data.ReadBytes(0x20); + } + + return obj; } /// @@ -227,92 +321,285 @@ namespace SabreTools.Serialization.Deserializers /// Filled initial data on success, null on error public static InitialData? ParseInitialData(Stream data) { - var id = new InitialData(); + var obj = new InitialData(); - id.CardSeedKeyY = data.ReadBytes(0x10); - id.EncryptedCardSeed = data.ReadBytes(0x10); - id.CardSeedAESMAC = data.ReadBytes(0x10); - id.CardSeedNonce = data.ReadBytes(0x0C); - id.Reserved = data.ReadBytes(0xC4); - id.BackupHeader = ParseNCCHHeader(data, skipSignature: true); + obj.CardSeedKeyY = data.ReadBytes(0x10); + obj.EncryptedCardSeed = data.ReadBytes(0x10); + obj.CardSeedAESMAC = data.ReadBytes(0x10); + obj.CardSeedNonce = data.ReadBytes(0x0C); + obj.Reserved = data.ReadBytes(0xC4); + obj.BackupHeader = ParseNCCHHeader(data, skipSignature: true); - return id; + return obj; } /// - /// Parse a Stream into an NCCH header + /// Parse a Stream into a NCCHExtendedHeader + /// + /// Stream to parse + /// Filled NCCHExtendedHeader on success, null on error + public static NCCHExtendedHeader ParseNCCHExtendedHeader(Stream data) + { + var obj = new NCCHExtendedHeader(); + + obj.SCI = ParseSystemControlInfo(data); + obj.ACI = ParseAccessControlInfo(data); + obj.AccessDescSignature = data.ReadBytes(0x100); + obj.NCCHHDRPublicKey = data.ReadBytes(0x100); + obj.ACIForLimitations = ParseAccessControlInfo(data); + + return obj; + } + + /// + /// Parse a Stream into an NCCHHeader /// /// Stream to parse /// Indicates if the signature should be skipped - /// Filled NCCH header on success, null on error + /// Filled NCCHHeader on success, null on error public static NCCHHeader ParseNCCHHeader(Stream data, bool skipSignature = false) { - var header = new NCCHHeader(); + var obj = new NCCHHeader(); if (!skipSignature) - header.RSA2048Signature = data.ReadBytes(0x100); + obj.RSA2048Signature = data.ReadBytes(0x100); byte[] magicId = data.ReadBytes(4); - header.MagicID = Encoding.ASCII.GetString(magicId).TrimEnd('\0'); - header.ContentSizeInMediaUnits = data.ReadUInt32(); - header.PartitionId = data.ReadUInt64(); - header.MakerCode = data.ReadUInt16(); - header.Version = data.ReadUInt16(); - header.VerificationHash = data.ReadUInt32(); - header.ProgramId = data.ReadBytes(8); - header.Reserved1 = data.ReadBytes(0x10); - header.LogoRegionHash = data.ReadBytes(0x20); + obj.MagicID = Encoding.ASCII.GetString(magicId).TrimEnd('\0'); + obj.ContentSizeInMediaUnits = data.ReadUInt32LittleEndian(); + obj.PartitionId = data.ReadUInt64(); + obj.MakerCode = data.ReadUInt16LittleEndian(); + obj.Version = data.ReadUInt16LittleEndian(); + obj.VerificationHash = data.ReadUInt32LittleEndian(); + obj.ProgramId = data.ReadBytes(8); + obj.Reserved1 = data.ReadBytes(0x10); + obj.LogoRegionHash = data.ReadBytes(0x20); byte[] productCode = data.ReadBytes(0x10); - header.ProductCode = Encoding.ASCII.GetString(productCode).TrimEnd('\0'); - header.ExtendedHeaderHash = data.ReadBytes(0x20); - header.ExtendedHeaderSizeInBytes = data.ReadUInt32(); - header.Reserved2 = data.ReadUInt32(); - header.Flags = data.ReadType(); - header.PlainRegionOffsetInMediaUnits = data.ReadUInt32(); - header.PlainRegionSizeInMediaUnits = data.ReadUInt32(); - header.LogoRegionOffsetInMediaUnits = data.ReadUInt32(); - header.LogoRegionSizeInMediaUnits = data.ReadUInt32(); - header.ExeFSOffsetInMediaUnits = data.ReadUInt32(); - header.ExeFSSizeInMediaUnits = data.ReadUInt32(); - header.ExeFSHashRegionSizeInMediaUnits = data.ReadUInt32(); - header.Reserved3 = data.ReadUInt32(); - header.RomFSOffsetInMediaUnits = data.ReadUInt32(); - header.RomFSSizeInMediaUnits = data.ReadUInt32(); - header.RomFSHashRegionSizeInMediaUnits = data.ReadUInt32(); - header.Reserved4 = data.ReadUInt32(); - header.ExeFSSuperblockHash = data.ReadBytes(0x20); - header.RomFSSuperblockHash = data.ReadBytes(0x20); + obj.ProductCode = Encoding.ASCII.GetString(productCode).TrimEnd('\0'); + obj.ExtendedHeaderHash = data.ReadBytes(0x20); + obj.ExtendedHeaderSizeInBytes = data.ReadUInt32LittleEndian(); + obj.Reserved2 = data.ReadUInt32LittleEndian(); + obj.Flags = ParseNCCHHeaderFlags(data); + obj.PlainRegionOffsetInMediaUnits = data.ReadUInt32LittleEndian(); + obj.PlainRegionSizeInMediaUnits = data.ReadUInt32LittleEndian(); + obj.LogoRegionOffsetInMediaUnits = data.ReadUInt32LittleEndian(); + obj.LogoRegionSizeInMediaUnits = data.ReadUInt32LittleEndian(); + obj.ExeFSOffsetInMediaUnits = data.ReadUInt32LittleEndian(); + obj.ExeFSSizeInMediaUnits = data.ReadUInt32LittleEndian(); + obj.ExeFSHashRegionSizeInMediaUnits = data.ReadUInt32LittleEndian(); + obj.Reserved3 = data.ReadUInt32LittleEndian(); + obj.RomFSOffsetInMediaUnits = data.ReadUInt32LittleEndian(); + obj.RomFSSizeInMediaUnits = data.ReadUInt32LittleEndian(); + obj.RomFSHashRegionSizeInMediaUnits = data.ReadUInt32LittleEndian(); + obj.Reserved4 = data.ReadUInt32LittleEndian(); + obj.ExeFSSuperblockHash = data.ReadBytes(0x20); + obj.RomFSSuperblockHash = data.ReadBytes(0x20); - return header; + return obj; } /// - /// Parse a Stream into an ExeFS header + /// Parse a Stream into an NCCHHeaderFlags /// /// Stream to parse - /// Filled ExeFS header on success, null on error - public static ExeFSHeader? ParseExeFSHeader(Stream data) + /// Filled NCCHHeaderFlags on success, null on error + public static NCCHHeaderFlags ParseNCCHHeaderFlags(Stream data) { - var exeFSHeader = new ExeFSHeader(); + var obj = new NCCHHeaderFlags(); - exeFSHeader.FileHeaders = new ExeFSFileHeader[10]; - for (int i = 0; i < 10; i++) - { - var exeFsFileHeader = data.ReadType(); - if (exeFsFileHeader == null) - return null; + obj.Reserved0 = data.ReadByteValue(); + obj.Reserved1 = data.ReadByteValue(); + obj.Reserved2 = data.ReadByteValue(); + obj.CryptoMethod = (CryptoMethod)data.ReadByteValue(); + obj.ContentPlatform = (ContentPlatform)data.ReadByteValue(); + obj.MediaPlatformIndex = (ContentType)data.ReadByteValue(); + obj.ContentUnitSize = data.ReadByteValue(); + obj.BitMasks = (BitMasks)data.ReadByteValue(); - exeFsFileHeader.FileName = exeFsFileHeader.FileName?.TrimEnd('\0'); - exeFSHeader.FileHeaders[i] = exeFsFileHeader; - } - exeFSHeader.Reserved = data.ReadBytes(0x20); - exeFSHeader.FileHashes = new byte[10][]; - for (int i = 0; i < 10; i++) + return obj; + } + + /// + /// Parse a Stream into an NCSDHeader + /// + /// Stream to parse + /// Filled NCSDHeader on success, null on error + public static NCSDHeader ParseNCSDHeader(Stream data) + { + var obj = new NCSDHeader(); + + obj.RSA2048Signature = data.ReadBytes(0x100); + byte[] magicNumber = data.ReadBytes(4); + obj.MagicNumber = Encoding.ASCII.GetString(magicNumber).TrimEnd('\0'); + obj.ImageSizeInMediaUnits = data.ReadUInt32LittleEndian(); + obj.MediaId = data.ReadBytes(8); + obj.PartitionsFSType = (FilesystemType)data.ReadUInt64(); + obj.PartitionsCryptType = data.ReadBytes(8); + + obj.PartitionsTable = new PartitionTableEntry[8]; + for (int i = 0; i < 8; i++) { - exeFSHeader.FileHashes[i] = data.ReadBytes(0x20) ?? []; + obj.PartitionsTable[i] = ParsePartitionTableEntry(data); } - return exeFSHeader; + if (obj.PartitionsFSType == FilesystemType.Normal || obj.PartitionsFSType == FilesystemType.None) + { + obj.ExheaderHash = data.ReadBytes(0x20); + obj.AdditionalHeaderSize = data.ReadUInt32LittleEndian(); + obj.SectorZeroOffset = data.ReadUInt32LittleEndian(); + obj.PartitionFlags = data.ReadBytes(8); + + obj.PartitionIdTable = new ulong[8]; + for (int i = 0; i < 8; i++) + { + obj.PartitionIdTable[i] = data.ReadUInt64(); + } + + obj.Reserved1 = data.ReadBytes(0x20); + obj.Reserved2 = data.ReadBytes(0x0E); + obj.FirmUpdateByte1 = data.ReadByteValue(); + obj.FirmUpdateByte2 = data.ReadByteValue(); + } + else if (obj.PartitionsFSType == FilesystemType.FIRM) + { + obj.Unknown = data.ReadBytes(0x5E); + obj.EncryptedMBR = data.ReadBytes(0x42); + } + + return obj; + } + + /// + /// Parse a Stream into an PartitionTableEntry + /// + /// Stream to parse + /// Filled PartitionTableEntry on success, null on error + public static PartitionTableEntry ParsePartitionTableEntry(Stream data) + { + var obj = new PartitionTableEntry(); + + obj.Offset = data.ReadUInt32LittleEndian(); + obj.Length = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into an RomFSHeader + /// + /// Stream to parse + /// Filled RomFSHeader on success, null on error + public static RomFSHeader ParseRomFSHeader(Stream data) + { + var obj = new RomFSHeader(); + + byte[] magicString = data.ReadBytes(4); + obj.MagicString = Encoding.ASCII.GetString(magicString); + obj.MagicNumber = data.ReadUInt32LittleEndian(); + obj.MasterHashSize = data.ReadUInt32LittleEndian(); + obj.Level1LogicalOffset = data.ReadUInt64LittleEndian(); + obj.Level1HashdataSize = data.ReadUInt64LittleEndian(); + obj.Level1BlockSizeLog2 = data.ReadUInt32LittleEndian(); + obj.Reserved1 = data.ReadUInt32LittleEndian(); + obj.Level2LogicalOffset = data.ReadUInt64LittleEndian(); + obj.Level2HashdataSize = data.ReadUInt64LittleEndian(); + obj.Level2BlockSizeLog2 = data.ReadUInt32LittleEndian(); + obj.Reserved2 = data.ReadUInt32LittleEndian(); + obj.Level3LogicalOffset = data.ReadUInt64LittleEndian(); + obj.Level3HashdataSize = data.ReadUInt64LittleEndian(); + obj.Level3BlockSizeLog2 = data.ReadUInt32LittleEndian(); + obj.Reserved3 = data.ReadUInt32LittleEndian(); + obj.Reserved4 = data.ReadUInt32LittleEndian(); + obj.OptionalInfoSize = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into an StorageInfo + /// + /// Stream to parse + /// Filled StorageInfo on success, null on error + public static StorageInfo ParseStorageInfo(Stream data) + { + var obj = new StorageInfo(); + + obj.ExtdataID = data.ReadUInt64LittleEndian(); + obj.SystemSavedataIDs = data.ReadBytes(8); + obj.StorageAccessibleUniqueIDs = data.ReadBytes(8); + obj.FileSystemAccessInfo = data.ReadBytes(7); + obj.OtherAttributes = (StorageInfoOtherAttributes)data.ReadByteValue(); + + return obj; + } + + /// + /// Parse a Stream into an SystemControlInfo + /// + /// Stream to parse + /// Filled SystemControlInfo on success, null on error + public static SystemControlInfo ParseSystemControlInfo(Stream data) + { + var obj = new SystemControlInfo(); + + byte[] applicationTitle = data.ReadBytes(8); + obj.ApplicationTitle = Encoding.ASCII.GetString(applicationTitle).TrimEnd('\0'); + obj.Reserved1 = data.ReadBytes(5); + obj.Flag = data.ReadByteValue(); + obj.RemasterVersion = data.ReadUInt16LittleEndian(); + obj.TextCodeSetInfo = ParseCodeSetInfo(data); + obj.StackSize = data.ReadUInt32LittleEndian(); + obj.ReadOnlyCodeSetInfo = ParseCodeSetInfo(data); + obj.Reserved2 = data.ReadUInt32LittleEndian(); + obj.DataCodeSetInfo = ParseCodeSetInfo(data); + obj.BSSSize = data.ReadUInt32LittleEndian(); + obj.DependencyModuleList = new ulong[48]; + for (int i = 0; i < 48; i++) + { + obj.DependencyModuleList[i] = data.ReadUInt64LittleEndian(); + } + obj.SystemInfo = ParseSystemInfo(data); + + return obj; + } + + /// + /// Parse a Stream into an SystemInfo + /// + /// Stream to parse + /// Filled SystemInfo on success, null on error + public static SystemInfo ParseSystemInfo(Stream data) + { + var obj = new SystemInfo(); + + obj.SaveDataSize = data.ReadUInt64LittleEndian(); + obj.JumpID = data.ReadUInt64LittleEndian(); + obj.Reserved = data.ReadBytes(0x30); + + return obj; + } + + /// + /// Parse a Stream into an TestData + /// + /// Stream to parse + /// Filled TestData on success, null on error + public static TestData ParseTestData(Stream data) + { + var obj = new TestData(); + + obj.Signature = data.ReadBytes(8); + obj.AscendingByteSequence = data.ReadBytes(0x1F8); + obj.DescendingByteSequence = data.ReadBytes(0x200); + obj.Filled00 = data.ReadBytes(0x200); + obj.FilledFF = data.ReadBytes(0x200); + obj.Filled0F = data.ReadBytes(0x200); + obj.FilledF0 = data.ReadBytes(0x200); + obj.Filled55 = data.ReadBytes(0x200); + obj.FilledAA = data.ReadBytes(0x1FF); + obj.FinalByte = data.ReadByteValue(); + + return obj; } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/NCF.cs b/SabreTools.Serialization/Deserializers/NCF.cs index 28305254..6108c536 100644 --- a/SabreTools.Serialization/Deserializers/NCF.cs +++ b/SabreTools.Serialization/Deserializers/NCF.cs @@ -23,12 +23,12 @@ namespace SabreTools.Serialization.Deserializers #region Header // Try to parse the header - var header = data.ReadType
(); - if (header?.Dummy0 != 0x00000001) + var header = ParseHeader(data); + if (header.Dummy0 != 0x00000001) return null; - if (header?.MajorVersion != 0x00000002) + if (header.MajorVersion != 0x00000002) return null; - if (header?.MinorVersion != 1) + if (header.MinorVersion != 1) return null; // Set the no cache header @@ -42,8 +42,8 @@ namespace SabreTools.Serialization.Deserializers #region Directory Header // Try to parse the directory header - var directoryHeader = data.ReadType(); - if (directoryHeader?.Dummy0 != 0x00000004) + var directoryHeader = ParseDirectoryHeader(data); + if (directoryHeader.Dummy0 != 0x00000004) return null; // Set the game cache directory header @@ -59,11 +59,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the directory entries for (int i = 0; i < directoryHeader.ItemCount; i++) { - var directoryEntry = data.ReadType(); - if (directoryEntry == null) - return null; - - file.DirectoryEntries[i] = directoryEntry; + file.DirectoryEntries[i] = ParseDirectoryEntry(data); } #endregion @@ -107,11 +103,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the directory info 1 entries for (int i = 0; i < directoryHeader.Info1Count; i++) { - var directoryInfo1Entry = data.ReadType(); - if (directoryInfo1Entry == null) - return null; - - file.DirectoryInfo1Entries[i] = directoryInfo1Entry; + file.DirectoryInfo1Entries[i] = ParseDirectoryInfo1Entry(data); } #endregion @@ -124,11 +116,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the directory info 2 entries for (int i = 0; i < directoryHeader.ItemCount; i++) { - var directoryInfo2Entry = data.ReadType(); - if (directoryInfo2Entry == null) - return null; - - file.DirectoryInfo2Entries[i] = directoryInfo2Entry; + file.DirectoryInfo2Entries[i] = ParseDirectoryInfo2Entry(data); } #endregion @@ -141,11 +129,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the directory copy entries for (int i = 0; i < directoryHeader.CopyCount; i++) { - var directoryCopyEntry = data.ReadType(); - if (directoryCopyEntry == null) - return null; - - file.DirectoryCopyEntries[i] = directoryCopyEntry; + file.DirectoryCopyEntries[i] = ParseDirectoryCopyEntry(data); } #endregion @@ -158,11 +142,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the directory local entries for (int i = 0; i < directoryHeader.LocalCount; i++) { - var directoryLocalEntry = data.ReadType(); - if (directoryLocalEntry == null) - return null; - - file.DirectoryLocalEntries[i] = directoryLocalEntry; + file.DirectoryLocalEntries[i] = ParseDirectoryLocalEntry(data); } #endregion @@ -173,10 +153,10 @@ namespace SabreTools.Serialization.Deserializers #region Unknown Header // Try to parse the unknown header - var unknownHeader = data.ReadType(); - if (unknownHeader?.Dummy0 != 0x00000001) + var unknownHeader = ParseUnknownHeader(data); + if (unknownHeader.Dummy0 != 0x00000001) return null; - if (unknownHeader?.Dummy1 != 0x00000000) + if (unknownHeader.Dummy1 != 0x00000000) return null; // Set the game cache unknown header @@ -192,11 +172,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the unknown entries for (int i = 0; i < directoryHeader.ItemCount; i++) { - var unknownEntry = data.ReadType(); - if (unknownEntry == null) - return null; - - file.UnknownEntries[i] = unknownEntry; + file.UnknownEntries[i] = ParseUnknownEntry(data); } #endregion @@ -204,8 +180,8 @@ namespace SabreTools.Serialization.Deserializers #region Checksum Header // Try to parse the checksum header - var checksumHeader = data.ReadType(); - if (checksumHeader?.Dummy0 != 0x00000001) + var checksumHeader = ParseChecksumHeader(data); + if (checksumHeader.Dummy0 != 0x00000001) return null; // Set the game cache checksum header @@ -219,10 +195,10 @@ namespace SabreTools.Serialization.Deserializers #region Checksum Map Header // Try to parse the checksum map header - var checksumMapHeader = data.ReadType(); - if (checksumMapHeader?.Dummy0 != 0x14893721) + var checksumMapHeader = ParseChecksumMapHeader(data); + if (checksumMapHeader.Dummy0 != 0x14893721) return null; - if (checksumMapHeader?.Dummy1 != 0x00000001) + if (checksumMapHeader.Dummy1 != 0x00000001) return null; // Set the game cache checksum map header @@ -238,11 +214,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the checksum map entries for (int i = 0; i < checksumMapHeader.ItemCount; i++) { - var checksumMapEntry = data.ReadType(); - if (checksumMapEntry == null) - return null; - - file.ChecksumMapEntries[i] = checksumMapEntry; + file.ChecksumMapEntries[i] = ParseChecksumMapEntry(data); } #endregion @@ -255,11 +227,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the checksum entries for (int i = 0; i < checksumMapHeader.ChecksumCount; i++) { - var checksumEntry = data.ReadType(); - if (checksumEntry == null) - return null; - - file.ChecksumEntries[i] = checksumEntry; + file.ChecksumEntries[i] = ParseChecksumEntry(data); } #endregion @@ -275,5 +243,222 @@ namespace SabreTools.Serialization.Deserializers return null; } } + + /// + /// Parse a Stream into a ChecksumEntry + /// + /// Stream to parse + /// Filled ChecksumEntry on success, null on error + public static ChecksumEntry ParseChecksumEntry(Stream data) + { + var obj = new ChecksumEntry(); + + obj.Checksum = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a ChecksumHeader + /// + /// Stream to parse + /// Filled ChecksumHeader on success, null on error + public static ChecksumHeader ParseChecksumHeader(Stream data) + { + var obj = new ChecksumHeader(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.ChecksumSize = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a ChecksumMapEntry + /// + /// Stream to parse + /// Filled ChecksumMapEntry on success, null on error + public static ChecksumMapEntry ParseChecksumMapEntry(Stream data) + { + var obj = new ChecksumMapEntry(); + + obj.ChecksumCount = data.ReadUInt32LittleEndian(); + obj.FirstChecksumIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a ChecksumMapHeader + /// + /// Stream to parse + /// Filled ChecksumMapHeader on success, null on error + public static ChecksumMapHeader ParseChecksumMapHeader(Stream data) + { + var obj = new ChecksumMapHeader(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.Dummy1 = data.ReadUInt32LittleEndian(); + obj.ItemCount = data.ReadUInt32LittleEndian(); + obj.ChecksumCount = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryCopyEntry + /// + /// Stream to parse + /// Filled DirectoryCopyEntry on success, null on error + public static DirectoryCopyEntry ParseDirectoryCopyEntry(Stream data) + { + var obj = new DirectoryCopyEntry(); + + obj.DirectoryIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryEntry + /// + /// Stream to parse + /// Filled DirectoryEntry on success, null on error + public static DirectoryEntry ParseDirectoryEntry(Stream data) + { + var obj = new DirectoryEntry(); + + obj.NameOffset = data.ReadUInt32LittleEndian(); + obj.ItemSize = data.ReadUInt32LittleEndian(); + obj.ChecksumIndex = data.ReadUInt32LittleEndian(); + obj.DirectoryFlags = (HL_NCF_FLAG)data.ReadUInt32LittleEndian(); + obj.ParentIndex = data.ReadUInt32LittleEndian(); + obj.NextIndex = data.ReadUInt32LittleEndian(); + obj.FirstIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryHeader + /// + /// Stream to parse + /// Filled DirectoryHeader on success, null on error + public static DirectoryHeader ParseDirectoryHeader(Stream data) + { + var obj = new DirectoryHeader(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.CacheID = data.ReadUInt32LittleEndian(); + obj.LastVersionPlayed = data.ReadUInt32LittleEndian(); + obj.ItemCount = data.ReadUInt32LittleEndian(); + obj.FileCount = data.ReadUInt32LittleEndian(); + obj.ChecksumDataLength = data.ReadUInt32LittleEndian(); + obj.DirectorySize = data.ReadUInt32LittleEndian(); + obj.NameSize = data.ReadUInt32LittleEndian(); + obj.Info1Count = data.ReadUInt32LittleEndian(); + obj.CopyCount = data.ReadUInt32LittleEndian(); + obj.LocalCount = data.ReadUInt32LittleEndian(); + obj.Dummy1 = data.ReadUInt32LittleEndian(); + obj.Dummy2 = data.ReadUInt32LittleEndian(); + obj.Checksum = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryInfo1Entry + /// + /// Stream to parse + /// Filled DirectoryInfo1Entry on success, null on error + public static DirectoryInfo1Entry ParseDirectoryInfo1Entry(Stream data) + { + var obj = new DirectoryInfo1Entry(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryInfo2Entry + /// + /// Stream to parse + /// Filled DirectoryInfo2Entry on success, null on error + public static DirectoryInfo2Entry ParseDirectoryInfo2Entry(Stream data) + { + var obj = new DirectoryInfo2Entry(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a DirectoryLocalEntry + /// + /// Stream to parse + /// Filled DirectoryLocalEntry on success, null on error + public static DirectoryLocalEntry ParseDirectoryLocalEntry(Stream data) + { + var obj = new DirectoryLocalEntry(); + + obj.DirectoryIndex = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a Header + /// + /// Stream to parse + /// Filled Header on success, null on error + public static Header ParseHeader(Stream data) + { + var obj = new Header(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.MajorVersion = data.ReadUInt32LittleEndian(); + obj.MinorVersion = data.ReadUInt32LittleEndian(); + obj.CacheID = data.ReadUInt32LittleEndian(); + obj.LastVersionPlayed = data.ReadUInt32LittleEndian(); + obj.Dummy1 = data.ReadUInt32LittleEndian(); + obj.Dummy2 = data.ReadUInt32LittleEndian(); + obj.FileSize = data.ReadUInt32LittleEndian(); + obj.BlockSize = data.ReadUInt32LittleEndian(); + obj.BlockCount = data.ReadUInt32LittleEndian(); + obj.Dummy3 = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a UnknownEntry + /// + /// Stream to parse + /// Filled UnknownEntry on success, null on error + public static UnknownEntry ParseUnknownEntry(Stream data) + { + var obj = new UnknownEntry(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a UnknownHeader + /// + /// Stream to parse + /// Filled UnknownHeader on success, null on error + public static UnknownHeader ParseUnknownHeader(Stream data) + { + var obj = new UnknownHeader(); + + obj.Dummy0 = data.ReadUInt32LittleEndian(); + obj.Dummy1 = data.ReadUInt32LittleEndian(); + + return obj; + } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/NewExecutable.cs b/SabreTools.Serialization/Deserializers/NewExecutable.cs index 8ba5f8e9..8bdb25ba 100644 --- a/SabreTools.Serialization/Deserializers/NewExecutable.cs +++ b/SabreTools.Serialization/Deserializers/NewExecutable.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.IO; +using System.Text; using SabreTools.IO.Extensions; using SabreTools.Models.NewExecutable; using static SabreTools.Models.NewExecutable.Constants; @@ -39,8 +40,8 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the executable header data.Seek(initialOffset + stub.Header.NewExeHeaderAddr, SeekOrigin.Begin); - var header = data.ReadType(); - if (header?.Magic != SignatureString) + var header = ParseExecutableHeader(data); + if (header.Magic != SignatureString) return null; // Set the executable header @@ -57,14 +58,15 @@ namespace SabreTools.Serialization.Deserializers if (tableAddress >= data.Length) return executable; - // Try to parse the segment table + // Seek to the segment table data.Seek(tableAddress, SeekOrigin.Begin); - var segmentTable = ParseSegmentTable(data, header.FileSegmentCount); - if (segmentTable == null) - return null; // Set the segment table - executable.SegmentTable = segmentTable; + executable.SegmentTable = new SegmentTableEntry[header.FileSegmentCount]; + for (int i = 0; i < header.FileSegmentCount; i++) + { + executable.SegmentTable[i] = ParseSegmentTableEntry(data); + } #endregion @@ -77,14 +79,11 @@ namespace SabreTools.Serialization.Deserializers if (tableAddress >= data.Length) return executable; - // Try to parse the resource table + // Seek to the resource table data.Seek(tableAddress, SeekOrigin.Begin); - var resourceTable = ParseResourceTable(data, header.ResourceEntriesCount); - if (resourceTable == null) - return null; // Set the resource table - executable.ResourceTable = resourceTable; + executable.ResourceTable = ParseResourceTable(data, header.ResourceEntriesCount); ; #endregion @@ -100,14 +99,11 @@ namespace SabreTools.Serialization.Deserializers if (tableAddress >= data.Length) return executable; - // Try to parse the resident-name table + // Seek to the resident-name table data.Seek(tableAddress, SeekOrigin.Begin); - var residentNameTable = ParseResidentNameTable(data, endOffset); - if (residentNameTable == null) - return null; // Set the resident-name table - executable.ResidentNameTable = residentNameTable; + executable.ResidentNameTable = ParseResidentNameTable(data, endOffset); #endregion @@ -120,14 +116,15 @@ namespace SabreTools.Serialization.Deserializers if (tableAddress >= data.Length) return executable; - // Try to parse the module-reference table + // Seek to the module-reference table data.Seek(tableAddress, SeekOrigin.Begin); - var moduleReferenceTable = ParseModuleReferenceTable(data, header.ModuleReferenceTableSize); - if (moduleReferenceTable == null) - return null; // Set the module-reference table - executable.ModuleReferenceTable = moduleReferenceTable; + executable.ModuleReferenceTable = new ModuleReferenceTableEntry[header.ModuleReferenceTableSize]; + for (int i = 0; i < header.ModuleReferenceTableSize; i++) + { + executable.ModuleReferenceTable[i] = ParseModuleReferenceTableEntry(data); + } #endregion @@ -143,14 +140,11 @@ namespace SabreTools.Serialization.Deserializers if (tableAddress >= data.Length) return executable; - // Try to parse the imported-name table + // Seek to the imported-name table data.Seek(tableAddress, SeekOrigin.Begin); - var importedNameTable = ParseImportedNameTable(data, endOffset); - if (importedNameTable == null) - return null; // Set the imported-name table - executable.ImportedNameTable = importedNameTable; + executable.ImportedNameTable = ParseImportedNameTable(data, endOffset); #endregion @@ -167,14 +161,11 @@ namespace SabreTools.Serialization.Deserializers if (tableAddress >= data.Length) return executable; - // Try to parse the imported-name table + // Seek to the imported-name table data.Seek(tableAddress, SeekOrigin.Begin); - var entryTable = ParseEntryTable(data, endOffset); - if (entryTable == null) - return null; // Set the entry table - executable.EntryTable = entryTable; + executable.EntryTable = ParseEntryTable(data, endOffset); #endregion @@ -189,14 +180,11 @@ namespace SabreTools.Serialization.Deserializers if (tableAddress >= data.Length) return executable; - // Try to parse the nonresident-name table + // Seek to the nonresident-name table data.Seek(tableAddress, SeekOrigin.Begin); - var nonResidentNameTable = ParseNonResidentNameTable(data, endOffset); - if (nonResidentNameTable == null) - return null; // Set the nonresident-name table - executable.NonResidentNameTable = nonResidentNameTable; + executable.NonResidentNameTable = ParseNonResidentNameTable(data, endOffset); #endregion @@ -210,68 +198,229 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a segment table + /// Parse a Stream into an entry table /// /// Stream to parse - /// Number of segment table entries to read - /// Filled segment table on success, null on error - public static SegmentTableEntry[]? ParseSegmentTable(Stream data, int count) + /// First address not part of the entry table + /// Filled entry table on success, null on error + public static EntryTableBundle[] ParseEntryTable(Stream data, int endOffset) { - var segmentTable = new SegmentTableEntry[count]; + var entryTable = new List(); - for (int i = 0; i < count; i++) + while (data.Position < endOffset && data.Position < data.Length) { - var entry = data.ReadType(); - if (entry == null) - return null; + var entry = new EntryTableBundle(); + entry.EntryCount = data.ReadByteValue(); + entry.SegmentIndicator = data.ReadByteValue(); + switch (entry.GetEntryType()) + { + case SegmentEntryType.Unused: + break; - segmentTable[i] = entry; + case SegmentEntryType.FixedSegment: + entry.FixedFlagWord = (FixedSegmentEntryFlag)data.ReadByteValue(); + entry.FixedOffset = data.ReadUInt16LittleEndian(); + break; + + case SegmentEntryType.MoveableSegment: + entry.MoveableFlagWord = (MoveableSegmentEntryFlag)data.ReadByteValue(); + entry.MoveableReserved = data.ReadUInt16LittleEndian(); + entry.MoveableSegmentNumber = data.ReadByteValue(); + entry.MoveableOffset = data.ReadUInt16LittleEndian(); + break; + } + entryTable.Add(entry); } - return segmentTable; + return [.. entryTable]; } /// - /// Parse a Stream into a resource table + /// Parse a Stream into an ExecutableHeader + /// + /// Stream to parse + /// Filled ExecutableHeader on success, null on error + public static ExecutableHeader ParseExecutableHeader(Stream data) + { + var obj = new ExecutableHeader(); + + byte[] magic = data.ReadBytes(2); + obj.Magic = Encoding.ASCII.GetString(magic); + obj.LinkerVersion = data.ReadByteValue(); + obj.LinkerRevision = data.ReadByteValue(); + obj.EntryTableOffset = data.ReadUInt16LittleEndian(); + obj.EntryTableSize = data.ReadUInt16LittleEndian(); + obj.CrcChecksum = data.ReadUInt32LittleEndian(); + obj.FlagWord = (HeaderFlag)data.ReadUInt16LittleEndian(); + obj.AutomaticDataSegmentNumber = data.ReadUInt16LittleEndian(); + obj.InitialHeapAlloc = data.ReadUInt16LittleEndian(); + obj.InitialStackAlloc = data.ReadUInt16LittleEndian(); + obj.InitialCSIPSetting = data.ReadUInt32LittleEndian(); + obj.InitialSSSPSetting = data.ReadUInt32LittleEndian(); + obj.FileSegmentCount = data.ReadUInt16LittleEndian(); + obj.ModuleReferenceTableSize = data.ReadUInt16LittleEndian(); + obj.NonResidentNameTableSize = data.ReadUInt16LittleEndian(); + obj.SegmentTableOffset = data.ReadUInt16LittleEndian(); + obj.ResourceTableOffset = data.ReadUInt16LittleEndian(); + obj.ResidentNameTableOffset = data.ReadUInt16LittleEndian(); + obj.ModuleReferenceTableOffset = data.ReadUInt16LittleEndian(); + obj.ImportedNamesTableOffset = data.ReadUInt16LittleEndian(); + obj.NonResidentNamesTableOffset = data.ReadUInt32LittleEndian(); + obj.MovableEntriesCount = data.ReadUInt16LittleEndian(); + obj.SegmentAlignmentShiftCount = data.ReadUInt16LittleEndian(); + obj.ResourceEntriesCount = data.ReadUInt16LittleEndian(); + obj.TargetOperatingSystem = (Models.NewExecutable.OperatingSystem)data.ReadByteValue(); + obj.AdditionalFlags = (OS2Flag)data.ReadByteValue(); + obj.ReturnThunkOffset = data.ReadUInt16LittleEndian(); + obj.SegmentReferenceThunkOffset = data.ReadUInt16LittleEndian(); + obj.MinCodeSwapAreaSize = data.ReadUInt16LittleEndian(); + obj.WindowsSDKRevision = data.ReadByteValue(); + obj.WindowsSDKVersion = data.ReadByteValue(); + + return obj; + } + + /// + /// Parse a Stream into an imported-name table + /// + /// Stream to parse + /// First address not part of the imported-name table + /// Filled imported-name table on success, null on error + public static Dictionary ParseImportedNameTable(Stream data, int endOffset) + { + var importedNameTable = new Dictionary(); + + while (data.Position < endOffset && data.Position < data.Length) + { + ushort currentOffset = (ushort)data.Position; + importedNameTable[currentOffset] = ParseImportedNameTableEntry(data); + } + + return importedNameTable; + } + + /// + /// Parse a Stream into an ImportedNameTableEntry + /// + /// Stream to parse + /// Filled ImportedNameTableEntry on success, null on error + public static ImportedNameTableEntry ParseImportedNameTableEntry(Stream data) + { + var obj = new ImportedNameTableEntry(); + + obj.Length = data.ReadByteValue(); + obj.NameString = data.ReadBytes(obj.Length); + + return obj; + } + + /// + /// Parse a Stream into an ModuleReferenceTableEntry + /// + /// Stream to parse + /// Filled ModuleReferenceTableEntry on success, null on error + public static ModuleReferenceTableEntry ParseModuleReferenceTableEntry(Stream data) + { + var obj = new ModuleReferenceTableEntry(); + + obj.Offset = data.ReadUInt16LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a nonresident-name table + /// + /// Stream to parse + /// First address not part of the nonresident-name table + /// Filled nonresident-name table on success, null on error + public static NonResidentNameTableEntry[] ParseNonResidentNameTable(Stream data, int endOffset) + { + var residentNameTable = new List(); + + while (data.Position < endOffset && data.Position < data.Length) + { + var entry = ParseNonResidentNameTableEntry(data); + residentNameTable.Add(entry); + } + + return [.. residentNameTable]; + } + + /// + /// Parse a Stream into a NonResidentNameTableEntry + /// + /// Stream to parse + /// Filled NonResidentNameTableEntry on success, null on error + public static NonResidentNameTableEntry ParseNonResidentNameTableEntry(Stream data) + { + var obj = new NonResidentNameTableEntry(); + + obj.Length = data.ReadByteValue(); + obj.NameString = data.ReadBytes(obj.Length); + obj.OrdinalNumber = data.ReadUInt16LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a resident-name table + /// + /// Stream to parse + /// First address not part of the resident-name table + /// Filled resident-name table on success, null on error + public static ResidentNameTableEntry[] ParseResidentNameTable(Stream data, int endOffset) + { + var residentNameTable = new List(); + + while (data.Position < endOffset && data.Position < data.Length) + { + var entry = ParseResidentNameTableEntry(data); + residentNameTable.Add(entry); + } + + return [.. residentNameTable]; + } + + /// + /// Parse a Stream into a ResidentNameTableEntry + /// + /// Stream to parse + /// Filled ResidentNameTableEntry on success, null on error + public static ResidentNameTableEntry ParseResidentNameTableEntry(Stream data) + { + var obj = new ResidentNameTableEntry(); + + obj.Length = data.ReadByteValue(); + obj.NameString = data.ReadBytes(obj.Length); + obj.OrdinalNumber = data.ReadUInt16LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a ResourceTable /// /// Stream to parse /// Number of resource table entries to read - /// Filled resource table on success, null on error - public static ResourceTable? ParseResourceTable(Stream data, ushort count) + /// Filled ResourceTable on success, null on error + public static ResourceTable ParseResourceTable(Stream data, ushort count) { long initialOffset = data.Position; var resourceTable = new ResourceTable(); - resourceTable.AlignmentShiftCount = data.ReadUInt16(); + resourceTable.AlignmentShiftCount = data.ReadUInt16LittleEndian(); var resourceTypes = new List(); for (int i = 0; i < count; i++) { - var entry = new ResourceTypeInformationEntry(); - - entry.TypeID = data.ReadUInt16(); - entry.ResourceCount = data.ReadUInt16(); - entry.Reserved = data.ReadUInt32(); + var entry = ParseResourceTypeInformationEntry(data); + resourceTypes.Add(entry); // 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++) - { - // TODO: Should we read and store the resource data? - var resource = data.ReadType(); - if (resource == null) - return null; - - entry.Resources[j] = resource; - } - resourceTypes.Add(entry); } resourceTable.ResourceTypes = [.. resourceTypes]; @@ -314,9 +463,6 @@ namespace SabreTools.Serialization.Deserializers data.Seek(stringOffset, SeekOrigin.Begin); var str = ParseResourceTypeAndNameString(data); - if (str == null) - return null; - resourceTable.TypeAndNameStrings[stringOffsets[i]] = str; } @@ -324,192 +470,80 @@ namespace SabreTools.Serialization.Deserializers } /// - /// Parse a Stream into a resource type and name string + /// Parse a Stream into an ResourceTypeInformationEntry /// /// Stream to parse - /// Filled resource type and name string on success, null on error - public static ResourceTypeAndNameString? ParseResourceTypeAndNameString(Stream data) + /// Filled ResourceTypeInformationEntry on success, null on error + public static ResourceTypeInformationEntry ParseResourceTypeInformationEntry(Stream data) { - var str = new ResourceTypeAndNameString(); + var obj = new ResourceTypeInformationEntry(); - str.Length = data.ReadByteValue(); - str.Text = data.ReadBytes(str.Length); + obj.TypeID = data.ReadUInt16LittleEndian(); + obj.ResourceCount = data.ReadUInt16LittleEndian(); + obj.Reserved = data.ReadUInt32LittleEndian(); - return str; - } + // A zero type ID marks the end of the resource type information blocks. + if (obj.TypeID == 0) + return obj; - /// - /// Parse a Stream into a resident-name table - /// - /// Stream to parse - /// First address not part of the resident-name table - /// Filled resident-name table on success, null on error - public static ResidentNameTableEntry[]? ParseResidentNameTable(Stream data, int endOffset) - { - var residentNameTable = new List(); - - while (data.Position < endOffset && data.Position < data.Length) + obj.Resources = new ResourceTypeResourceEntry[obj.ResourceCount]; + for (int i = 0; i < obj.ResourceCount; i++) { - var entry = ParseResidentNameTableEntry(data); - if (entry == null) - return null; - - residentNameTable.Add(entry); + // TODO: Should we read and store the resource data? + obj.Resources[i] = ParseResourceTypeResourceEntry(data); } - return [.. residentNameTable]; + return obj; } /// - /// Parse a Stream into a resident-name table entry + /// Parse a Stream into a ResourceTypeAndNameString /// /// Stream to parse - /// Filled resident-name table entry on success, null on error - public static ResidentNameTableEntry? ParseResidentNameTableEntry(Stream data) + /// Filled rResourceTypeAndNameString on success, null on error + public static ResourceTypeAndNameString ParseResourceTypeAndNameString(Stream data) { - var entry = new ResidentNameTableEntry(); + var obj = new ResourceTypeAndNameString(); - entry.Length = data.ReadByteValue(); - entry.NameString = data.ReadBytes(entry.Length); - entry.OrdinalNumber = data.ReadUInt16(); + obj.Length = data.ReadByteValue(); + obj.Text = data.ReadBytes(obj.Length); - return entry; + return obj; } /// - /// Parse a Stream into a module-reference table + /// Parse a Stream into an ResourceTypeResourceEntry /// /// Stream to parse - /// Number of module-reference table entries to read - /// Filled module-reference table on success, null on error - public static ModuleReferenceTableEntry[]? ParseModuleReferenceTable(Stream data, int count) + /// Filled ResourceTypeResourceEntry on success, null on error + public static ResourceTypeResourceEntry ParseResourceTypeResourceEntry(Stream data) { - var moduleReferenceTable = new ModuleReferenceTableEntry[count]; + var obj = new ResourceTypeResourceEntry(); - for (int i = 0; i < count; i++) - { - var entry = data.ReadType(); - if (entry == null) - return null; + obj.Offset = data.ReadUInt16LittleEndian(); + obj.Length = data.ReadUInt16LittleEndian(); + obj.FlagWord = (ResourceTypeResourceFlag)data.ReadUInt16LittleEndian(); + obj.ResourceID = data.ReadUInt16LittleEndian(); + obj.Reserved = data.ReadUInt32LittleEndian(); - moduleReferenceTable[i] = entry; - } - - return moduleReferenceTable; + return obj; } /// - /// Parse a Stream into an imported-name table + /// Parse a Stream into an SegmentTableEntry /// /// Stream to parse - /// First address not part of the imported-name table - /// Filled imported-name table on success, null on error - public static Dictionary? ParseImportedNameTable(Stream data, int endOffset) + /// Filled SegmentTableEntry on success, null on error + public static SegmentTableEntry ParseSegmentTableEntry(Stream data) { - var importedNameTable = new Dictionary(); + var obj = new SegmentTableEntry(); - while (data.Position < endOffset && data.Position < data.Length) - { - ushort currentOffset = (ushort)data.Position; - var entry = ParseImportedNameTableEntry(data); - if (entry == null) - return null; + obj.Offset = data.ReadUInt16LittleEndian(); + obj.Length = data.ReadUInt16LittleEndian(); + obj.FlagWord = (SegmentTableEntryFlag)data.ReadUInt16LittleEndian(); + obj.MinimumAllocationSize = data.ReadUInt16LittleEndian(); - importedNameTable[currentOffset] = entry; - } - - return importedNameTable; - } - - /// - /// Parse a Stream into an imported-name table entry - /// - /// Stream to parse - /// Filled imported-name table entry on success, null on error - public static ImportedNameTableEntry? ParseImportedNameTableEntry(Stream data) - { - var entry = new ImportedNameTableEntry(); - - entry.Length = data.ReadByteValue(); - entry.NameString = data.ReadBytes(entry.Length); - - return entry; - } - - /// - /// Parse a Stream into an entry table - /// - /// Stream to parse - /// First address not part of the entry table - /// Filled entry table on success, null on error - public static EntryTableBundle[] ParseEntryTable(Stream data, int endOffset) - { - var entryTable = new List(); - - while (data.Position < endOffset && data.Position < data.Length) - { - var entry = new EntryTableBundle(); - entry.EntryCount = data.ReadByteValue(); - entry.SegmentIndicator = data.ReadByteValue(); - switch (entry.GetEntryType()) - { - case SegmentEntryType.Unused: - break; - - case SegmentEntryType.FixedSegment: - entry.FixedFlagWord = (FixedSegmentEntryFlag)data.ReadByteValue(); - entry.FixedOffset = data.ReadUInt16(); - break; - - case SegmentEntryType.MoveableSegment: - entry.MoveableFlagWord = (MoveableSegmentEntryFlag)data.ReadByteValue(); - entry.MoveableReserved = data.ReadUInt16(); - entry.MoveableSegmentNumber = data.ReadByteValue(); - entry.MoveableOffset = data.ReadUInt16(); - break; - } - entryTable.Add(entry); - } - - return [.. entryTable]; - } - - /// - /// Parse a Stream into a nonresident-name table - /// - /// Stream to parse - /// First address not part of the nonresident-name table - /// Filled nonresident-name table on success, null on error - public static NonResidentNameTableEntry[]? ParseNonResidentNameTable(Stream data, int endOffset) - { - var residentNameTable = new List(); - - while (data.Position < endOffset && data.Position < data.Length) - { - var entry = ParseNonResidentNameTableEntry(data); - if (entry == null) - return null; - - residentNameTable.Add(entry); - } - - return [.. residentNameTable]; - } - - /// - /// Parse a Stream into a nonresident-name table entry - /// - /// Stream to parse - /// Filled nonresident-name table entry on success, null on error - public static NonResidentNameTableEntry? ParseNonResidentNameTableEntry(Stream data) - { - var entry = new NonResidentNameTableEntry(); - - entry.Length = data.ReadByteValue(); - entry.NameString = data.ReadBytes(entry.Length); - entry.OrdinalNumber = data.ReadUInt16(); - - return entry; + return obj; } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/Nitro.cs b/SabreTools.Serialization/Deserializers/Nitro.cs index 3ba47fb4..7407e963 100644 --- a/SabreTools.Serialization/Deserializers/Nitro.cs +++ b/SabreTools.Serialization/Deserializers/Nitro.cs @@ -22,27 +22,16 @@ namespace SabreTools.Serialization.Deserializers #region Header - // Try to parse the header - var header = data.ReadType(); - if (header == null) - return null; - // Set the cart image header - cart.CommonHeader = header; + cart.CommonHeader = ParseCommonHeader(data); #endregion #region Extended DSi Header // If we have a DSi-compatible cartridge - if (header.UnitCode == Unitcode.NDSPlusDSi || header.UnitCode == Unitcode.DSi) - { - var extendedDSiHeader = data.ReadType(); - if (extendedDSiHeader == null) - return null; - - cart.ExtendedDSiHeader = extendedDSiHeader; - } + if (cart.CommonHeader.UnitCode == Unitcode.NDSPlusDSi || cart.CommonHeader.UnitCode == Unitcode.DSi) + cart.ExtendedDSiHeader = ParseExtendedDSiHeader(data); #endregion @@ -64,27 +53,22 @@ namespace SabreTools.Serialization.Deserializers #region Name Table // Try to get the name table offset - long nameTableOffset = header.FileNameTableOffset; + long nameTableOffset = cart.CommonHeader.FileNameTableOffset; if (nameTableOffset < 0 || nameTableOffset > data.Length) return null; // Seek to the name table data.Seek(nameTableOffset, SeekOrigin.Begin); - // Try to parse the name table - var nameTable = ParseNameTable(data); - if (nameTable == null) - return null; - // Set the name table - cart.NameTable = nameTable; + cart.NameTable = ParseNameTable(data); #endregion #region File Allocation Table // Try to get the file allocation table offset - long fileAllocationTableOffset = header.FileAllocationTableOffset; + long fileAllocationTableOffset = cart.CommonHeader.FileAllocationTableOffset; if (fileAllocationTableOffset < 0 || fileAllocationTableOffset > data.Length) return null; @@ -95,12 +79,9 @@ namespace SabreTools.Serialization.Deserializers var fileAllocationTable = new List(); // Try to parse the file allocation table - while (data.Position - fileAllocationTableOffset < header.FileAllocationTableLength) + while (data.Position - fileAllocationTableOffset < cart.CommonHeader.FileAllocationTableLength) { - var entry = data.ReadType(); - if (entry == null) - return null; - + var entry = ParseFileAllocationTableEntry(data); fileAllocationTable.Add(entry); } @@ -121,12 +102,203 @@ namespace SabreTools.Serialization.Deserializers } } + /// + /// Parse a Stream into a CommonHeader + /// + /// Stream to parse + /// Filled CommonHeader on success, null on error + public static CommonHeader ParseCommonHeader(Stream data) + { + var obj = new CommonHeader(); + + byte[] gameTitle = data.ReadBytes(12); + obj.GameTitle = Encoding.ASCII.GetString(gameTitle).TrimEnd('\0'); + obj.GameCode = data.ReadUInt32LittleEndian(); + byte[] makerCode = data.ReadBytes(2); + obj.MakerCode = Encoding.ASCII.GetString(makerCode); + obj.UnitCode = (Unitcode)data.ReadByteValue(); + obj.EncryptionSeedSelect = data.ReadByteValue(); + obj.DeviceCapacity = data.ReadByteValue(); + obj.Reserved1 = data.ReadBytes(7); + obj.GameRevision = data.ReadUInt16LittleEndian(); + obj.RomVersion = data.ReadByteValue(); + obj.InternalFlags = data.ReadByteValue(); + obj.ARM9RomOffset = data.ReadUInt32LittleEndian(); + obj.ARM9EntryAddress = data.ReadUInt32LittleEndian(); + obj.ARM9LoadAddress = data.ReadUInt32LittleEndian(); + obj.ARM9Size = data.ReadUInt32LittleEndian(); + obj.ARM7RomOffset = data.ReadUInt32LittleEndian(); + obj.ARM7EntryAddress = data.ReadUInt32LittleEndian(); + obj.ARM7LoadAddress = data.ReadUInt32LittleEndian(); + obj.ARM7Size = data.ReadUInt32LittleEndian(); + obj.FileNameTableOffset = data.ReadUInt32LittleEndian(); + obj.FileNameTableLength = data.ReadUInt32LittleEndian(); + obj.FileAllocationTableOffset = data.ReadUInt32LittleEndian(); + obj.FileAllocationTableLength = data.ReadUInt32LittleEndian(); + obj.ARM9OverlayOffset = data.ReadUInt32LittleEndian(); + obj.ARM9OverlayLength = data.ReadUInt32LittleEndian(); + obj.ARM7OverlayOffset = data.ReadUInt32LittleEndian(); + obj.ARM7OverlayLength = data.ReadUInt32LittleEndian(); + obj.NormalCardControlRegisterSettings = data.ReadUInt32LittleEndian(); + obj.SecureCardControlRegisterSettings = data.ReadUInt32LittleEndian(); + obj.IconBannerOffset = data.ReadUInt32LittleEndian(); + obj.SecureAreaCRC = data.ReadUInt16LittleEndian(); + obj.SecureTransferTimeout = data.ReadUInt16LittleEndian(); + obj.ARM9Autoload = data.ReadUInt32LittleEndian(); + obj.ARM7Autoload = data.ReadUInt32LittleEndian(); + obj.SecureDisable = data.ReadBytes(8); + obj.NTRRegionRomSize = data.ReadUInt32LittleEndian(); + obj.HeaderSize = data.ReadUInt32LittleEndian(); + obj.Reserved2 = data.ReadBytes(56); + obj.NintendoLogo = data.ReadBytes(156); + obj.NintendoLogoCRC = data.ReadUInt16LittleEndian(); + obj.HeaderCRC = data.ReadUInt16LittleEndian(); + obj.DebuggerReserved = data.ReadBytes(0x20); + + return obj; + } + + /// + /// Parse a Stream into a ExtendedDSiHeader + /// + /// Stream to parse + /// Filled ExtendedDSiHeader on success, null on error + public static ExtendedDSiHeader ParseExtendedDSiHeader(Stream data) + { + var obj = new ExtendedDSiHeader(); + + obj.GlobalMBK15Settings = new uint[5]; + for (int i = 0; i < 5; i++) + { + obj.GlobalMBK15Settings[i] = data.ReadUInt32LittleEndian(); + } + obj.LocalMBK68SettingsARM9 = new uint[3]; + for (int i = 0; i < 3; i++) + { + obj.LocalMBK68SettingsARM9[i] = data.ReadUInt32LittleEndian(); + } + obj.LocalMBK68SettingsARM7 = new uint[3]; + for (int i = 0; i < 3; i++) + { + obj.LocalMBK68SettingsARM7[i] = data.ReadUInt32LittleEndian(); + } + obj.GlobalMBK9Setting = data.ReadUInt32LittleEndian(); + obj.RegionFlags = data.ReadUInt32LittleEndian(); + obj.AccessControl = data.ReadUInt32LittleEndian(); + obj.ARM7SCFGEXTMask = data.ReadUInt32LittleEndian(); + obj.ReservedFlags = data.ReadUInt32LittleEndian(); + obj.ARM9iRomOffset = data.ReadUInt32LittleEndian(); + obj.Reserved3 = data.ReadUInt32LittleEndian(); + obj.ARM9iLoadAddress = data.ReadUInt32LittleEndian(); + obj.ARM9iSize = data.ReadUInt32LittleEndian(); + obj.ARM7iRomOffset = data.ReadUInt32LittleEndian(); + obj.Reserved4 = data.ReadUInt32LittleEndian(); + obj.ARM7iLoadAddress = data.ReadUInt32LittleEndian(); + obj.ARM7iSize = data.ReadUInt32LittleEndian(); + obj.DigestNTRRegionOffset = data.ReadUInt32LittleEndian(); + obj.DigestNTRRegionLength = data.ReadUInt32LittleEndian(); + obj.DigestTWLRegionOffset = data.ReadUInt32LittleEndian(); + obj.DigestTWLRegionLength = data.ReadUInt32LittleEndian(); + obj.DigestSectorHashtableRegionOffset = data.ReadUInt32LittleEndian(); + obj.DigestSectorHashtableRegionLength = data.ReadUInt32LittleEndian(); + obj.DigestBlockHashtableRegionOffset = data.ReadUInt32LittleEndian(); + obj.DigestBlockHashtableRegionLength = data.ReadUInt32LittleEndian(); + obj.DigestSectorSize = data.ReadUInt32LittleEndian(); + obj.DigestBlockSectorCount = data.ReadUInt32LittleEndian(); + obj.IconBannerSize = data.ReadUInt32LittleEndian(); + obj.Unknown1 = data.ReadUInt32LittleEndian(); + obj.NTRTWLRegionRomSize = data.ReadUInt32LittleEndian(); + obj.Unknown2 = data.ReadBytes(12); + obj.ModcryptArea1Offset = data.ReadUInt32LittleEndian(); + obj.ModcryptArea1Size = data.ReadUInt32LittleEndian(); + obj.ModcryptArea2Offset = data.ReadUInt32LittleEndian(); + obj.ModcryptArea2Size = data.ReadUInt32LittleEndian(); + obj.TitleID = data.ReadBytes(8); + obj.DSiWarePublicSavSize = data.ReadUInt32LittleEndian(); + obj.DSiWarePrivateSavSize = data.ReadUInt32LittleEndian(); + obj.ReservedZero = data.ReadBytes(176); + obj.Unknown3 = data.ReadBytes(16); + obj.ARM9WithSecureAreaSHA1HMACHash = data.ReadBytes(20); + obj.ARM7SHA1HMACHash = data.ReadBytes(20); + obj.DigestMasterSHA1HMACHash = data.ReadBytes(20); + obj.BannerSHA1HMACHash = data.ReadBytes(20); + obj.ARM9iDecryptedSHA1HMACHash = data.ReadBytes(20); + obj.ARM7iDecryptedSHA1HMACHash = data.ReadBytes(20); + obj.Reserved5 = data.ReadBytes(40); + obj.ARM9NoSecureAreaSHA1HMACHash = data.ReadBytes(20); + obj.Reserved6 = data.ReadBytes(2636); + obj.ReservedAndUnchecked = data.ReadBytes(0x180); + obj.RSASignature = data.ReadBytes(0x80); + + return obj; + } + + /// + /// Parse a Stream into a FileAllocationTableEntry + /// + /// Stream to parse + /// Filled FileAllocationTableEntry on success, null on error + public static FileAllocationTableEntry ParseFileAllocationTableEntry(Stream data) + { + var obj = new FileAllocationTableEntry(); + + obj.StartOffset = data.ReadUInt32LittleEndian(); + obj.EndOffset = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a FolderAllocationTableEntry + /// + /// Stream to parse + /// Filled FolderAllocationTableEntry on success, null on error + public static FolderAllocationTableEntry ParseFolderAllocationTableEntry(Stream data) + { + var obj = new FolderAllocationTableEntry(); + + obj.StartOffset = data.ReadUInt32LittleEndian(); + obj.FirstFileIndex = data.ReadUInt16LittleEndian(); + obj.ParentFolderIndex = data.ReadByteValue(); + obj.Unknown = data.ReadByteValue(); + + return obj; + } + + /// + /// Parse a Stream into a NameListEntry + /// + /// Stream to parse + /// Filled NameListEntry on success, null on error + public static NameListEntry? ParseNameListEntry(Stream data) + { + var entry = new NameListEntry(); + + byte flagAndSize = data.ReadByteValue(); + if (flagAndSize == 0xFF) + return null; + + entry.Folder = (flagAndSize & 0x80) != 0; + + byte size = (byte)(flagAndSize & ~0x80); + if (size > 0) + { + byte[] name = data.ReadBytes(size); + entry.Name = Encoding.UTF8.GetString(name); + } + + if (entry.Folder) + entry.Index = data.ReadUInt16(); + + return entry; + } + /// /// Parse a Stream into a name table /// /// Stream to parse /// Filled name table on success, null on error - private static NameTable? ParseNameTable(Stream data) + public static NameTable ParseNameTable(Stream data) { var nameTable = new NameTable(); @@ -135,10 +307,7 @@ namespace SabreTools.Serialization.Deserializers int entryCount = int.MaxValue; while (entryCount > 0) { - var entry = data.ReadType(); - if (entry == null) - return null; - + var entry = ParseFolderAllocationTableEntry(data); folderAllocationTable.Add(entry); // If we have the root entry @@ -168,33 +337,5 @@ namespace SabreTools.Serialization.Deserializers return nameTable; } - - /// - /// Parse a Stream into a name list entry - /// - /// Stream to parse - /// Filled name list entry on success, null on error - private static NameListEntry? ParseNameListEntry(Stream data) - { - var entry = new NameListEntry(); - - byte flagAndSize = data.ReadByteValue(); - if (flagAndSize == 0xFF) - return null; - - entry.Folder = (flagAndSize & 0x80) != 0; - - byte size = (byte)(flagAndSize & ~0x80); - if (size > 0) - { - byte[] name = data.ReadBytes(size); - entry.Name = Encoding.UTF8.GetString(name); - } - - if (entry.Folder) - entry.Index = data.ReadUInt16(); - - return entry; - } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/PAK.cs b/SabreTools.Serialization/Deserializers/PAK.cs index ea08e236..eb1aebea 100644 --- a/SabreTools.Serialization/Deserializers/PAK.cs +++ b/SabreTools.Serialization/Deserializers/PAK.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Text; using SabreTools.IO.Extensions; using SabreTools.Models.PAK; using static SabreTools.Models.PAK.Constants; @@ -22,8 +23,8 @@ namespace SabreTools.Serialization.Deserializers #region Header // Try to parse the header - var header = data.ReadType
(); - if (header?.Signature != SignatureString) + var header = ParseHeader(data); + if (header.Signature != SignatureString) return null; // Set the package header @@ -47,11 +48,7 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the directory items for (int i = 0; i < file.DirectoryItems.Length; i++) { - var directoryItem = data.ReadType(); - if (directoryItem == null) - return null; - - file.DirectoryItems[i] = directoryItem; + file.DirectoryItems[i] = ParseDirectoryItem(data); } #endregion @@ -64,5 +61,39 @@ namespace SabreTools.Serialization.Deserializers return null; } } + + /// + /// Parse a Stream into a DirectoryItem + /// + /// Stream to parse + /// Filled DirectoryItem on success, null on error + public static DirectoryItem ParseDirectoryItem(Stream data) + { + var obj = new DirectoryItem(); + + byte[] itemName = data.ReadBytes(56); + obj.ItemName = Encoding.ASCII.GetString(itemName).TrimEnd('\0'); + obj.ItemOffset = data.ReadUInt32LittleEndian(); + obj.ItemLength = data.ReadUInt32LittleEndian(); + + return obj; + } + + /// + /// Parse a Stream into a Header + /// + /// Stream to parse + /// Filled Header on success, null on error + public static Header ParseHeader(Stream data) + { + var obj = new Header(); + + byte[] signature = data.ReadBytes(4); + obj.Signature = Encoding.ASCII.GetString(signature); + obj.DirectoryOffset = data.ReadUInt32LittleEndian(); + obj.DirectoryLength = data.ReadUInt32LittleEndian(); + + return obj; + } } } \ No newline at end of file diff --git a/SabreTools.Serialization/Deserializers/PFF.cs b/SabreTools.Serialization/Deserializers/PFF.cs index 835bebb8..9ed490b4 100644 --- a/SabreTools.Serialization/Deserializers/PFF.cs +++ b/SabreTools.Serialization/Deserializers/PFF.cs @@ -24,8 +24,33 @@ namespace SabreTools.Serialization.Deserializers // Try to parse the header var header = ParseHeader(data); - if (header == null) + if (header.Signature == Version0SignatureString) + { + if (header.FileSegmentSize != Version0HSegmentSize) + return null; + } + else if (header.Signature == Version2SignatureString) + { + if (header.FileSegmentSize != Version2SegmentSize) + return null; + } + else if (header.Signature == Version3SignatureString) + { + if (header.FileSegmentSize != Version2SegmentSize + && header.FileSegmentSize != Version3SegmentSize) + { + return null; + } + } + else if (header.Signature == Version4SignatureString) + { + if (header.FileSegmentSize != Version4SegmentSize) + return null; + } + else + { return null; + } // Set the archive header archive.Header = header; @@ -48,11 +73,7 @@ namespace SabreTools.Serialization.Deserializers // Read all segments in turn for (int i = 0; i < header.NumberOfFiles; i++) { - var file = ParseSegment(data, header.FileSegmentSize); - if (file == null) - continue; - - archive.Segments[i] = file; + archive.Segments[i] = ParseSegment(data, header.FileSegmentSize); ; } #endregion @@ -67,13 +88,8 @@ namespace SabreTools.Serialization.Deserializers // Seek to the footer data.Seek(offset, SeekOrigin.Begin); - // Try to parse the footer - var footer = data.ReadType