Add inherent locking the the data source in wrappers

This commit is contained in:
Matt Nadareski
2025-09-05 07:36:15 -04:00
parent c8e65e1e30
commit 738a1d250a
19 changed files with 160 additions and 170 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
/// <summary>
@@ -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

View File

@@ -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
/// </summary>
private readonly object _stubExecutableDataLock = new();
/// <summary>
/// Lock object for reading from the source
/// </summary>
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);
}
/// <summary>
@@ -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);
}
/// <summary>
@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
/// </summary>
private AssemblyManifest? _assemblyManifest = null;
/// <summary>
/// Lock object for reading from the source
/// </summary>
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;
}
/// <summary>
@@ -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<string>? 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<string>? 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;
}
/// <summary>
@@ -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<string>[16];
// Create the table string array if we have to
_tableStringData ??= new List<string>[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<string>? tableStringData = _dataSource.ReadStringsFrom((int)address, (int)size);
// Populate the table string data based on the source
List<string>? 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

View File

@@ -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");

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
/// <summary>
/// Lock for accessing <see cref="_dataSource"/>
/// </summary>
protected readonly object _dataSourceLock = new();
#endregion
#region Constructors
@@ -89,6 +95,33 @@ namespace SabreTools.Serialization.Wrappers
#endregion
#region Data
/// <summary>
/// Read a number of bytes from an offset fomr the data source, if possible
/// </summary>
/// <param name="offset">Offset within the data source to start reading</param>
/// <param name="length">Number of bytes to read from the offset</param>
/// <returns>Filled byte array on success, null on error</returns>
/// <remarks>
/// 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.
/// </remarks>
protected byte[]? ReadRangeFromSource(long offset, int length)
{
lock (_dataSourceLock)
{
return _dataSource.ReadFrom(offset, length, retainPosition: true);
}
}
#endregion
#region JSON Export
#if NETCOREAPP

View File

@@ -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;