From 738a1d250af4e3c74aa356c5569d2ca6dddf6f65 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Fri, 5 Sep 2025 07:36:15 -0400 Subject: [PATCH] Add inherent locking the the data source in wrappers --- SabreTools.Serialization/Wrappers/BFPK.cs | 2 +- SabreTools.Serialization/Wrappers/BSP.cs | 2 +- SabreTools.Serialization/Wrappers/CFB.cs | 2 +- SabreTools.Serialization/Wrappers/GCF.cs | 2 +- .../Wrappers/InstallShieldArchiveV3.cs | 2 +- SabreTools.Serialization/Wrappers/LZKWAJ.cs | 2 +- SabreTools.Serialization/Wrappers/LZQBasic.cs | 2 +- SabreTools.Serialization/Wrappers/LZSZDD.cs | 2 +- .../Wrappers/LinearExecutable.cs | 4 +- .../Wrappers/NewExecutable.cs | 114 ++++++-------- SabreTools.Serialization/Wrappers/PAK.cs | 2 +- SabreTools.Serialization/Wrappers/PFF.cs | 2 +- .../Wrappers/PortableExecutable.cs | 149 +++++++----------- SabreTools.Serialization/Wrappers/Quantum.cs | 2 +- SabreTools.Serialization/Wrappers/SGA.cs | 2 +- SabreTools.Serialization/Wrappers/VBSP.cs | 2 +- SabreTools.Serialization/Wrappers/WAD3.cs | 2 +- .../Wrappers/WrapperBase.cs | 33 ++++ SabreTools.Serialization/Wrappers/XZP.cs | 2 +- 19 files changed, 160 insertions(+), 170 deletions(-) diff --git a/SabreTools.Serialization/Wrappers/BFPK.cs b/SabreTools.Serialization/Wrappers/BFPK.cs index 2cb051ef..bcad47a9 100644 --- a/SabreTools.Serialization/Wrappers/BFPK.cs +++ b/SabreTools.Serialization/Wrappers/BFPK.cs @@ -167,7 +167,7 @@ namespace SabreTools.Serialization.Wrappers using FileStream fs = File.OpenWrite(filename); // Read the data block - var data = _dataSource.ReadFrom(offset, compressedSize, retainPosition: true); + var data = ReadRangeFromSource(offset, compressedSize); if (data == null) return false; diff --git a/SabreTools.Serialization/Wrappers/BSP.cs b/SabreTools.Serialization/Wrappers/BSP.cs index c7d125e3..d8def264 100644 --- a/SabreTools.Serialization/Wrappers/BSP.cs +++ b/SabreTools.Serialization/Wrappers/BSP.cs @@ -128,7 +128,7 @@ namespace SabreTools.Serialization.Wrappers // Read the data var lump = Lumps[index]; - var data = _dataSource.ReadFrom(lump.Offset, lump.Length, retainPosition: true); + var data = ReadRangeFromSource(lump.Offset, lump.Length); if (data == null) return false; diff --git a/SabreTools.Serialization/Wrappers/CFB.cs b/SabreTools.Serialization/Wrappers/CFB.cs index a988b59c..9f2e59d9 100644 --- a/SabreTools.Serialization/Wrappers/CFB.cs +++ b/SabreTools.Serialization/Wrappers/CFB.cs @@ -322,7 +322,7 @@ namespace SabreTools.Serialization.Wrappers return null; // Try to read the sector data - var sectorData = _dataSource.ReadFrom(sectorDataOffset, (int)SectorSize, retainPosition: true); + var sectorData = ReadRangeFromSource(sectorDataOffset, (int)SectorSize); if (sectorData == null) return null; diff --git a/SabreTools.Serialization/Wrappers/GCF.cs b/SabreTools.Serialization/Wrappers/GCF.cs index 47dc5f3c..293550a4 100644 --- a/SabreTools.Serialization/Wrappers/GCF.cs +++ b/SabreTools.Serialization/Wrappers/GCF.cs @@ -321,7 +321,7 @@ namespace SabreTools.Serialization.Wrappers for (int i = 0; i < dataBlockOffsets.Count; i++) { int readSize = (int)Math.Min(BlockSize, fileSize); - var data = _dataSource.ReadFrom((int)dataBlockOffsets[i], readSize, retainPosition: true); + var data = ReadRangeFromSource((int)dataBlockOffsets[i], readSize); if (data == null) return false; diff --git a/SabreTools.Serialization/Wrappers/InstallShieldArchiveV3.cs b/SabreTools.Serialization/Wrappers/InstallShieldArchiveV3.cs index 6354fa56..5d27a75b 100644 --- a/SabreTools.Serialization/Wrappers/InstallShieldArchiveV3.cs +++ b/SabreTools.Serialization/Wrappers/InstallShieldArchiveV3.cs @@ -240,7 +240,7 @@ namespace SabreTools.Serialization.Wrappers long outputFileSize = file.UncompressedSize; // Read the compressed data directly - var compressedData = _dataSource.ReadFrom((int)fileOffset, (int)fileSize, retainPosition: true); + var compressedData = ReadRangeFromSource((int)fileOffset, (int)fileSize); if (compressedData == null) return false; diff --git a/SabreTools.Serialization/Wrappers/LZKWAJ.cs b/SabreTools.Serialization/Wrappers/LZKWAJ.cs index a6f1230a..aed3bcd7 100644 --- a/SabreTools.Serialization/Wrappers/LZKWAJ.cs +++ b/SabreTools.Serialization/Wrappers/LZKWAJ.cs @@ -111,7 +111,7 @@ namespace SabreTools.Serialization.Wrappers return false; // Read in the data as an array - byte[]? contents = _dataSource.ReadFrom(DataOffset, (int)compressedSize, retainPosition: true); + byte[]? contents = ReadRangeFromSource(DataOffset, (int)compressedSize); if (contents == null) return false; diff --git a/SabreTools.Serialization/Wrappers/LZQBasic.cs b/SabreTools.Serialization/Wrappers/LZQBasic.cs index dd0416a6..30c30360 100644 --- a/SabreTools.Serialization/Wrappers/LZQBasic.cs +++ b/SabreTools.Serialization/Wrappers/LZQBasic.cs @@ -95,7 +95,7 @@ namespace SabreTools.Serialization.Wrappers return false; // Read in the data as an array - byte[]? contents = _dataSource.ReadFrom(12, (int)compressedSize, retainPosition: true); + byte[]? contents = ReadRangeFromSource(12, (int)compressedSize); if (contents == null) return false; diff --git a/SabreTools.Serialization/Wrappers/LZSZDD.cs b/SabreTools.Serialization/Wrappers/LZSZDD.cs index 28749867..9973c879 100644 --- a/SabreTools.Serialization/Wrappers/LZSZDD.cs +++ b/SabreTools.Serialization/Wrappers/LZSZDD.cs @@ -111,7 +111,7 @@ namespace SabreTools.Serialization.Wrappers return false; // Read in the data as an array - byte[]? contents = _dataSource.ReadFrom(14, (int)compressedSize, retainPosition: true); + byte[]? contents = ReadRangeFromSource(14, (int)compressedSize); if (contents == null) return false; diff --git a/SabreTools.Serialization/Wrappers/LinearExecutable.cs b/SabreTools.Serialization/Wrappers/LinearExecutable.cs index a894d134..ff6ff533 100644 --- a/SabreTools.Serialization/Wrappers/LinearExecutable.cs +++ b/SabreTools.Serialization/Wrappers/LinearExecutable.cs @@ -139,7 +139,7 @@ namespace SabreTools.Serialization.Wrappers return []; // Read the entry data and return - return _dataSource.ReadFrom(offset, length, retainPosition: true); + return ReadRangeFromSource(offset, length); } /// @@ -315,7 +315,7 @@ namespace SabreTools.Serialization.Wrappers if (length == -1) length = Length; - return _dataSource.ReadFrom(rangeStart, (int)length, retainPosition: true); + return ReadRangeFromSource(rangeStart, (int)length); } #endregion diff --git a/SabreTools.Serialization/Wrappers/NewExecutable.cs b/SabreTools.Serialization/Wrappers/NewExecutable.cs index 76915b9b..6f2a3af2 100644 --- a/SabreTools.Serialization/Wrappers/NewExecutable.cs +++ b/SabreTools.Serialization/Wrappers/NewExecutable.cs @@ -51,59 +51,56 @@ namespace SabreTools.Serialization.Wrappers if (Header == null || SegmentTable == null || ResourceTable?.ResourceTypes == null) return -1; - lock (_sourceDataLock) + // Search through the segments table to find the furthest + long endOfSectionData = -1; + foreach (var entry in SegmentTable) { - // Search through the segments table to find the furthest - long endOfSectionData = -1; - foreach (var entry in SegmentTable) - { - // Get end of segment data - long offset = (entry.Offset * (1 << Header.SegmentAlignmentShiftCount)) + entry.Length; + // Get end of segment data + long offset = (entry.Offset * (1 << Header.SegmentAlignmentShiftCount)) + entry.Length; - // Read and find the end of the relocation data + // Read and find the end of the relocation data #if NET20 || NET35 if ((entry.FlagWord & SegmentTableEntryFlag.RELOCINFO) != 0) #else - if (entry.FlagWord.HasFlag(SegmentTableEntryFlag.RELOCINFO)) + if (entry.FlagWord.HasFlag(SegmentTableEntryFlag.RELOCINFO)) #endif - { - _dataSource.Seek(offset, SeekOrigin.Begin); - var relocationData = Deserializers.NewExecutable.ParsePerSegmentData(_dataSource); + { + _dataSource.Seek(offset, SeekOrigin.Begin); + var relocationData = Deserializers.NewExecutable.ParsePerSegmentData(_dataSource); - offset = _dataSource.Position; - } + offset = _dataSource.Position; + } + if (offset > endOfSectionData) + endOfSectionData = offset; + } + + // Search through the resources table to find the furthest + foreach (var entry in ResourceTable.ResourceTypes) + { + // Skip invalid entries + if (entry.ResourceCount == 0 || entry.Resources == null || entry.Resources.Length == 0) + continue; + + foreach (var resource in entry.Resources) + { + int offset = (resource.Offset << ResourceTable.AlignmentShiftCount) + resource.Length; if (offset > endOfSectionData) endOfSectionData = offset; } - - // Search through the resources table to find the furthest - foreach (var entry in ResourceTable.ResourceTypes) - { - // Skip invalid entries - if (entry.ResourceCount == 0 || entry.Resources == null || entry.Resources.Length == 0) - continue; - - foreach (var resource in entry.Resources) - { - int offset = (resource.Offset << ResourceTable.AlignmentShiftCount) + resource.Length; - if (offset > endOfSectionData) - endOfSectionData = offset; - } - } - - // If we didn't find the end of section data - if (endOfSectionData <= 0) - endOfSectionData = -1; - - // Adjust the position of the data by 705 bytes - // TODO: Investigate what the byte data is - endOfSectionData += 705; - - // Cache and return the position - _overlayAddress = endOfSectionData; - return _overlayAddress.Value; } + + // If we didn't find the end of section data + if (endOfSectionData <= 0) + endOfSectionData = -1; + + // Adjust the position of the data by 705 bytes + // TODO: Investigate what the byte data is + endOfSectionData += 705; + + // Cache and return the position + _overlayAddress = endOfSectionData; + return _overlayAddress.Value; } } } @@ -146,12 +143,9 @@ namespace SabreTools.Serialization.Wrappers } // Otherwise, cache and return the data - lock (_sourceDataLock) - { - long overlayLength = dataLength - endOfSectionData; - _overlayData = _dataSource.ReadFrom((int)endOfSectionData, (int)overlayLength, retainPosition: true); - return _overlayData; - } + long overlayLength = dataLength - endOfSectionData; + _overlayData = ReadRangeFromSource((int)endOfSectionData, (int)overlayLength); + return _overlayData; } } } @@ -221,16 +215,13 @@ namespace SabreTools.Serialization.Wrappers if (Stub?.Header?.NewExeHeaderAddr == null) return null; - lock (_sourceDataLock) - { - // Populate the raw stub executable data based on the source - int endOfStubHeader = 0x40; - int lengthOfStubExecutableData = (int)Stub.Header.NewExeHeaderAddr - endOfStubHeader; - _stubExecutableData = _dataSource.ReadFrom(endOfStubHeader, lengthOfStubExecutableData, retainPosition: true); + // Populate the raw stub executable data based on the source + int endOfStubHeader = 0x40; + int lengthOfStubExecutableData = (int)Stub.Header.NewExeHeaderAddr - endOfStubHeader; + _stubExecutableData = ReadRangeFromSource(endOfStubHeader, lengthOfStubExecutableData); - // Cache and return the stub executable data, even if null - return _stubExecutableData; - } + // Cache and return the stub executable data, even if null + return _stubExecutableData; } } } @@ -279,11 +270,6 @@ namespace SabreTools.Serialization.Wrappers /// private readonly object _stubExecutableDataLock = new(); - /// - /// Lock object for reading from the source - /// - private readonly object _sourceDataLock = new(); - #endregion #region Constructors @@ -654,7 +640,7 @@ namespace SabreTools.Serialization.Wrappers return []; // Read the resource data and return - return _dataSource.ReadFrom(offset, length, retainPosition: true); + return ReadRangeFromSource(offset, length); } /// @@ -746,7 +732,7 @@ namespace SabreTools.Serialization.Wrappers return []; // Read the segment data and return - return _dataSource.ReadFrom(offset, length, retainPosition: true); + return ReadRangeFromSource(offset, length); } /// @@ -816,7 +802,7 @@ namespace SabreTools.Serialization.Wrappers if (length == -1) length = Length; - return _dataSource.ReadFrom(rangeStart, (int)length, retainPosition: true); + return ReadRangeFromSource(rangeStart, (int)length); } #endregion diff --git a/SabreTools.Serialization/Wrappers/PAK.cs b/SabreTools.Serialization/Wrappers/PAK.cs index d2244028..4b9d7206 100644 --- a/SabreTools.Serialization/Wrappers/PAK.cs +++ b/SabreTools.Serialization/Wrappers/PAK.cs @@ -128,7 +128,7 @@ namespace SabreTools.Serialization.Wrappers // Read the item data var directoryItem = DirectoryItems[index]; - var data = _dataSource.ReadFrom((int)directoryItem.ItemOffset, (int)directoryItem.ItemLength, retainPosition: true); + var data = ReadRangeFromSource((int)directoryItem.ItemOffset, (int)directoryItem.ItemLength); if (data == null) return false; diff --git a/SabreTools.Serialization/Wrappers/PFF.cs b/SabreTools.Serialization/Wrappers/PFF.cs index 1ba60192..c0a44adf 100644 --- a/SabreTools.Serialization/Wrappers/PFF.cs +++ b/SabreTools.Serialization/Wrappers/PFF.cs @@ -159,7 +159,7 @@ namespace SabreTools.Serialization.Wrappers using FileStream fs = File.OpenWrite(filename); // Read the data block - var data = _dataSource.ReadFrom(offset, size, retainPosition: true); + var data = ReadRangeFromSource(offset, size); if (data == null) return false; diff --git a/SabreTools.Serialization/Wrappers/PortableExecutable.cs b/SabreTools.Serialization/Wrappers/PortableExecutable.cs index 5703c0a8..0326eb42 100644 --- a/SabreTools.Serialization/Wrappers/PortableExecutable.cs +++ b/SabreTools.Serialization/Wrappers/PortableExecutable.cs @@ -42,11 +42,8 @@ namespace SabreTools.Serialization.Wrappers return null; // Otherwise, build and return the cached dictionary - lock (_sourceDataLock) - { - ParseDebugTable(); - return _debugData; - } + ParseDebugTable(); + return _debugData; } } } @@ -90,11 +87,8 @@ namespace SabreTools.Serialization.Wrappers } // Read the first 128 bytes of the entry point - lock (_sourceDataLock) - { - _entryPointData = _dataSource.ReadFrom(entryPointAddress, length: 128, retainPosition: true); - return _entryPointData; - } + _entryPointData = ReadRangeFromSource(entryPointAddress, length: 128); + return _entryPointData; } } } @@ -145,11 +139,8 @@ namespace SabreTools.Serialization.Wrappers return _headerPaddingData; } - lock (_sourceDataLock) - { - _headerPaddingData = _dataSource.ReadFrom((int)headerStartAddress, headerLength, retainPosition: true); - return _headerPaddingData; - } + _headerPaddingData = ReadRangeFromSource((int)headerStartAddress, headerLength); + return _headerPaddingData; } } } @@ -197,11 +188,8 @@ namespace SabreTools.Serialization.Wrappers return _headerPaddingStrings; } - lock (_sourceDataLock) - { - _headerPaddingStrings = _dataSource.ReadStringsFrom((int)headerStartAddress, headerLength, charLimit: 3); - return _headerPaddingStrings; - } + _headerPaddingStrings = _dataSource.ReadStringsFrom((int)headerStartAddress, headerLength, charLimit: 3); + return _headerPaddingStrings; } } } @@ -321,12 +309,9 @@ namespace SabreTools.Serialization.Wrappers } // Otherwise, cache and return the data - lock (_sourceDataLock) - { - long overlayLength = dataLength - endOfSectionData; - _overlayData = _dataSource.ReadFrom(endOfSectionData, (int)overlayLength, retainPosition: true); - return _overlayData; - } + long overlayLength = dataLength - endOfSectionData; + _overlayData = ReadRangeFromSource(endOfSectionData, (int)overlayLength); + return _overlayData; } } } @@ -432,16 +417,13 @@ namespace SabreTools.Serialization.Wrappers if (Stub?.Header?.NewExeHeaderAddr == null) return null; - lock (_sourceDataLock) - { - // Populate the raw stub executable data based on the source - int endOfStubHeader = 0x40; - int lengthOfStubExecutableData = (int)Stub.Header.NewExeHeaderAddr - endOfStubHeader; - _stubExecutableData = _dataSource.ReadFrom(endOfStubHeader, lengthOfStubExecutableData, retainPosition: true); + // Populate the raw stub executable data based on the source + int endOfStubHeader = 0x40; + int lengthOfStubExecutableData = (int)Stub.Header.NewExeHeaderAddr - endOfStubHeader; + _stubExecutableData = ReadRangeFromSource(endOfStubHeader, lengthOfStubExecutableData); - // Cache and return the stub executable data, even if null - return _stubExecutableData; - } + // Cache and return the stub executable data, even if null + return _stubExecutableData; } } } @@ -777,11 +759,6 @@ namespace SabreTools.Serialization.Wrappers /// private AssemblyManifest? _assemblyManifest = null; - /// - /// Lock object for reading from the source - /// - private readonly object _sourceDataLock = new(); - #endregion #region Constructors @@ -1035,7 +1012,7 @@ namespace SabreTools.Serialization.Wrappers byte[]? entryData; try { - entryData = _dataSource.ReadFrom((int)address, (int)size, retainPosition: true); + entryData = ReadRangeFromSource((int)address, (int)size); if (entryData == null || entryData.Length < 4) continue; } @@ -1545,7 +1522,11 @@ namespace SabreTools.Serialization.Wrappers // Read the section into a local array int sectionLength = (int)section.VirtualSize; - byte[]? sectionData = source.ReadFrom(offset, sectionLength, retainPosition: true); + byte[]? sectionData; + lock (source) + { + sectionData = source.ReadFrom(offset, sectionLength, retainPosition: true); + } // Parse the section header var header = WiseSectionHeader.Create(sectionData, 0); @@ -2209,22 +2190,20 @@ namespace SabreTools.Serialization.Wrappers // Set the section size uint size = section.SizeOfRawData; - lock (_sourceDataLock) - { - // Create the section data array if we have to - _sectionData ??= new byte[SectionNames.Length][]; - // If we already have cached data, just use that immediately - if (_sectionData[index] != null && _sectionData[index].Length > 0) - return _sectionData[index]; + // Create the section data array if we have to + _sectionData ??= new byte[SectionNames.Length][]; - // Populate the raw section data based on the source - byte[]? sectionData = _dataSource.ReadFrom((int)address, (int)size, retainPosition: true); + // If we already have cached data, just use that immediately + if (_sectionData[index] != null && _sectionData[index].Length > 0) + return _sectionData[index]; - // Cache and return the section data, even if null - _sectionData[index] = sectionData ?? []; - return sectionData; - } + // Populate the raw section data based on the source + byte[]? sectionData = ReadRangeFromSource((int)address, (int)size); + + // Cache and return the section data, even if null + _sectionData[index] = sectionData ?? []; + return sectionData; } /// @@ -2304,15 +2283,13 @@ namespace SabreTools.Serialization.Wrappers // Set the section size uint size = section.SizeOfRawData; - lock (_sourceDataLock) - { - // Populate the section string data based on the source - List? sectionStringData = _dataSource.ReadStringsFrom((int)address, (int)size); - // Cache and return the section string data, even if null - _sectionStringData[index] = sectionStringData ?? []; - return sectionStringData; - } + // Populate the section string data based on the source + List? sectionStringData = _dataSource.ReadStringsFrom((int)address, (int)size); + + // Cache and return the section string data, even if null + _sectionStringData[index] = sectionStringData ?? []; + return sectionStringData; } } @@ -2382,22 +2359,19 @@ namespace SabreTools.Serialization.Wrappers if (address == 0 || size == 0) return null; - lock (_sourceDataLock) - { - // Create the table data array if we have to - _tableData ??= new byte[16][]; + // Create the table data array if we have to + _tableData ??= new byte[16][]; - // If we already have cached data, just use that immediately - if (_tableData[index] != null && _tableData[index].Length > 0) - return _tableData[index]; + // If we already have cached data, just use that immediately + if (_tableData[index] != null && _tableData[index].Length > 0) + return _tableData[index]; - // Populate the raw table data based on the source - byte[]? tableData = _dataSource.ReadFrom((int)address, (int)size, retainPosition: true); + // Populate the raw table data based on the source + byte[]? tableData = ReadRangeFromSource((int)address, (int)size); - // Cache and return the table data, even if null - _tableData[index] = tableData ?? []; - return tableData; - } + // Cache and return the table data, even if null + _tableData[index] = tableData ?? []; + return tableData; } /// @@ -2427,22 +2401,19 @@ namespace SabreTools.Serialization.Wrappers if (address == 0 || size == 0) return null; - lock (_sourceDataLock) - { - // Create the table string array if we have to - _tableStringData ??= new List[16]; + // Create the table string array if we have to + _tableStringData ??= new List[16]; - // If we already have cached data, just use that immediately - if (_tableStringData[index] != null && _tableStringData[index].Count > 0) - return _tableStringData[index]; + // If we already have cached data, just use that immediately + if (_tableStringData[index] != null && _tableStringData[index].Count > 0) + return _tableStringData[index]; - // Populate the table string data based on the source - List? tableStringData = _dataSource.ReadStringsFrom((int)address, (int)size); + // Populate the table string data based on the source + List? tableStringData = _dataSource.ReadStringsFrom((int)address, (int)size); - // Cache and return the table string data, even if null - _tableStringData[index] = tableStringData ?? []; - return tableStringData; - } + // Cache and return the table string data, even if null + _tableStringData[index] = tableStringData ?? []; + return tableStringData; } #endregion diff --git a/SabreTools.Serialization/Wrappers/Quantum.cs b/SabreTools.Serialization/Wrappers/Quantum.cs index b78f9777..f8660cdc 100644 --- a/SabreTools.Serialization/Wrappers/Quantum.cs +++ b/SabreTools.Serialization/Wrappers/Quantum.cs @@ -141,7 +141,7 @@ namespace SabreTools.Serialization.Wrappers // Read the entire compressed data int compressedDataOffset = (int)CompressedDataOffset; long compressedDataLength = Length - compressedDataOffset; - var compressedData = _dataSource.ReadFrom(compressedDataOffset, (int)compressedDataLength, retainPosition: true); + var compressedData = ReadRangeFromSource(compressedDataOffset, (int)compressedDataLength); // Print a debug reminder if (includeDebug) Console.WriteLine("Quantum archive extraction is unsupported"); diff --git a/SabreTools.Serialization/Wrappers/SGA.cs b/SabreTools.Serialization/Wrappers/SGA.cs index de59af2e..e8cf7776 100644 --- a/SabreTools.Serialization/Wrappers/SGA.cs +++ b/SabreTools.Serialization/Wrappers/SGA.cs @@ -204,7 +204,7 @@ namespace SabreTools.Serialization.Wrappers long outputFileSize = GetUncompressedSize(index); // Read the compressed data directly - var compressedData = _dataSource.ReadFrom((int)fileOffset, (int)fileSize, retainPosition: true); + var compressedData = ReadRangeFromSource((int)fileOffset, (int)fileSize); if (compressedData == null) return false; diff --git a/SabreTools.Serialization/Wrappers/VBSP.cs b/SabreTools.Serialization/Wrappers/VBSP.cs index 8fc16853..d9c79a47 100644 --- a/SabreTools.Serialization/Wrappers/VBSP.cs +++ b/SabreTools.Serialization/Wrappers/VBSP.cs @@ -128,7 +128,7 @@ namespace SabreTools.Serialization.Wrappers // Read the data var lump = Lumps[index]; - var data = _dataSource.ReadFrom(lump.Offset, lump.Length, retainPosition: true); + var data = ReadRangeFromSource(lump.Offset, lump.Length); if (data == null) return false; diff --git a/SabreTools.Serialization/Wrappers/WAD3.cs b/SabreTools.Serialization/Wrappers/WAD3.cs index ac562c36..6b625a8a 100644 --- a/SabreTools.Serialization/Wrappers/WAD3.cs +++ b/SabreTools.Serialization/Wrappers/WAD3.cs @@ -127,7 +127,7 @@ namespace SabreTools.Serialization.Wrappers // Read the data -- TODO: Handle uncompressed lumps (see BSP.ExtractTexture) var lump = DirEntries[index]; - var data = _dataSource.ReadFrom((int)lump.Offset, (int)lump.Length, retainPosition: true); + var data = ReadRangeFromSource((int)lump.Offset, (int)lump.Length); if (data == null) return false; diff --git a/SabreTools.Serialization/Wrappers/WrapperBase.cs b/SabreTools.Serialization/Wrappers/WrapperBase.cs index ea9746de..eb20a890 100644 --- a/SabreTools.Serialization/Wrappers/WrapperBase.cs +++ b/SabreTools.Serialization/Wrappers/WrapperBase.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using SabreTools.IO.Extensions; using SabreTools.IO.Streams; using SabreTools.Serialization.Interfaces; @@ -57,6 +58,11 @@ namespace SabreTools.Serialization.Wrappers } #endif + /// + /// Lock for accessing + /// + protected readonly object _dataSourceLock = new(); + #endregion #region Constructors @@ -89,6 +95,33 @@ namespace SabreTools.Serialization.Wrappers #endregion + #region Data + + /// + /// Read a number of bytes from an offset fomr the data source, if possible + /// + /// Offset within the data source to start reading + /// Number of bytes to read from the offset + /// Filled byte array on success, null on error + /// + /// This method locks the data source to avoid potential conflicts in reading + /// from the data source. This should be the preferred way of reading in cases + /// where there may be multiple threads accessing the wrapper. + /// + /// This method will return a null array if the length is greater than what is left + /// in the stream. This is different behavior than a normal stream read that would + /// attempt to read as much as possible, returning the amount of bytes read. + /// + protected byte[]? ReadRangeFromSource(long offset, int length) + { + lock (_dataSourceLock) + { + return _dataSource.ReadFrom(offset, length, retainPosition: true); + } + } + + #endregion + #region JSON Export #if NETCOREAPP diff --git a/SabreTools.Serialization/Wrappers/XZP.cs b/SabreTools.Serialization/Wrappers/XZP.cs index 4f1b9805..ae557494 100644 --- a/SabreTools.Serialization/Wrappers/XZP.cs +++ b/SabreTools.Serialization/Wrappers/XZP.cs @@ -139,7 +139,7 @@ namespace SabreTools.Serialization.Wrappers return false; // Load the item data - var data = _dataSource.ReadFrom((int)directoryEntry.EntryOffset, (int)directoryEntry.EntryLength, retainPosition: true); + var data = ReadRangeFromSource((int)directoryEntry.EntryOffset, (int)directoryEntry.EntryLength); if (data == null) return false;