mirror of
https://github.com/SabreTools/SabreTools.Serialization.git
synced 2026-09-23 07:14:57 +00:00
Privatize Reader class
This commit is contained in:
@@ -1,249 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SabreTools.Models.InstallShieldCabinet;
|
||||
using Header = SabreTools.Serialization.Wrappers.InstallShieldCabinet;
|
||||
|
||||
namespace UnshieldSharpInternal
|
||||
{
|
||||
internal class Reader : IDisposable
|
||||
{
|
||||
#region Private Instance Variables
|
||||
|
||||
/// <summary>
|
||||
/// Cabinet file to read from
|
||||
/// </summary>
|
||||
private readonly Header _cabinet;
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected index
|
||||
/// </summary>
|
||||
private readonly uint _index;
|
||||
|
||||
/// <summary>
|
||||
/// File descriptor defining the currently selected index
|
||||
/// </summary>
|
||||
private readonly FileDescriptor _fileDescriptor;
|
||||
|
||||
/// <summary>
|
||||
/// Offset in the data where the file exists
|
||||
/// </summary>
|
||||
private ulong _dataOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes left in the current volume
|
||||
/// </summary>
|
||||
private ulong _volumeBytesLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Handle to the current volume stream
|
||||
/// </summary>
|
||||
private Stream? _volumeFile;
|
||||
|
||||
/// <summary>
|
||||
/// Current volume header
|
||||
/// </summary>
|
||||
private VolumeHeader? _volumeHeader;
|
||||
|
||||
/// <summary>
|
||||
/// Current volume ID
|
||||
/// </summary>
|
||||
private ushort _volumeId;
|
||||
|
||||
/// <summary>
|
||||
/// Offset for obfuscation seed
|
||||
/// </summary>
|
||||
private uint _obfuscationOffset;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private Reader(Header cabinet, uint index, FileDescriptor fileDescriptor)
|
||||
{
|
||||
_cabinet = cabinet;
|
||||
_index = index;
|
||||
_fileDescriptor = fileDescriptor;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Reader"> from an existing cabinet, index, and file descriptor
|
||||
/// </summary>
|
||||
public static Reader? Create(Header cabinet, int index, FileDescriptor fileDescriptor)
|
||||
{
|
||||
var reader = new Reader(cabinet, (uint)index, fileDescriptor);
|
||||
for (; ; )
|
||||
{
|
||||
// If the volume is invalid
|
||||
if (!reader.OpenVolume(fileDescriptor.Volume))
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to open volume {fileDescriptor.Volume}");
|
||||
return null;
|
||||
}
|
||||
else if (reader._volumeFile == null || reader._volumeHeader == null)
|
||||
{
|
||||
Console.Error.WriteLine($"Volume {fileDescriptor.Volume} is invalid");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Start with the correct volume for IS5 cabinets
|
||||
if (reader._cabinet.MajorVersion <= 5 && index > (int)reader._volumeHeader.LastFileIndex)
|
||||
{
|
||||
// Normalize the volume ID for odd cases
|
||||
if (fileDescriptor.Volume == ushort.MinValue || fileDescriptor.Volume == ushort.MaxValue)
|
||||
fileDescriptor.Volume = 1;
|
||||
|
||||
fileDescriptor.Volume++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return reader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of the current object
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_volumeFile?.Close();
|
||||
}
|
||||
|
||||
#region Reading
|
||||
|
||||
/// <summary>
|
||||
/// Read a certain number of bytes from the current volume
|
||||
/// </summary>
|
||||
public bool Read(byte[] buffer, int start, long size)
|
||||
{
|
||||
long bytesLeft = size;
|
||||
while (bytesLeft > 0)
|
||||
{
|
||||
// Open the next volume, if necessary
|
||||
if (_volumeBytesLeft == 0)
|
||||
{
|
||||
if (!OpenNextVolume(out _))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the number of bytes to read from this volume
|
||||
int bytesToRead = (int)Math.Min(bytesLeft, (long)_volumeBytesLeft);
|
||||
if (bytesToRead == 0)
|
||||
break;
|
||||
|
||||
// Read as much as possible from this volume
|
||||
if (bytesToRead != _volumeFile!.Read(buffer, start, bytesToRead))
|
||||
return false;
|
||||
|
||||
// Set the number of bytes left
|
||||
bytesLeft -= bytesToRead;
|
||||
_volumeBytesLeft -= (uint)bytesToRead;
|
||||
}
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((_fileDescriptor.Flags & FileFlags.FILE_OBFUSCATED) != 0)
|
||||
#else
|
||||
if (_fileDescriptor.Flags.HasFlag(FileFlags.FILE_OBFUSCATED))
|
||||
#endif
|
||||
Header.Deobfuscate(buffer, size, ref _obfuscationOffset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the next volume based on the current index
|
||||
/// </summary>
|
||||
private bool OpenNextVolume(out ushort nextVolume)
|
||||
{
|
||||
nextVolume = (ushort)(_volumeId + 1);
|
||||
return OpenVolume(nextVolume);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the volume at the inputted index
|
||||
/// </summary>
|
||||
private bool OpenVolume(ushort volume)
|
||||
{
|
||||
// Read the volume from the cabinet set
|
||||
var next = _cabinet.OpenVolume(volume, out var volumeStream);
|
||||
if (next?.VolumeHeader == null || volumeStream == null)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to open input cabinet file {volume}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assign the next items
|
||||
_volumeFile?.Close();
|
||||
_volumeFile = volumeStream;
|
||||
_volumeHeader = next.VolumeHeader;
|
||||
|
||||
// Enable support for split archives for IS5
|
||||
if (_cabinet.MajorVersion == 5)
|
||||
{
|
||||
if (_index < (_cabinet.FileCount - 1)
|
||||
&& _index == _volumeHeader.LastFileIndex
|
||||
&& _volumeHeader.LastFileSizeCompressed != _fileDescriptor.CompressedSize)
|
||||
{
|
||||
_fileDescriptor.Flags |= FileFlags.FILE_SPLIT;
|
||||
}
|
||||
else if (_index > 0
|
||||
&& _index == _volumeHeader.FirstFileIndex
|
||||
&& _volumeHeader.FirstFileSizeCompressed != _fileDescriptor.CompressedSize)
|
||||
{
|
||||
_fileDescriptor.Flags |= FileFlags.FILE_SPLIT;
|
||||
}
|
||||
}
|
||||
|
||||
ulong volumeBytesLeftCompressed, volumeBytesLeftExpanded;
|
||||
#if NET20 || NET35
|
||||
if ((_fileDescriptor.Flags & FileFlags.FILE_SPLIT) != 0)
|
||||
#else
|
||||
if (_fileDescriptor.Flags.HasFlag(FileFlags.FILE_SPLIT))
|
||||
#endif
|
||||
{
|
||||
if (_index == _volumeHeader.LastFileIndex && _volumeHeader.LastFileOffset != 0x7FFFFFFF)
|
||||
{
|
||||
// can be first file too
|
||||
_dataOffset = _volumeHeader.LastFileOffset;
|
||||
volumeBytesLeftExpanded = _volumeHeader.LastFileSizeExpanded;
|
||||
volumeBytesLeftCompressed = _volumeHeader.LastFileSizeCompressed;
|
||||
}
|
||||
else if (_index == _volumeHeader.FirstFileIndex)
|
||||
{
|
||||
_dataOffset = _volumeHeader.FirstFileOffset;
|
||||
volumeBytesLeftExpanded = _volumeHeader.FirstFileSizeExpanded;
|
||||
volumeBytesLeftCompressed = _volumeHeader.FirstFileSizeCompressed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_dataOffset = _fileDescriptor.DataOffset;
|
||||
volumeBytesLeftExpanded = _fileDescriptor.ExpandedSize;
|
||||
volumeBytesLeftCompressed = _fileDescriptor.CompressedSize;
|
||||
}
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((_fileDescriptor.Flags & FileFlags.FILE_COMPRESSED) != 0)
|
||||
#else
|
||||
if (_fileDescriptor.Flags.HasFlag(FileFlags.FILE_COMPRESSED))
|
||||
#endif
|
||||
_volumeBytesLeft = volumeBytesLeftCompressed;
|
||||
else
|
||||
_volumeBytesLeft = volumeBytesLeftExpanded;
|
||||
|
||||
_volumeFile.Seek((long)_dataOffset, SeekOrigin.Begin);
|
||||
_volumeId = volume;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ using SabreTools.Hashing;
|
||||
using SabreTools.IO.Compression.zlib;
|
||||
using SabreTools.Models.InstallShieldCabinet;
|
||||
using SabreTools.Serialization.Interfaces;
|
||||
using UnshieldSharpInternal;
|
||||
using static SabreTools.Models.InstallShieldCabinet.Constants;
|
||||
|
||||
namespace SabreTools.Serialization.Wrappers
|
||||
@@ -940,5 +939,254 @@ namespace SabreTools.Serialization.Wrappers
|
||||
private static int ROL8(int x, byte n) => (x << n) | (x >> (8 - n));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Classes
|
||||
|
||||
/// <summary>
|
||||
/// Helper to read a single file from a cabinet set
|
||||
/// </summary>
|
||||
private class Reader : IDisposable
|
||||
{
|
||||
#region Private Instance Variables
|
||||
|
||||
/// <summary>
|
||||
/// Cabinet file to read from
|
||||
/// </summary>
|
||||
private readonly InstallShieldCabinet _cabinet;
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected index
|
||||
/// </summary>
|
||||
private readonly uint _index;
|
||||
|
||||
/// <summary>
|
||||
/// File descriptor defining the currently selected index
|
||||
/// </summary>
|
||||
private readonly FileDescriptor _fileDescriptor;
|
||||
|
||||
/// <summary>
|
||||
/// Offset in the data where the file exists
|
||||
/// </summary>
|
||||
private ulong _dataOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Number of bytes left in the current volume
|
||||
/// </summary>
|
||||
private ulong _volumeBytesLeft;
|
||||
|
||||
/// <summary>
|
||||
/// Handle to the current volume stream
|
||||
/// </summary>
|
||||
private Stream? _volumeFile;
|
||||
|
||||
/// <summary>
|
||||
/// Current volume header
|
||||
/// </summary>
|
||||
private VolumeHeader? _volumeHeader;
|
||||
|
||||
/// <summary>
|
||||
/// Current volume ID
|
||||
/// </summary>
|
||||
private ushort _volumeId;
|
||||
|
||||
/// <summary>
|
||||
/// Offset for obfuscation seed
|
||||
/// </summary>
|
||||
private uint _obfuscationOffset;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
private Reader(InstallShieldCabinet cabinet, uint index, FileDescriptor fileDescriptor)
|
||||
{
|
||||
_cabinet = cabinet;
|
||||
_index = index;
|
||||
_fileDescriptor = fileDescriptor;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="Reader"> from an existing cabinet, index, and file descriptor
|
||||
/// </summary>
|
||||
public static Reader? Create(InstallShieldCabinet cabinet, int index, FileDescriptor fileDescriptor)
|
||||
{
|
||||
var reader = new Reader(cabinet, (uint)index, fileDescriptor);
|
||||
for (; ; )
|
||||
{
|
||||
// If the volume is invalid
|
||||
if (!reader.OpenVolume(fileDescriptor.Volume))
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to open volume {fileDescriptor.Volume}");
|
||||
return null;
|
||||
}
|
||||
else if (reader._volumeFile == null || reader._volumeHeader == null)
|
||||
{
|
||||
Console.Error.WriteLine($"Volume {fileDescriptor.Volume} is invalid");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Start with the correct volume for IS5 cabinets
|
||||
if (reader._cabinet.MajorVersion <= 5 && index > (int)reader._volumeHeader.LastFileIndex)
|
||||
{
|
||||
// Normalize the volume ID for odd cases
|
||||
if (fileDescriptor.Volume == ushort.MinValue || fileDescriptor.Volume == ushort.MaxValue)
|
||||
fileDescriptor.Volume = 1;
|
||||
|
||||
fileDescriptor.Volume++;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return reader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of the current object
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_volumeFile?.Close();
|
||||
}
|
||||
|
||||
#region Reading
|
||||
|
||||
/// <summary>
|
||||
/// Read a certain number of bytes from the current volume
|
||||
/// </summary>
|
||||
public bool Read(byte[] buffer, int start, long size)
|
||||
{
|
||||
long bytesLeft = size;
|
||||
while (bytesLeft > 0)
|
||||
{
|
||||
// Open the next volume, if necessary
|
||||
if (_volumeBytesLeft == 0)
|
||||
{
|
||||
if (!OpenNextVolume(out _))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the number of bytes to read from this volume
|
||||
int bytesToRead = (int)Math.Min(bytesLeft, (long)_volumeBytesLeft);
|
||||
if (bytesToRead == 0)
|
||||
break;
|
||||
|
||||
// Read as much as possible from this volume
|
||||
if (bytesToRead != _volumeFile!.Read(buffer, start, bytesToRead))
|
||||
return false;
|
||||
|
||||
// Set the number of bytes left
|
||||
bytesLeft -= bytesToRead;
|
||||
_volumeBytesLeft -= (uint)bytesToRead;
|
||||
}
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((_fileDescriptor.Flags & FileFlags.FILE_OBFUSCATED) != 0)
|
||||
#else
|
||||
if (_fileDescriptor.Flags.HasFlag(FileFlags.FILE_OBFUSCATED))
|
||||
#endif
|
||||
Deobfuscate(buffer, size, ref _obfuscationOffset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the next volume based on the current index
|
||||
/// </summary>
|
||||
private bool OpenNextVolume(out ushort nextVolume)
|
||||
{
|
||||
nextVolume = (ushort)(_volumeId + 1);
|
||||
return OpenVolume(nextVolume);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the volume at the inputted index
|
||||
/// </summary>
|
||||
private bool OpenVolume(ushort volume)
|
||||
{
|
||||
// Read the volume from the cabinet set
|
||||
var next = _cabinet.OpenVolume(volume, out var volumeStream);
|
||||
if (next?.VolumeHeader == null || volumeStream == null)
|
||||
{
|
||||
Console.Error.WriteLine($"Failed to open input cabinet file {volume}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Assign the next items
|
||||
_volumeFile?.Close();
|
||||
_volumeFile = volumeStream;
|
||||
_volumeHeader = next.VolumeHeader;
|
||||
|
||||
// Enable support for split archives for IS5
|
||||
if (_cabinet.MajorVersion == 5)
|
||||
{
|
||||
if (_index < (_cabinet.FileCount - 1)
|
||||
&& _index == _volumeHeader.LastFileIndex
|
||||
&& _volumeHeader.LastFileSizeCompressed != _fileDescriptor.CompressedSize)
|
||||
{
|
||||
_fileDescriptor.Flags |= FileFlags.FILE_SPLIT;
|
||||
}
|
||||
else if (_index > 0
|
||||
&& _index == _volumeHeader.FirstFileIndex
|
||||
&& _volumeHeader.FirstFileSizeCompressed != _fileDescriptor.CompressedSize)
|
||||
{
|
||||
_fileDescriptor.Flags |= FileFlags.FILE_SPLIT;
|
||||
}
|
||||
}
|
||||
|
||||
ulong volumeBytesLeftCompressed, volumeBytesLeftExpanded;
|
||||
#if NET20 || NET35
|
||||
if ((_fileDescriptor.Flags & FileFlags.FILE_SPLIT) != 0)
|
||||
#else
|
||||
if (_fileDescriptor.Flags.HasFlag(FileFlags.FILE_SPLIT))
|
||||
#endif
|
||||
{
|
||||
if (_index == _volumeHeader.LastFileIndex && _volumeHeader.LastFileOffset != 0x7FFFFFFF)
|
||||
{
|
||||
// can be first file too
|
||||
_dataOffset = _volumeHeader.LastFileOffset;
|
||||
volumeBytesLeftExpanded = _volumeHeader.LastFileSizeExpanded;
|
||||
volumeBytesLeftCompressed = _volumeHeader.LastFileSizeCompressed;
|
||||
}
|
||||
else if (_index == _volumeHeader.FirstFileIndex)
|
||||
{
|
||||
_dataOffset = _volumeHeader.FirstFileOffset;
|
||||
volumeBytesLeftExpanded = _volumeHeader.FirstFileSizeExpanded;
|
||||
volumeBytesLeftCompressed = _volumeHeader.FirstFileSizeCompressed;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_dataOffset = _fileDescriptor.DataOffset;
|
||||
volumeBytesLeftExpanded = _fileDescriptor.ExpandedSize;
|
||||
volumeBytesLeftCompressed = _fileDescriptor.CompressedSize;
|
||||
}
|
||||
|
||||
#if NET20 || NET35
|
||||
if ((_fileDescriptor.Flags & FileFlags.FILE_COMPRESSED) != 0)
|
||||
#else
|
||||
if (_fileDescriptor.Flags.HasFlag(FileFlags.FILE_COMPRESSED))
|
||||
#endif
|
||||
_volumeBytesLeft = volumeBytesLeftCompressed;
|
||||
else
|
||||
_volumeBytesLeft = volumeBytesLeftExpanded;
|
||||
|
||||
_volumeFile.Seek((long)_dataOffset, SeekOrigin.Begin);
|
||||
_volumeId = volume;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user