Compare commits

..

8 Commits
1.9.0 ... 1.9.1

Author SHA1 Message Date
Matt Nadareski
ce016c5eb0 Bump version 2025-09-06 08:18:43 -04:00
Matt Nadareski
2225c1f2d8 Update Nuget packages 2025-09-05 10:57:14 -04:00
Matt Nadareski
2d0c0d5845 Make a bunch of things cache more safer 2025-09-05 08:32:40 -04:00
Matt Nadareski
60f1756cbb Wrap places where ReadFrom was not being used but still could be parallel 2025-09-05 07:45:55 -04:00
Matt Nadareski
738a1d250a Add inherent locking the the data source in wrappers 2025-09-05 07:36:15 -04:00
Matt Nadareski
c8e65e1e30 Add section string lock 2025-09-03 13:46:12 -04:00
Matt Nadareski
ecb09ce6f2 Make sure source data isn't locked unnecessarily 2025-09-02 23:56:29 -04:00
Matt Nadareski
72a1484a71 More granular locks 2025-09-02 23:51:02 -04:00
29 changed files with 624 additions and 647 deletions

View File

@@ -10,7 +10,7 @@
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.9.0</Version>
<Version>1.9.1</Version>
</PropertyGroup>
<!-- Support All Frameworks -->
@@ -66,7 +66,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.IO" Version="1.7.1" />
<PackageReference Include="SabreTools.IO" Version="1.7.2" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.8" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
</ItemGroup>

View File

@@ -10,7 +10,7 @@
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.9.0</Version>
<Version>1.9.1</Version>
</PropertyGroup>
<!-- Support All Frameworks -->
@@ -32,7 +32,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.IO" Version="1.7.1" />
<PackageReference Include="SabreTools.IO" Version="1.7.2" />
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
</ItemGroup>

View File

