diff --git a/SabreTools.Serialization/Wrappers/AACSMediaKeyBlock.cs b/SabreTools.Serialization/Wrappers/AACSMediaKeyBlock.cs index 0c429e68..6ae70ba0 100644 --- a/SabreTools.Serialization/Wrappers/AACSMediaKeyBlock.cs +++ b/SabreTools.Serialization/Wrappers/AACSMediaKeyBlock.cs @@ -27,13 +27,21 @@ namespace SabreTools.Serialization.Wrappers { get { + // Use the cached value, if it exists + if (field != null) + return field; + + // Find the type and version record, if possible var record = Array.Find(Records, r => r.RecordType == RecordType.TypeAndVersion); if (record is TypeAndVersionRecord tavr) - return tavr.VersionNumber.ToString(); + { + field = tavr.VersionNumber.ToString(); + return field; + } - return null; + return field; } - } + } = null; #endregion diff --git a/SabreTools.Serialization/Wrappers/CFB.cs b/SabreTools.Serialization/Wrappers/CFB.cs index 0a068289..59f36f91 100644 --- a/SabreTools.Serialization/Wrappers/CFB.cs +++ b/SabreTools.Serialization/Wrappers/CFB.cs @@ -41,7 +41,7 @@ namespace SabreTools.Serialization.Wrappers return field; // If there are no directory entries - if (DirectoryEntries == null || DirectoryEntries.Length == 0) + if (DirectoryEntries.Length == 0) return []; // Get the mini stream offset from root object @@ -56,12 +56,12 @@ namespace SabreTools.Serialization.Wrappers /// /// Normal sector size in bytes /// - public long SectorSize => (long)Math.Pow(2, Header?.SectorShift ?? 0); + public long SectorSize => (long)Math.Pow(2, Header.SectorShift); /// /// Mini sector size in bytes /// - public long MiniSectorSize => (long)Math.Pow(2, Header?.MiniSectorShift ?? 0); + public long MiniSectorSize => (long)Math.Pow(2, Header.MiniSectorShift); #endregion diff --git a/SabreTools.Serialization/Wrappers/GCF.cs b/SabreTools.Serialization/Wrappers/GCF.cs index d7c952d1..adaa4b17 100644 --- a/SabreTools.Serialization/Wrappers/GCF.cs +++ b/SabreTools.Serialization/Wrappers/GCF.cs @@ -24,7 +24,7 @@ namespace SabreTools.Serialization.Wrappers { get { - // Use the cached value if we have it + // Use the cached value, if it exists if (field != null) return field; @@ -48,14 +48,10 @@ namespace SabreTools.Serialization.Wrappers { get { - // Use the cached value if we have it + // Use the cached value, if it exists if (field != null) return field; - // If we don't have a required property - if (Model.DirectoryEntries == null || Model.DirectoryMapEntries == null) - return null; - // Otherwise, scan and build the files var files = new List(); for (int i = 0; i < Model.DirectoryEntries.Length; i++) @@ -82,7 +78,7 @@ namespace SabreTools.Serialization.Wrappers Encrypted = directoryEntry.DirectoryFlags.HasFlag(Data.Models.GCF.HL_GCF_FLAG.HL_GCF_FLAG_ENCRYPTED), #endif }; - var pathParts = new List { Model.DirectoryNames![directoryEntry.NameOffset] ?? string.Empty }; + var pathParts = new List { Model.DirectoryNames[directoryEntry.NameOffset] ?? string.Empty }; var blockEntries = new List(); // Traverse the parent tree @@ -90,7 +86,7 @@ namespace SabreTools.Serialization.Wrappers while (index != 0xFFFFFFFF) { var parentDirectoryEntry = Model.DirectoryEntries[index]; - pathParts.Add(Model.DirectoryNames![parentDirectoryEntry.NameOffset] ?? string.Empty); + pathParts.Add(Model.DirectoryNames[parentDirectoryEntry.NameOffset] ?? string.Empty); index = parentDirectoryEntry.ParentIndex; } diff --git a/SabreTools.Serialization/Wrappers/GZip.cs b/SabreTools.Serialization/Wrappers/GZip.cs index bc19274d..97e0f34b 100644 --- a/SabreTools.Serialization/Wrappers/GZip.cs +++ b/SabreTools.Serialization/Wrappers/GZip.cs @@ -27,11 +27,16 @@ namespace SabreTools.Serialization.Wrappers if (!IsTorrentGZip) return null; + // Use the cached value, if it exists + if (field != null) + return field; + // CRC-32 is the second packed field int extraIndex = 0x10; - return Header.ExtraFieldBytes.ReadBytes(ref extraIndex, 0x04); + field = Header.ExtraFieldBytes.ReadBytes(ref extraIndex, 0x04); + return field; } - } + } = null; /// /// Content MD5 as stored in the extra field @@ -45,11 +50,16 @@ namespace SabreTools.Serialization.Wrappers if (!IsTorrentGZip) return null; + // Use the cached value, if it exists + if (field != null) + return field; + // MD5 is the first packed field int extraIndex = 0x00; - return Header.ExtraFieldBytes.ReadBytes(ref extraIndex, 0x10); + field = Header.ExtraFieldBytes.ReadBytes(ref extraIndex, 0x10); + return field; } - } + } = null; /// /// Content size as stored in the extra field @@ -63,11 +73,16 @@ namespace SabreTools.Serialization.Wrappers if (!IsTorrentGZip) return 0; - // MD5 is the first packed field - int extraIndex = 0x00; - return Header.ExtraFieldBytes.ReadUInt64LittleEndian(ref extraIndex); + // Use the cached value, if it exists + if (field > 0) + return field; + + // Size is the third packed field + int extraIndex = 0x14; + field = Header.ExtraFieldBytes.ReadUInt64LittleEndian(ref extraIndex); + return field; } - } + } = 0; /// /// Offset to the compressed data @@ -77,6 +92,7 @@ namespace SabreTools.Serialization.Wrappers { get { + // Use the cached value, if it exists if (field > -1) return field; diff --git a/SabreTools.Serialization/Wrappers/LinearExecutable.cs b/SabreTools.Serialization/Wrappers/LinearExecutable.cs index 770ebcc0..c35ef588 100644 --- a/SabreTools.Serialization/Wrappers/LinearExecutable.cs +++ b/SabreTools.Serialization/Wrappers/LinearExecutable.cs @@ -17,16 +17,49 @@ namespace SabreTools.Serialization.Wrappers #region Extension Properties /// - public InformationBlock? InformationBlock => Model.InformationBlock; + public InformationBlock InformationBlock => Model.InformationBlock; /// - public ObjectPageMapEntry[]? ObjectPageMap => Model.ObjectPageMap; + public ObjectPageMapEntry[] ObjectPageMap => Model.ObjectPageMap; /// - public ResourceTableEntry[]? ResourceTable => Model.ResourceTable; + public ResourceTableEntry[] ResourceTable => Model.ResourceTable; /// - public Data.Models.MSDOS.Executable? Stub => Model.Stub; + public Data.Models.MSDOS.Executable Stub => Model.Stub; + + /// + /// Stub executable data, if it exists + /// + public byte[] StubExecutableData + { + get + { + lock (_stubExecutableDataLock) + { + // If we already have cached data, just use that immediately + if (field != null) + return field; + + // Populate the raw stub executable data based on the source + int endOfStubHeader = 0x40; + int lengthOfStubExecutableData = (int)Stub.Header.NewExeHeaderAddr - endOfStubHeader; + field = ReadRangeFromSource(endOfStubHeader, lengthOfStubExecutableData); + + // Cache and return the stub executable data, even if null + return field; + } + } + } = null; + + #endregion + + #region Instance Variables + + /// + /// Lock object for + /// + private readonly object _stubExecutableDataLock = new(); #endregion diff --git a/SabreTools.Serialization/Wrappers/PortableExecutable.cs b/SabreTools.Serialization/Wrappers/PortableExecutable.cs index e3675dc5..37885348 100644 --- a/SabreTools.Serialization/Wrappers/PortableExecutable.cs +++ b/SabreTools.Serialization/Wrappers/PortableExecutable.cs @@ -34,19 +34,19 @@ namespace SabreTools.Serialization.Wrappers lock (_debugDataLock) { // Use the cached data if possible - if (_debugData.Count != 0) - return _debugData; + if (field.Count != 0) + return field; // If we have no resource table, just return if (DebugDirectoryTable == null || DebugDirectoryTable.Length == 0) - return _debugData; + return field; // Otherwise, build and return the cached dictionary - ParseDebugTable(); - return _debugData; + field = ParseDebugTable(); + return field; } } - } + } = []; /// public Data.Models.PortableExecutable.DebugData.Entry[]? DebugDirectoryTable @@ -822,12 +822,7 @@ namespace SabreTools.Serialization.Wrappers #region Instance Variables /// - /// Cached debug data - /// - private readonly Dictionary _debugData = []; - - /// - /// Lock object for + /// Lock object for /// private readonly object _debugDataLock = new(); @@ -1200,11 +1195,14 @@ namespace SabreTools.Serialization.Wrappers /// /// Parse the debug directory table information /// - private void ParseDebugTable() + private Dictionary ParseDebugTable() { // If there is no debug table if (DebugDirectoryTable == null || DebugDirectoryTable.Length == 0) - return; + return []; + + // Create a new debug table + Dictionary debugData = []; // Loop through all debug table entries for (int i = 0; i < DebugDirectoryTable.Length; i++) @@ -1223,7 +1221,7 @@ namespace SabreTools.Serialization.Wrappers } catch (EndOfStreamException) { - return; + return debugData; } // If we have CodeView debug data, try to parse it @@ -1242,7 +1240,7 @@ namespace SabreTools.Serialization.Wrappers var nb10ProgramDatabase = entryData.ParseNB10ProgramDatabase(ref offset); if (nb10ProgramDatabase != null) { - _debugData[i] = nb10ProgramDatabase; + debugData[i] = nb10ProgramDatabase; continue; } } @@ -1253,16 +1251,18 @@ namespace SabreTools.Serialization.Wrappers var rsdsProgramDatabase = entryData.ParseRSDSProgramDatabase(ref offset); if (rsdsProgramDatabase != null) { - _debugData[i] = rsdsProgramDatabase; + debugData[i] = rsdsProgramDatabase; continue; } } } else { - _debugData[i] = entryData; + debugData[i] = entryData; } } + + return debugData; } #endregion diff --git a/SabreTools.Serialization/Wrappers/VPK.cs b/SabreTools.Serialization/Wrappers/VPK.cs index bf3768fa..a5f11a01 100644 --- a/SabreTools.Serialization/Wrappers/VPK.cs +++ b/SabreTools.Serialization/Wrappers/VPK.cs @@ -22,7 +22,7 @@ namespace SabreTools.Serialization.Wrappers { get { - // Use the cached value if we have it + // Use the cached value, if it exists if (field != null) return field;