@@ -28,7 +28,7 @@
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
<PackageReference Include="SabreTools.Models" Version="1.7.0" />
<PackageReference Include="SabreTools.Models" Version="1.7.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@@ -45,7 +45,7 @@ namespace SabreTools.Serialization.Deserializers
{
// Central Directory File Header
case CentralDirectoryFileHeaderSignature:
var cdr = ParseCentralDirectoryFileHeader(data, out _);
var cdr = ParseCentralDirectoryFileHeader(data);
if (cdr == null)
return null;
@@ -170,10 +170,9 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled central directory file header on success, null on error</returns>
public static CentralDirectoryFileHeader? ParseCentralDirectoryFileHeader(Stream data, out ExtensibleDataField[]? extraFields)
public static CentralDirectoryFileHeader? ParseCentralDirectoryFileHeader(Stream data)
{
var obj = new CentralDirectoryFileHeader();
extraFields = null;
obj.Signature = data.ReadUInt32LittleEndian();
if (obj.Signature != CentralDirectoryFileHeaderSignature)
@@ -220,8 +219,7 @@ namespace SabreTools.Serialization.Deserializers
if (extraBytes.Length != obj.ExtraFieldLength)
return null;
// TODO: This should live on the model instead of the byte representation
extraFields = ParseExtraFields(obj, extraBytes);
obj.ExtraFields = ParseExtraFields(obj, extraBytes);
}
if (obj.FileCommentLength > 0 && data.Position + obj.FileCommentLength <= data.Length)
{
@@ -416,7 +414,7 @@ namespace SabreTools.Serialization.Deserializers
#region Local File Header
// Try to read the header
var localFileHeader = ParseLocalFileHeader(data, out var extraFields);
var localFileHeader = ParseLocalFileHeader(data);
if (localFileHeader == null)
return null;
@@ -424,9 +422,9 @@ namespace SabreTools.Serialization.Deserializers
obj.LocalFileHeader = localFileHeader;
ulong compressedSize = localFileHeader.CompressedSize;
if (extraFields != null)
if (localFileHeader.ExtraFields != null)
{
foreach (var field in extraFields)
foreach (var field in localFileHeader.ExtraFields)
{
if (field is not Zip64ExtendedInformationExtraField infoField)
continue;
@@ -532,10 +530,9 @@ namespace SabreTools.Serialization.Deserializers
/// </summary>
/// <param name="data">Stream to parse</param>
/// <returns>Filled local file header on success, null on error</returns>
public static LocalFileHeader? ParseLocalFileHeader(Stream data, out ExtensibleDataField[]? extraFields)
public static LocalFileHeader? ParseLocalFileHeader(Stream data)
{
var obj = new LocalFileHeader();
extraFields = null;
obj.Signature = data.ReadUInt32LittleEndian();
if (obj.Signature != LocalFileHeaderSignature)
@@ -575,8 +572,7 @@ namespace SabreTools.Serialization.Deserializers
if (extraBytes.Length != obj.ExtraFieldLength)
return null;
// TODO: This should live on the model instead of the byte representation
extraFields = ParseExtraFields(obj, extraBytes);
obj.ExtraFields = ParseExtraFields(obj, extraBytes);
}
return obj;

View File

@@ -381,10 +381,10 @@ namespace SabreTools.Serialization.Deserializers
{
var obj = new CLRTokenDefinition();
obj.AuxFormat6AuxType = data.ReadByteValue();
obj.AuxFormat6Reserved1 = data.ReadByteValue();
obj.AuxFormat6SymbolTableIndex = data.ReadUInt32LittleEndian();
obj.AuxFormat6Reserved2 = data.ReadBytes(12);
obj.AuxType = data.ReadByteValue();
obj.Reserved1 = data.ReadByteValue();
obj.SymbolTableIndex = data.ReadUInt32LittleEndian();
obj.Reserved2 = data.ReadBytes(12);
return obj;
}

View File

@@ -69,10 +69,7 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine(localFileHeader.FileNameLength, " [Local File Header] File name length");
builder.AppendLine(localFileHeader.ExtraFieldLength, " [Local File Header] Extra field length");
builder.AppendLine(localFileHeader.FileName, " [Local File Header] File name");
// TODO: Reenable this when models are fixed
// var extraFields = Deserializers.PKZIP.ParseExtraFields(localFileHeader, localFileHeader.ExtraField);
// Print(builder, " [Local File Header] Extra Fields", extraFields);
Print(builder, " [Local File Header] Extra Fields", localFileHeader.ExtraFields);
}
#endregion
@@ -241,10 +238,7 @@ namespace SabreTools.Serialization.Printers
builder.AppendLine(entry.RelativeOffsetOfLocalHeader, " Relative offset of local header");
builder.AppendLine(entry.FileName, " File name");
builder.AppendLine(entry.FileComment, " File comment");
// TODO: Reenable this when models are fixed
// var extraFields = Deserializers.PKZIP.ParseExtraFields(entry, entry.ExtraField);
// Print(builder, " Extra Fields", extraFields);
Print(builder, " Extra Fields", entry.ExtraFields);
}
builder.AppendLine();

View File

@@ -410,10 +410,10 @@ namespace SabreTools.Serialization.Printers
private static void Print(StringBuilder builder, CLRTokenDefinition entry, int i)
{
builder.AppendLine($" COFF Symbol Table Entry {i} (CLR Token Defintion)");
builder.AppendLine(entry.AuxFormat6AuxType, " Aux type");
builder.AppendLine(entry.AuxFormat6Reserved1, " Reserved");
builder.AppendLine(entry.AuxFormat6SymbolTableIndex, " Symbol table index");
builder.AppendLine(entry.AuxFormat6Reserved2, " Reserved");
builder.AppendLine(entry.AuxType, " Aux type");
builder.AppendLine(entry.Reserved1, " Reserved");
builder.AppendLine(entry.SymbolTableIndex, " Symbol table index");
builder.AppendLine(entry.Reserved2, " Reserved");
}
private static void Print(StringBuilder builder, COFFStringTable? stringTable)

View File

@@ -12,7 +12,7 @@
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Version>1.9.0</Version>
<Version>1.9.1</Version>
<!-- Package Properties -->
<Authors>Matt Nadareski</Authors>
@@ -63,10 +63,10 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.ASN1" Version="1.6.0" />
<PackageReference Include="SabreTools.ASN1" Version="1.6.2" />
<PackageReference Include="SabreTools.Hashing" Version="1.5.0" />
<PackageReference Include="SabreTools.IO" Version="1.7.1" />
<PackageReference Include="SabreTools.Models" Version="1.7.0" />
<PackageReference Include="SabreTools.IO" Version="1.7.2" />
<PackageReference Include="SabreTools.Models" Version="1.7.1" />
<PackageReference Include="SabreTools.Matching" Version="1.6.0" />
<PackageReference Include="SharpCompress" Version="0.40.0" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
</ItemGroup>

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

@@ -36,7 +36,7 @@ namespace SabreTools.Serialization.Wrappers
{
get
{
lock (_sourceDataLock)
lock (_overlayAddressLock)
{
// Use the cached data if possible
if (_overlayAddress != null)
@@ -65,10 +65,13 @@ namespace SabreTools.Serialization.Wrappers
if (entry.FlagWord.HasFlag(SegmentTableEntryFlag.RELOCINFO))
#endif
{
_dataSource.Seek(offset, SeekOrigin.Begin);
var relocationData = Deserializers.NewExecutable.ParsePerSegmentData(_dataSource);
lock (_dataSourceLock)
{
_dataSource.Seek(offset, SeekOrigin.Begin);
var relocationData = Deserializers.NewExecutable.ParsePerSegmentData(_dataSource);
offset = _dataSource.Position;
offset = _dataSource.Position;
}
}
if (offset > endOfSectionData)
@@ -94,6 +97,10 @@ namespace SabreTools.Serialization.Wrappers
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;
@@ -105,11 +112,11 @@ namespace SabreTools.Serialization.Wrappers
/// Overlay data, if it exists
/// </summary>
/// <see href="https://codeberg.org/CYBERDEV/REWise/src/branch/master/src/exefile.c"/>
public byte[]? OverlayData
public byte[] OverlayData
{
get
{
lock (_sourceDataLock)
lock (_overlayDataLock)
{
// Use the cached data if possible
if (_overlayData != null)
@@ -118,54 +125,27 @@ namespace SabreTools.Serialization.Wrappers
// Get the available source length, if possible
long dataLength = Length;
if (dataLength == -1)
return null;
{
_overlayData = [];
return _overlayData;
}
// If a required property is missing
if (Header == null || SegmentTable == null || ResourceTable?.ResourceTypes == null)
return null;
// 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;
// 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))
#endif
{
_dataSource.Seek(offset, SeekOrigin.Begin);
var relocationData = Deserializers.NewExecutable.ParsePerSegmentData(_dataSource);
offset = _dataSource.Position;
}
if (offset > endOfSectionData)
endOfSectionData = offset;
_overlayData = [];
return _overlayData;
}
// 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;
}
}
// Get the overlay address if possible
long endOfSectionData = OverlayAddress;
// If we didn't find the end of section data
if (endOfSectionData <= 0)
return null;
{
_overlayData = [];
return _overlayData;
}
// If we're at the end of the file, cache an empty byte array
if (endOfSectionData >= dataLength)
@@ -176,7 +156,7 @@ namespace SabreTools.Serialization.Wrappers
// Otherwise, cache and return the data
long overlayLength = dataLength - endOfSectionData;
_overlayData = _dataSource.ReadFrom((int)endOfSectionData, (int)overlayLength, retainPosition: true);
_overlayData = ReadRangeFromSource((int)endOfSectionData, (int)overlayLength);
return _overlayData;
}
}
@@ -185,11 +165,11 @@ namespace SabreTools.Serialization.Wrappers
/// <summary>
/// Overlay strings, if they exist
/// </summary>
public List<string>? OverlayStrings
public List<string> OverlayStrings
{
get
{
lock (_sourceDataLock)
lock (_overlayStringsLock)
{
// Use the cached data if possible
if (_overlayStrings != null)
@@ -198,57 +178,21 @@ namespace SabreTools.Serialization.Wrappers
// Get the available source length, if possible
long dataLength = Length;
if (dataLength == -1)
return null;
// If a required property is missing
if (Header == null || SegmentTable == null || ResourceTable?.ResourceTypes == null)
return null;
// Search through the segments table to find the furthest
int endOfSectionData = -1;
foreach (var entry in SegmentTable)
{
int offset = (entry.Offset << Header.SegmentAlignmentShiftCount) + entry.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)
return null;
// Adjust the position of the data by 705 bytes
// TODO: Investigate what the byte data is
endOfSectionData += 705;
// If we're at the end of the file, cache an empty list
if (endOfSectionData >= dataLength)
{
_overlayStrings = [];
return _overlayStrings;
}
// TODO: Revisit the 16 MiB limit
// Cap the check for overlay strings to 16 MiB (arbitrary)
long overlayLength = Math.Min(dataLength - endOfSectionData, 16 * 1024 * 1024);
// Get the overlay data, if possible
var overlayData = OverlayData;
if (overlayData.Length == 0)
{
_overlayStrings = [];
return _overlayStrings;
}
// Otherwise, cache and return the strings
_overlayStrings = _dataSource.ReadStringsFrom(endOfSectionData, (int)overlayLength, charLimit: 3);
_overlayStrings = overlayData.ReadStringsFrom(charLimit: 3) ?? [];
return _overlayStrings;
}
}
@@ -269,23 +213,26 @@ namespace SabreTools.Serialization.Wrappers
/// <summary>
/// Stub executable data, if it exists
/// </summary>
public byte[]? StubExecutableData
public byte[] StubExecutableData
{
get
{
lock (_sourceDataLock)
lock (_stubExecutableDataLock)
{
// If we already have cached data, just use that immediately
if (_stubExecutableData != null)
return _stubExecutableData;
if (Stub?.Header?.NewExeHeaderAddr == null)
return null;
{
_stubExecutableData = [];
return _stubExecutableData;
}
// 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);
_stubExecutableData = ReadRangeFromSource(endOfStubHeader, lengthOfStubExecutableData);
// Cache and return the stub executable data, even if null
return _stubExecutableData;
@@ -302,25 +249,40 @@ namespace SabreTools.Serialization.Wrappers
/// </summary>
private long? _overlayAddress = null;
/// <summary>
/// Lock object for <see cref="_overlayAddress"/>
/// </summary>
private readonly object _overlayAddressLock = new();
/// <summary>
/// Overlay data, if it exists
/// </summary>
private byte[]? _overlayData = null;
/// <summary>
/// Lock object for <see cref="_overlayData"/>
/// </summary>
private readonly object _overlayDataLock = new();
/// <summary>
/// Overlay strings, if they exist
/// </summary>
private List<string>? _overlayStrings = null;
/// <summary>
/// Lock object for <see cref="_overlayStrings"/>
/// </summary>
private readonly object _overlayStringsLock = new();
/// <summary>
/// Stub executable data, if it exists
/// </summary>
private byte[]? _stubExecutableData = null;
/// <summary>
/// Lock object for reading from the source
/// Lock object for <see cref="_stubExecutableData"/>
/// </summary>
private readonly object _sourceDataLock = new();
private readonly object _stubExecutableDataLock = new();
#endregion
@@ -420,7 +382,7 @@ namespace SabreTools.Serialization.Wrappers
{
// Cache the overlay data for easier reading
var overlayData = OverlayData;
if (overlayData == null || overlayData.Length == 0)
if (overlayData.Length == 0)
return false;
// Set the output variables
@@ -605,28 +567,31 @@ namespace SabreTools.Serialization.Wrappers
if (overlayOffset < 0 || overlayOffset >= Length)
return -1;
// Attempt to get the overlay header
_dataSource.Seek(overlayOffset, SeekOrigin.Begin);
var header = WiseOverlayHeader.Create(_dataSource);
if (header != null)
return overlayOffset;
// Align and loop to see if it can be found
_dataSource.Seek(overlayOffset, SeekOrigin.Begin);
_dataSource.AlignToBoundary(0x10);
overlayOffset = _dataSource.Position;
while (_dataSource.Position < Length)
lock (_dataSourceLock)
{
// Attempt to get the overlay header
_dataSource.Seek(overlayOffset, SeekOrigin.Begin);
header = WiseOverlayHeader.Create(_dataSource);
var header = WiseOverlayHeader.Create(_dataSource);
if (header != null)
return overlayOffset;
overlayOffset += 0x10;
}
// Align and loop to see if it can be found
_dataSource.Seek(overlayOffset, SeekOrigin.Begin);
_dataSource.AlignToBoundary(0x10);
overlayOffset = _dataSource.Position;
while (_dataSource.Position < Length)
{
_dataSource.Seek(overlayOffset, SeekOrigin.Begin);
header = WiseOverlayHeader.Create(_dataSource);
if (header != null)
return overlayOffset;
header = null;
return -1;
overlayOffset += 0x10;
}
header = null;
return -1;
}
}
/// <summary>
@@ -692,7 +657,7 @@ namespace SabreTools.Serialization.Wrappers
return [];
// Read the resource data and return
return _dataSource.ReadFrom(offset, length, retainPosition: true);
return ReadRangeFromSource(offset, length);
}
/// <summary>
@@ -784,7 +749,7 @@ namespace SabreTools.Serialization.Wrappers
return [];
// Read the segment data and return
return _dataSource.ReadFrom(offset, length, retainPosition: true);
return ReadRangeFromSource(offset, length);
}
/// <summary>
@@ -854,7 +819,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;

File diff suppressed because it is too large Load Diff

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

@@ -232,76 +232,79 @@ namespace SabreTools.Serialization.Wrappers
/// <remarks>On success, this sets <see cref="InstallerDataOffset"/></remarks>
public bool ExtractHeaderDefinedFiles(string outputDirectory, bool includeDebug)
{
// Seek to the compressed data offset
_dataSource.Seek(CompressedDataOffset, SeekOrigin.Begin);
if (includeDebug) Console.WriteLine($"Beginning of header-defined files: {CompressedDataOffset}");
lock (_dataSourceLock)
{
// Seek to the compressed data offset
_dataSource.Seek(CompressedDataOffset, SeekOrigin.Begin);
if (includeDebug) Console.WriteLine($"Beginning of header-defined files: {CompressedDataOffset}");
// Extract WiseColors.dib, if it exists
var expected = new DeflateInfo { InputSize = DibDeflatedSize, OutputSize = DibInflatedSize, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, "WiseColors.dib", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract WiseColors.dib, if it exists
var expected = new DeflateInfo { InputSize = DibDeflatedSize, OutputSize = DibInflatedSize, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, "WiseColors.dib", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract WiseScript.bin
expected = new DeflateInfo { InputSize = WiseScriptDeflatedSize, OutputSize = WiseScriptInflatedSize, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, "WiseScript.bin", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract WiseScript.bin
expected = new DeflateInfo { InputSize = WiseScriptDeflatedSize, OutputSize = WiseScriptInflatedSize, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, "WiseScript.bin", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract WISE0001.DLL, if it exists
expected = new DeflateInfo { InputSize = WiseDllDeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "WISE0001.DLL", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract WISE0001.DLL, if it exists
expected = new DeflateInfo { InputSize = WiseDllDeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "WISE0001.DLL", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract CTL3D32.DLL, if it exists
expected = new DeflateInfo { InputSize = Ctl3d32DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "CTL3D32.DLL", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract CTL3D32.DLL, if it exists
expected = new DeflateInfo { InputSize = Ctl3d32DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "CTL3D32.DLL", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE0004, if it exists
expected = new DeflateInfo { InputSize = SomeData4DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE0004", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE0004, if it exists
expected = new DeflateInfo { InputSize = SomeData4DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE0004", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract Ocxreg32.EXE, if it exists
expected = new DeflateInfo { InputSize = RegToolDeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "Ocxreg32.EXE", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract Ocxreg32.EXE, if it exists
expected = new DeflateInfo { InputSize = RegToolDeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "Ocxreg32.EXE", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract PROGRESS.DLL, if it exists
expected = new DeflateInfo { InputSize = ProgressDllDeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "PROGRESS.DLL", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract PROGRESS.DLL, if it exists
expected = new DeflateInfo { InputSize = ProgressDllDeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "PROGRESS.DLL", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE0007, if it exists
expected = new DeflateInfo { InputSize = SomeData7DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE0007", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE0007, if it exists
expected = new DeflateInfo { InputSize = SomeData7DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE0007", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE0008, if it exists
expected = new DeflateInfo { InputSize = SomeData8DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE0008", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE0008, if it exists
expected = new DeflateInfo { InputSize = SomeData8DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE0008", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE0009, if it exists
expected = new DeflateInfo { InputSize = SomeData9DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE0009", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE0009, if it exists
expected = new DeflateInfo { InputSize = SomeData9DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE0009", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE000A, if it exists
expected = new DeflateInfo { InputSize = SomeData10DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE000A", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE000A, if it exists
expected = new DeflateInfo { InputSize = SomeData10DeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE000A", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract install script, if it exists
expected = new DeflateInfo { InputSize = InstallScriptDeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "INSTALL_SCRIPT", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract install script, if it exists
expected = new DeflateInfo { InputSize = InstallScriptDeflatedSize, OutputSize = -1, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "INSTALL_SCRIPT", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE000{n}.DAT, if it exists
expected = new DeflateInfo { InputSize = FinalFileDeflatedSize, OutputSize = FinalFileInflatedSize, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE00XX.DAT", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
// Extract FILE000{n}.DAT, if it exists
expected = new DeflateInfo { InputSize = FinalFileDeflatedSize, OutputSize = FinalFileInflatedSize, Crc32 = 0 };
if (InflateWrapper.ExtractFile(_dataSource, IsPKZIP ? null : "FILE00XX.DAT", outputDirectory, expected, IsPKZIP, includeDebug) == ExtractionStatus.FAIL)
return false;
InstallerDataOffset = _dataSource.Position;
InstallerDataOffset = _dataSource.Position;
}
return true;
}
@@ -328,13 +331,17 @@ namespace SabreTools.Serialization.Wrappers
// Perform path replacements
string filename = obj.DestinationPathname ?? $"WISE{index:X4}";
filename = filename.Replace("%", string.Empty);
_dataSource.Seek(InstallerDataOffset + obj.DeflateStart, SeekOrigin.Begin);
return InflateWrapper.ExtractFile(_dataSource,
filename,
outputDirectory,
expected,
IsPKZIP,
includeDebug);
lock (_dataSourceLock)
{
_dataSource.Seek(InstallerDataOffset + obj.DeflateStart, SeekOrigin.Begin);
return InflateWrapper.ExtractFile(_dataSource,
filename,
outputDirectory,
expected,
IsPKZIP,
includeDebug);
}
}
/// <summary>
@@ -373,8 +380,12 @@ namespace SabreTools.Serialization.Wrappers
// Perform path replacements
string filename = $"{baseName}{i:X4}";
_dataSource.Seek(InstallerDataOffset + info.DeflateStart, SeekOrigin.Begin);
_ = InflateWrapper.ExtractFile(_dataSource, filename, outputDirectory, expected, IsPKZIP, includeDebug);
lock (_dataSourceLock)
{
_dataSource.Seek(InstallerDataOffset + info.DeflateStart, SeekOrigin.Begin);
_ = InflateWrapper.ExtractFile(_dataSource, filename, outputDirectory, expected, IsPKZIP, includeDebug);
}
}
// Always return good -- TODO: Fix this
@@ -402,8 +413,12 @@ namespace SabreTools.Serialization.Wrappers
// Perform path replacements
string filename = $"CustomDialogSet_{obj.DisplayVariable}-{obj.Name}";
filename = filename.Replace("%", string.Empty);
_dataSource.Seek(InstallerDataOffset + obj.DeflateStart, SeekOrigin.Begin);
return InflateWrapper.ExtractFile(_dataSource, filename, outputDirectory, expected, IsPKZIP, includeDebug);
lock (_dataSourceLock)
{
_dataSource.Seek(InstallerDataOffset + obj.DeflateStart, SeekOrigin.Begin);
return InflateWrapper.ExtractFile(_dataSource, filename, outputDirectory, expected, IsPKZIP, includeDebug);
}
}
/// <summary>

View File

@@ -29,7 +29,7 @@ namespace SabreTools.Serialization.Wrappers
/// to ensure that all possible values before the temp string are found
/// and read properly
public long CompressedDataOffset { get; private set; }
/// <inheritdoc cref="SectionHeader.UnknownDataSize"/>
public uint UnknownDataSize => Model.UnknownDataSize;
@@ -159,7 +159,7 @@ namespace SabreTools.Serialization.Wrappers
long endOffset = data.Position - currentOffset;
data.Seek(currentOffset, SeekOrigin.Begin);
return new WiseSectionHeader(model, data) { CompressedDataOffset = endOffset};
return new WiseSectionHeader(model, data) { CompressedDataOffset = endOffset };
}
catch
{
@@ -181,7 +181,7 @@ namespace SabreTools.Serialization.Wrappers
if (includeDebug) Console.Error.WriteLine("Could not extract header-defined files");
return false;
}
return true;
}
@@ -196,37 +196,40 @@ namespace SabreTools.Serialization.Wrappers
/// <returns>True if the files extracted successfully, false otherwise</returns>
private bool ExtractHeaderDefinedFiles(string outputDirectory, bool includeDebug)
{
// Seek to the compressed data offset
_dataSource.Seek(CompressedDataOffset, SeekOrigin.Begin);
bool successful = true;
// Extract first executable, if it exists
if (ExtractFile("FirstExecutable.exe", outputDirectory, FirstExecutableFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
successful = false;
// Extract second executable, if it exists
// If there's a size provided for the second executable but no size for the first executable, the size of
// the second executable appears to be some unrelated value that's larger than the second executable
// actually is. Currently unable to extract properly in these cases, as no header value in such installers
// seems to actually correspond to the real size of the second executable.
if (ExtractFile("SecondExecutable.exe", outputDirectory, SecondExecutableFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
successful = false;
// Extract third executable, if it exists
if (ExtractFile("ThirdExecutable.exe", outputDirectory, ThirdExecutableFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
successful = false;
// Extract main MSI file
if (ExtractFile("ExtractedMsi.msi", outputDirectory, MsiFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
lock (_dataSourceLock)
{
// Fallback- seek to the position that's the length of the MSI file entry from the end, then try and
// extract from there.
_dataSource.Seek(-MsiFileEntryLength + 1, SeekOrigin.End);
if (ExtractFile("ExtractedMsi.msi", outputDirectory, MsiFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
return false; // The fallback also failed.
}
// Seek to the compressed data offset
_dataSource.Seek(CompressedDataOffset, SeekOrigin.Begin);
bool successful = true;
return successful;
// Extract first executable, if it exists
if (ExtractFile("FirstExecutable.exe", outputDirectory, FirstExecutableFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
successful = false;
// Extract second executable, if it exists
// If there's a size provided for the second executable but no size for the first executable, the size of
// the second executable appears to be some unrelated value that's larger than the second executable
// actually is. Currently unable to extract properly in these cases, as no header value in such installers
// seems to actually correspond to the real size of the second executable.
if (ExtractFile("SecondExecutable.exe", outputDirectory, SecondExecutableFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
successful = false;
// Extract third executable, if it exists
if (ExtractFile("ThirdExecutable.exe", outputDirectory, ThirdExecutableFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
successful = false;
// Extract main MSI file
if (ExtractFile("ExtractedMsi.msi", outputDirectory, MsiFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
{
// Fallback- seek to the position that's the length of the MSI file entry from the end, then try and
// extract from there.
_dataSource.Seek(-MsiFileEntryLength + 1, SeekOrigin.End);
if (ExtractFile("ExtractedMsi.msi", outputDirectory, MsiFileEntryLength, includeDebug) != ExtractionStatus.GOOD)
return false; // The fallback also failed.
}
return successful;
}
}
/// <summary>

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 an empty 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;