1451 lines
55 KiB
C#
1451 lines
55 KiB
C#
/* This file is part of SevenZipSharp.
|
|
|
|
SevenZipSharp is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
SevenZipSharp is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with SevenZipSharp. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
#if DOTNET20
|
|
using System.Threading;
|
|
#else
|
|
using System.Linq;
|
|
#endif
|
|
using SevenZip.Sdk.Compression.Lzma;
|
|
#if MONO
|
|
using SevenZip.Mono.COM;
|
|
#endif
|
|
|
|
namespace SevenZip
|
|
{
|
|
/// <summary>
|
|
/// Class to unpack data from archives supported by 7-Zip.
|
|
/// </summary>
|
|
/// <example>
|
|
/// using (var extr = new SevenZipExtractor(@"C:\Test.7z"))
|
|
/// {
|
|
/// extr.ExtractArchive(@"C:\TestDirectory");
|
|
/// }
|
|
/// </example>
|
|
public sealed partial class SevenZipExtractor
|
|
#if UNMANAGED
|
|
: SevenZipBase, IDisposable
|
|
#endif
|
|
{
|
|
#if UNMANAGED
|
|
private List<ArchiveFileInfo> _archiveFileData;
|
|
private IInArchive _archive;
|
|
private IInStream _archiveStream;
|
|
private int _offset;
|
|
private ArchiveOpenCallback _openCallback;
|
|
private string _fileName;
|
|
private Stream _inStream;
|
|
private long? _packedSize;
|
|
private long? _unpackedSize;
|
|
private uint? _filesCount;
|
|
private bool? _isSolid;
|
|
private bool _opened;
|
|
private bool _disposed;
|
|
private InArchiveFormat _format = (InArchiveFormat)(-1);
|
|
private ReadOnlyCollection<ArchiveFileInfo> _archiveFileInfoCollection;
|
|
private ReadOnlyCollection<ArchiveProperty> _archiveProperties;
|
|
private ReadOnlyCollection<string> _volumeFileNames;
|
|
/// <summary>
|
|
/// This is used to lock possible Dispose() calls.
|
|
/// </summary>
|
|
private bool _asynchronousDisposeLock;
|
|
|
|
#region Constructors
|
|
/// <summary>
|
|
/// General initialization function.
|
|
/// </summary>
|
|
/// <param name="archiveFullName">The archive file name.</param>
|
|
private void Init(string archiveFullName)
|
|
{
|
|
_fileName = archiveFullName;
|
|
bool isExecutable = false;
|
|
if ((int)_format == -1)
|
|
{
|
|
_format = FileChecker.CheckSignature(archiveFullName, out _offset, out isExecutable);
|
|
}
|
|
PreserveDirectoryStructure = true;
|
|
SevenZipLibraryManager.LoadLibrary(this, _format);
|
|
try
|
|
{
|
|
_archive = SevenZipLibraryManager.InArchive(_format, this);
|
|
}
|
|
catch (SevenZipLibraryException)
|
|
{
|
|
SevenZipLibraryManager.FreeLibrary(this, _format);
|
|
throw;
|
|
}
|
|
if (isExecutable && _format != InArchiveFormat.PE)
|
|
{
|
|
if (!Check())
|
|
{
|
|
CommonDispose();
|
|
_format = InArchiveFormat.PE;
|
|
SevenZipLibraryManager.LoadLibrary(this, _format);
|
|
try
|
|
{
|
|
_archive = SevenZipLibraryManager.InArchive(_format, this);
|
|
}
|
|
catch (SevenZipLibraryException)
|
|
{
|
|
SevenZipLibraryManager.FreeLibrary(this, _format);
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// General initialization function.
|
|
/// </summary>
|
|
/// <param name="stream">The stream to read the archive from.</param>
|
|
private void Init(Stream stream)
|
|
{
|
|
ValidateStream(stream);
|
|
bool isExecutable = false;
|
|
if ((int)_format == -1)
|
|
{
|
|
_format = FileChecker.CheckSignature(stream, out _offset, out isExecutable);
|
|
}
|
|
PreserveDirectoryStructure = true;
|
|
SevenZipLibraryManager.LoadLibrary(this, _format);
|
|
try
|
|
{
|
|
_inStream = new ArchiveEmulationStreamProxy(stream, _offset);
|
|
_packedSize = stream.Length;
|
|
_archive = SevenZipLibraryManager.InArchive(_format, this);
|
|
}
|
|
catch (SevenZipLibraryException)
|
|
{
|
|
SevenZipLibraryManager.FreeLibrary(this, _format);
|
|
throw;
|
|
}
|
|
if (isExecutable && _format != InArchiveFormat.PE)
|
|
{
|
|
if (!Check())
|
|
{
|
|
CommonDispose();
|
|
_format = InArchiveFormat.PE;
|
|
try
|
|
{
|
|
_inStream = new ArchiveEmulationStreamProxy(stream, _offset);
|
|
_packedSize = stream.Length;
|
|
_archive = SevenZipLibraryManager.InArchive(_format, this);
|
|
}
|
|
catch (SevenZipLibraryException)
|
|
{
|
|
SevenZipLibraryManager.FreeLibrary(this, _format);
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of SevenZipExtractor class.
|
|
/// </summary>
|
|
/// <param name="archiveStream">The stream to read the archive from.
|
|
/// Use SevenZipExtractor(string) to extract from disk, though it is not necessary.</param>
|
|
/// <remarks>The archive format is guessed by the signature.</remarks>
|
|
public SevenZipExtractor(Stream archiveStream)
|
|
{
|
|
Init(archiveStream);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of SevenZipExtractor class.
|
|
/// </summary>
|
|
/// <param name="archiveStream">The stream to read the archive from.
|
|
/// Use SevenZipExtractor(string) to extract from disk, though it is not necessary.</param>
|
|
/// <param name="format">Manual archive format setup. You SHOULD NOT normally specify it this way.
|
|
/// Instead, use SevenZipExtractor(Stream archiveStream), that constructor
|
|
/// automatically detects the archive format.</param>
|
|
public SevenZipExtractor(Stream archiveStream, InArchiveFormat format)
|
|
{
|
|
_format = format;
|
|
Init(archiveStream);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of SevenZipExtractor class.
|
|
/// </summary>
|
|
/// <param name="archiveFullName">The archive full file name.</param>
|
|
public SevenZipExtractor(string archiveFullName)
|
|
{
|
|
Init(archiveFullName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of SevenZipExtractor class.
|
|
/// </summary>
|
|
/// <param name="archiveFullName">The archive full file name.</param>
|
|
/// <param name="format">Manual archive format setup. You SHOULD NOT normally specify it this way.
|
|
/// Instead, use SevenZipExtractor(string archiveFullName), that constructor
|
|
/// automatically detects the archive format.</param>
|
|
public SevenZipExtractor(string archiveFullName, InArchiveFormat format)
|
|
{
|
|
_format = format;
|
|
Init(archiveFullName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of SevenZipExtractor class.
|
|
/// </summary>
|
|
/// <param name="archiveFullName">The archive full file name.</param>
|
|
/// <param name="password">Password for an encrypted archive.</param>
|
|
public SevenZipExtractor(string archiveFullName, string password)
|
|
: base(password)
|
|
{
|
|
Init(archiveFullName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of SevenZipExtractor class.
|
|
/// </summary>
|
|
/// <param name="archiveFullName">The archive full file name.</param>
|
|
/// <param name="password">Password for an encrypted archive.</param>
|
|
/// <param name="format">Manual archive format setup. You SHOULD NOT normally specify it this way.
|
|
/// Instead, use SevenZipExtractor(string archiveFullName, string password), that constructor
|
|
/// automatically detects the archive format.</param>
|
|
public SevenZipExtractor(string archiveFullName, string password, InArchiveFormat format)
|
|
: base(password)
|
|
{
|
|
_format = format;
|
|
Init(archiveFullName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of SevenZipExtractor class.
|
|
/// </summary>
|
|
/// <param name="archiveStream">The stream to read the archive from.</param>
|
|
/// <param name="password">Password for an encrypted archive.</param>
|
|
/// <remarks>The archive format is guessed by the signature.</remarks>
|
|
public SevenZipExtractor(Stream archiveStream, string password)
|
|
: base(password)
|
|
{
|
|
Init(archiveStream);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of SevenZipExtractor class.
|
|
/// </summary>
|
|
/// <param name="archiveStream">The stream to read the archive from.</param>
|
|
/// <param name="password">Password for an encrypted archive.</param>
|
|
/// <param name="format">Manual archive format setup. You SHOULD NOT normally specify it this way.
|
|
/// Instead, use SevenZipExtractor(Stream archiveStream, string password), that constructor
|
|
/// automatically detects the archive format.</param>
|
|
public SevenZipExtractor(Stream archiveStream, string password, InArchiveFormat format)
|
|
: base(password)
|
|
{
|
|
_format = format;
|
|
Init(archiveStream);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Properties
|
|
|
|
/// <summary>
|
|
/// Gets or sets archive full file name
|
|
/// </summary>
|
|
public string FileName
|
|
{
|
|
get
|
|
{
|
|
DisposedCheck();
|
|
return _fileName;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the size of the archive file
|
|
/// </summary>
|
|
public long PackedSize
|
|
{
|
|
get
|
|
{
|
|
DisposedCheck();
|
|
return _packedSize.HasValue
|
|
?
|
|
_packedSize.Value
|
|
:
|
|
_fileName != null
|
|
?
|
|
(new FileInfo(_fileName)).Length
|
|
:
|
|
-1;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the size of unpacked archive data
|
|
/// </summary>
|
|
public long UnpackedSize
|
|
{
|
|
get
|
|
{
|
|
DisposedCheck();
|
|
if (!_unpackedSize.HasValue)
|
|
{
|
|
return -1;
|
|
}
|
|
return _unpackedSize.Value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a value indicating whether the archive is solid
|
|
/// </summary>
|
|
public bool IsSolid
|
|
{
|
|
get
|
|
{
|
|
DisposedCheck();
|
|
if (!_isSolid.HasValue)
|
|
{
|
|
GetArchiveInfo(true);
|
|
}
|
|
Debug.Assert(_isSolid != null);
|
|
return _isSolid.Value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the number of files in the archive
|
|
/// </summary>
|
|
public uint FilesCount
|
|
{
|
|
get
|
|
{
|
|
DisposedCheck();
|
|
if (!_filesCount.HasValue)
|
|
{
|
|
GetArchiveInfo(true);
|
|
}
|
|
Debug.Assert(_filesCount != null);
|
|
return _filesCount.Value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets archive format
|
|
/// </summary>
|
|
public InArchiveFormat Format
|
|
{
|
|
get
|
|
{
|
|
DisposedCheck();
|
|
return _format;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the value indicating whether to preserve the directory structure of extracted files.
|
|
/// </summary>
|
|
public bool PreserveDirectoryStructure { get; set; }
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Checked whether the class was disposed.
|
|
/// </summary>
|
|
/// <exception cref="System.ObjectDisposedException" />
|
|
private void DisposedCheck()
|
|
{
|
|
if (_disposed)
|
|
{
|
|
throw new ObjectDisposedException("SevenZipExtractor");
|
|
}
|
|
#if !WINCE
|
|
RecreateInstanceIfNeeded();
|
|
#endif
|
|
}
|
|
|
|
#region Core private functions
|
|
|
|
private ArchiveOpenCallback GetArchiveOpenCallback()
|
|
{
|
|
return _openCallback ?? (_openCallback = String.IsNullOrEmpty(Password)
|
|
? new ArchiveOpenCallback(_fileName)
|
|
: new ArchiveOpenCallback(_fileName, Password));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the archive input stream.
|
|
/// </summary>
|
|
/// <returns>The archive input wrapper stream.</returns>
|
|
private IInStream GetArchiveStream(bool dispose)
|
|
{
|
|
if (_archiveStream != null)
|
|
{
|
|
if (_archiveStream is DisposeVariableWrapper)
|
|
{
|
|
(_archiveStream as DisposeVariableWrapper).DisposeStream = dispose;
|
|
}
|
|
return _archiveStream;
|
|
}
|
|
|
|
if (_inStream != null)
|
|
{
|
|
_inStream.Seek(0, SeekOrigin.Begin);
|
|
_archiveStream = new InStreamWrapper(_inStream, false);
|
|
}
|
|
else
|
|
{
|
|
if (!_fileName.EndsWith(".001", StringComparison.OrdinalIgnoreCase)
|
|
|| (_volumeFileNames.Count == 1))
|
|
{
|
|
_archiveStream = new InStreamWrapper(
|
|
new ArchiveEmulationStreamProxy(new FileStream(
|
|
_fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
|
|
_offset),
|
|
dispose);
|
|
}
|
|
else
|
|
{
|
|
_archiveStream = new InMultiStreamWrapper(_fileName, dispose);
|
|
_packedSize = (_archiveStream as InMultiStreamWrapper).Length;
|
|
}
|
|
}
|
|
return _archiveStream;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens the archive and throws exceptions or returns OperationResult.DataError if any error occurs.
|
|
/// </summary>
|
|
/// <param name="archiveStream">The IInStream compliant class instance, that is, the input stream.</param>
|
|
/// <param name="openCallback">The ArchiveOpenCallback instance.</param>
|
|
/// <returns>OperationResult.Ok if Open() succeeds.</returns>
|
|
private OperationResult OpenArchiveInner(IInStream archiveStream,
|
|
IArchiveOpenCallback openCallback)
|
|
{
|
|
ulong checkPos = 1 << 15;
|
|
int res = _archive.Open(archiveStream, ref checkPos, openCallback);
|
|
return (OperationResult)res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Opens the archive and throws exceptions or returns OperationResult.DataError if any error occurs.
|
|
/// </summary>
|
|
/// <param name="archiveStream">The IInStream compliant class instance, that is, the input stream.</param>
|
|
/// <param name="openCallback">The ArchiveOpenCallback instance.</param>
|
|
/// <returns>True if Open() succeeds; otherwise, false.</returns>
|
|
private bool OpenArchive(IInStream archiveStream,
|
|
ArchiveOpenCallback openCallback)
|
|
{
|
|
if (!_opened)
|
|
{
|
|
if (OpenArchiveInner(archiveStream, openCallback) != OperationResult.Ok)
|
|
{
|
|
if (!ThrowException(null, new SevenZipArchiveException()))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
_volumeFileNames = new ReadOnlyCollection<string>(openCallback.VolumeFileNames);
|
|
_opened = true;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves all information about the archive.
|
|
/// </summary>
|
|
/// <exception cref="SevenZip.SevenZipArchiveException"/>
|
|
private void GetArchiveInfo(bool disposeStream)
|
|
{
|
|
if (_archive == null)
|
|
{
|
|
if (!ThrowException(null, new SevenZipArchiveException()))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IInStream archiveStream;
|
|
using ((archiveStream = GetArchiveStream(disposeStream)) as IDisposable)
|
|
{
|
|
var openCallback = GetArchiveOpenCallback();
|
|
if (!_opened)
|
|
{
|
|
if (!OpenArchive(archiveStream, openCallback))
|
|
{
|
|
return;
|
|
}
|
|
_opened = !disposeStream;
|
|
}
|
|
_filesCount = _archive.GetNumberOfItems();
|
|
_archiveFileData = new List<ArchiveFileInfo>((int)_filesCount);
|
|
if (_filesCount != 0)
|
|
{
|
|
var data = new PropVariant();
|
|
try
|
|
{
|
|
#region Getting archive items data
|
|
|
|
for (uint i = 0; i < _filesCount; i++)
|
|
{
|
|
try
|
|
{
|
|
var fileInfo = new ArchiveFileInfo { Index = (int)i };
|
|
_archive.GetProperty(i, ItemPropId.Path, ref data);
|
|
fileInfo.FileName = NativeMethods.SafeCast(data, "[no name]");
|
|
_archive.GetProperty(i, ItemPropId.LastWriteTime, ref data);
|
|
fileInfo.LastWriteTime = NativeMethods.SafeCast(data, DateTime.Now);
|
|
_archive.GetProperty(i, ItemPropId.CreationTime, ref data);
|
|
fileInfo.CreationTime = NativeMethods.SafeCast(data, DateTime.Now);
|
|
_archive.GetProperty(i, ItemPropId.LastAccessTime, ref data);
|
|
fileInfo.LastAccessTime = NativeMethods.SafeCast(data, DateTime.Now);
|
|
_archive.GetProperty(i, ItemPropId.Size, ref data);
|
|
fileInfo.Size = NativeMethods.SafeCast<ulong>(data, 0);
|
|
if (fileInfo.Size == 0)
|
|
{
|
|
fileInfo.Size = NativeMethods.SafeCast<uint>(data, 0);
|
|
}
|
|
_archive.GetProperty(i, ItemPropId.Attributes, ref data);
|
|
fileInfo.Attributes = NativeMethods.SafeCast<uint>(data, 0);
|
|
_archive.GetProperty(i, ItemPropId.IsDirectory, ref data);
|
|
fileInfo.IsDirectory = NativeMethods.SafeCast(data, false);
|
|
_archive.GetProperty(i, ItemPropId.Encrypted, ref data);
|
|
fileInfo.Encrypted = NativeMethods.SafeCast(data, false);
|
|
_archive.GetProperty(i, ItemPropId.Crc, ref data);
|
|
fileInfo.Crc = NativeMethods.SafeCast<uint>(data, 0);
|
|
_archive.GetProperty(i, ItemPropId.Comment, ref data);
|
|
fileInfo.Comment = NativeMethods.SafeCast(data, "");
|
|
_archiveFileData.Add(fileInfo);
|
|
}
|
|
catch (InvalidCastException)
|
|
{
|
|
ThrowException(null, new SevenZipArchiveException("probably archive is corrupted."));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Getting archive properties
|
|
|
|
uint numProps = _archive.GetNumberOfArchiveProperties();
|
|
var archProps = new List<ArchiveProperty>((int)numProps);
|
|
for (uint i = 0; i < numProps; i++)
|
|
{
|
|
string propName;
|
|
ItemPropId propId;
|
|
ushort varType;
|
|
_archive.GetArchivePropertyInfo(i, out propName, out propId, out varType);
|
|
_archive.GetArchiveProperty(propId, ref data);
|
|
if (propId == ItemPropId.Solid)
|
|
{
|
|
_isSolid = NativeMethods.SafeCast(data, true);
|
|
}
|
|
// TODO Add more archive properties
|
|
if (PropIdToName.PropIdNames.ContainsKey(propId))
|
|
{
|
|
archProps.Add(new ArchiveProperty
|
|
{
|
|
Name = PropIdToName.PropIdNames[propId],
|
|
Value = data.Object
|
|
});
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine(
|
|
"An unknown archive property encountered (code " +
|
|
((int)propId).ToString(CultureInfo.InvariantCulture) + ')');
|
|
}
|
|
}
|
|
_archiveProperties = new ReadOnlyCollection<ArchiveProperty>(archProps);
|
|
if (!_isSolid.HasValue && _format == InArchiveFormat.Zip)
|
|
{
|
|
_isSolid = false;
|
|
}
|
|
if (!_isSolid.HasValue)
|
|
{
|
|
_isSolid = true;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
catch (Exception)
|
|
{
|
|
if (openCallback.ThrowException())
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (disposeStream)
|
|
{
|
|
_archive.Close();
|
|
_archiveStream = null;
|
|
}
|
|
_archiveFileInfoCollection = new ReadOnlyCollection<ArchiveFileInfo>(_archiveFileData);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Ensure that _archiveFileData is loaded.
|
|
/// </summary>
|
|
/// <param name="disposeStream">Dispose the archive stream after this operation.</param>
|
|
private void InitArchiveFileData(bool disposeStream)
|
|
{
|
|
if (_archiveFileData == null)
|
|
{
|
|
GetArchiveInfo(disposeStream);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Produces an array of indexes from 0 to the maximum value in the specified array
|
|
/// </summary>
|
|
/// <param name="indexes">The source array</param>
|
|
/// <returns>The array of indexes from 0 to the maximum value in the specified array</returns>
|
|
private static uint[] SolidIndexes(uint[] indexes)
|
|
{
|
|
#if CS4
|
|
int max = indexes.Aggregate(0, (current, i) => Math.Max(current, (int) i));
|
|
#else
|
|
int max = 0;
|
|
foreach (uint i in indexes)
|
|
{
|
|
max = Math.Max(max, (int)i);
|
|
}
|
|
#endif
|
|
if (max > 0)
|
|
{
|
|
max++;
|
|
var res = new uint[max];
|
|
for (int i = 0; i < max; i++)
|
|
{
|
|
res[i] = (uint)i;
|
|
}
|
|
return res;
|
|
}
|
|
return indexes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checkes whether all the indexes are valid.
|
|
/// </summary>
|
|
/// <param name="indexes">The indexes to check.</param>
|
|
/// <returns>True is valid; otherwise, false.</returns>
|
|
private static bool CheckIndexes(params int[] indexes)
|
|
{
|
|
#if CS4 // Wow, C# 4 is great!
|
|
return indexes.All(i => i >= 0);
|
|
#else
|
|
bool res = true;
|
|
foreach (int i in indexes)
|
|
{
|
|
if (i < 0)
|
|
{
|
|
res = false;
|
|
break;
|
|
}
|
|
}
|
|
return res;
|
|
#endif
|
|
}
|
|
|
|
private void ArchiveExtractCallbackCommonInit(ArchiveExtractCallback aec)
|
|
{
|
|
aec.Open += ((s, e) => { _unpackedSize = (long)e.TotalSize; });
|
|
aec.FileExtractionStarted += FileExtractionStartedEventProxy;
|
|
aec.FileExtractionFinished += FileExtractionFinishedEventProxy;
|
|
aec.Extracting += ExtractingEventProxy;
|
|
aec.FileExists += FileExistsEventProxy;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the IArchiveExtractCallback callback
|
|
/// </summary>
|
|
/// <param name="directory">The directory where extract the files</param>
|
|
/// <param name="filesCount">The number of files to be extracted</param>
|
|
/// <param name="actualIndexes">The list of actual indexes (solid archives support)</param>
|
|
/// <returns>The ArchiveExtractCallback callback</returns>
|
|
private ArchiveExtractCallback GetArchiveExtractCallback(string directory, int filesCount,
|
|
List<uint> actualIndexes)
|
|
{
|
|
var aec = String.IsNullOrEmpty(Password)
|
|
? new ArchiveExtractCallback(_archive, directory, filesCount, PreserveDirectoryStructure, actualIndexes, this)
|
|
: new ArchiveExtractCallback(_archive, directory, filesCount, PreserveDirectoryStructure, actualIndexes, Password, this);
|
|
ArchiveExtractCallbackCommonInit(aec);
|
|
return aec;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the IArchiveExtractCallback callback
|
|
/// </summary>
|
|
/// <param name="stream">The stream where extract the file</param>
|
|
/// <param name="index">The file index</param>
|
|
/// <param name="filesCount">The number of files to be extracted</param>
|
|
/// <returns>The ArchiveExtractCallback callback</returns>
|
|
private ArchiveExtractCallback GetArchiveExtractCallback(Stream stream, uint index, int filesCount)
|
|
{
|
|
var aec = String.IsNullOrEmpty(Password)
|
|
? new ArchiveExtractCallback(_archive, stream, filesCount, index, this)
|
|
: new ArchiveExtractCallback(_archive, stream, filesCount, index, Password, this);
|
|
ArchiveExtractCallbackCommonInit(aec);
|
|
return aec;
|
|
}
|
|
|
|
private void FreeArchiveExtractCallback(ArchiveExtractCallback callback)
|
|
{
|
|
callback.Open -= ((s, e) => { _unpackedSize = (long)e.TotalSize; });
|
|
callback.FileExtractionStarted -= FileExtractionStartedEventProxy;
|
|
callback.FileExtractionFinished -= FileExtractionFinishedEventProxy;
|
|
callback.Extracting -= ExtractingEventProxy;
|
|
callback.FileExists -= FileExistsEventProxy;
|
|
}
|
|
#endregion
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Checks if the specified stream supports extraction.
|
|
/// </summary>
|
|
/// <param name="stream">The stream to check.</param>
|
|
private static void ValidateStream(Stream stream)
|
|
{
|
|
if (stream == null)
|
|
{
|
|
throw new ArgumentNullException("stream");
|
|
}
|
|
if (!stream.CanSeek || !stream.CanRead)
|
|
{
|
|
throw new ArgumentException("The specified stream can not seek or read.", "stream");
|
|
}
|
|
if (stream.Length == 0)
|
|
{
|
|
throw new ArgumentException("The specified stream has zero length.", "stream");
|
|
}
|
|
}
|
|
|
|
#if UNMANAGED
|
|
|
|
#region IDisposable Members
|
|
|
|
private void CommonDispose()
|
|
{
|
|
if (_opened)
|
|
{
|
|
try
|
|
{
|
|
if (_archive != null)
|
|
{
|
|
_archive.Close();
|
|
}
|
|
}
|
|
catch (Exception) { }
|
|
}
|
|
_archive = null;
|
|
_archiveFileData = null;
|
|
_archiveProperties = null;
|
|
_archiveFileInfoCollection = null;
|
|
if (_inStream != null)
|
|
{
|
|
_inStream.Dispose();
|
|
}
|
|
_inStream = null;
|
|
if (_openCallback != null)
|
|
{
|
|
try
|
|
{
|
|
_openCallback.Dispose();
|
|
}
|
|
catch (ObjectDisposedException) { }
|
|
_openCallback = null;
|
|
}
|
|
if (_archiveStream != null)
|
|
{
|
|
if (_archiveStream is IDisposable)
|
|
{
|
|
try
|
|
{
|
|
if (_archiveStream is DisposeVariableWrapper)
|
|
{
|
|
(_archiveStream as DisposeVariableWrapper).DisposeStream = true;
|
|
}
|
|
(_archiveStream as IDisposable).Dispose();
|
|
}
|
|
catch (ObjectDisposedException) { }
|
|
_archiveStream = null;
|
|
}
|
|
}
|
|
SevenZipLibraryManager.FreeLibrary(this, _format);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Releases the unmanaged resources used by SevenZipExtractor.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
if (_asynchronousDisposeLock)
|
|
{
|
|
throw new InvalidOperationException("SevenZipExtractor instance must not be disposed " +
|
|
"while making an asynchronous method call.");
|
|
}
|
|
if (!_disposed)
|
|
{
|
|
CommonDispose();
|
|
}
|
|
_disposed = true;
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Core public Members
|
|
|
|
#region Events
|
|
|
|
/// <summary>
|
|
/// Occurs when a new file is going to be unpacked.
|
|
/// </summary>
|
|
/// <remarks>Occurs when 7-zip engine requests for an output stream for a new file to unpack in.</remarks>
|
|
public event EventHandler<FileInfoEventArgs> FileExtractionStarted;
|
|
|
|
/// <summary>
|
|
/// Occurs when a file has been successfully unpacked.
|
|
/// </summary>
|
|
public event EventHandler<FileInfoEventArgs> FileExtractionFinished;
|
|
|
|
/// <summary>
|
|
/// Occurs when the archive has been unpacked.
|
|
/// </summary>
|
|
public event EventHandler<EventArgs> ExtractionFinished;
|
|
|
|
/// <summary>
|
|
/// Occurs when data are being extracted.
|
|
/// </summary>
|
|
/// <remarks>Use this event for accurate progress handling and various ProgressBar.StepBy(e.PercentDelta) routines.</remarks>
|
|
public event EventHandler<ProgressEventArgs> Extracting;
|
|
|
|
/// <summary>
|
|
/// Occurs during the extraction when a file already exists.
|
|
/// </summary>
|
|
public event EventHandler<FileOverwriteEventArgs> FileExists;
|
|
|
|
#region Event proxies
|
|
/// <summary>
|
|
/// Event proxy for FileExtractionStarted.
|
|
/// </summary>
|
|
/// <param name="sender">The sender of the event.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void FileExtractionStartedEventProxy(object sender, FileInfoEventArgs e)
|
|
{
|
|
OnEvent(FileExtractionStarted, e, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event proxy for FileExtractionFinished.
|
|
/// </summary>
|
|
/// <param name="sender">The sender of the event.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void FileExtractionFinishedEventProxy(object sender, FileInfoEventArgs e)
|
|
{
|
|
OnEvent(FileExtractionFinished, e, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event proxy for Extractng.
|
|
/// </summary>
|
|
/// <param name="sender">The sender of the event.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void ExtractingEventProxy(object sender, ProgressEventArgs e)
|
|
{
|
|
OnEvent(Extracting, e, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Event proxy for FileExists.
|
|
/// </summary>
|
|
/// <param name="sender">The sender of the event.</param>
|
|
/// <param name="e">The event arguments.</param>
|
|
private void FileExistsEventProxy(object sender, FileOverwriteEventArgs e)
|
|
{
|
|
OnEvent(FileExists, e, true);
|
|
}
|
|
#endregion
|
|
#endregion
|
|
|
|
#region Properties
|
|
/// <summary>
|
|
/// Gets the collection of ArchiveFileInfo with all information about files in the archive
|
|
/// </summary>
|
|
public ReadOnlyCollection<ArchiveFileInfo> ArchiveFileData
|
|
{
|
|
get
|
|
{
|
|
DisposedCheck();
|
|
InitArchiveFileData(true);
|
|
return _archiveFileInfoCollection;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the properties for the current archive
|
|
/// </summary>
|
|
public ReadOnlyCollection<ArchiveProperty> ArchiveProperties
|
|
{
|
|
get
|
|
{
|
|
DisposedCheck();
|
|
InitArchiveFileData(true);
|
|
return _archiveProperties;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the collection of all file names contained in the archive.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Each get recreates the collection
|
|
/// </remarks>
|
|
public ReadOnlyCollection<string> ArchiveFileNames
|
|
{
|
|
get
|
|
{
|
|
DisposedCheck();
|
|
InitArchiveFileData(true);
|
|
var fileNames = new List<string>(_archiveFileData.Count);
|
|
#if CS4
|
|
fileNames.AddRange(_archiveFileData.Select(afi => afi.FileName));
|
|
#else
|
|
foreach (var afi in _archiveFileData)
|
|
{
|
|
fileNames.Add(afi.FileName);
|
|
}
|
|
#endif
|
|
return new ReadOnlyCollection<string>(fileNames);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the list of archive volume file names.
|
|
/// </summary>
|
|
public ReadOnlyCollection<string> VolumeFileNames
|
|
{
|
|
get
|
|
{
|
|
DisposedCheck();
|
|
InitArchiveFileData(true);
|
|
return _volumeFileNames;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Performs the archive integrity test.
|
|
/// </summary>
|
|
/// <returns>True is the archive is ok; otherwise, false.</returns>
|
|
public bool Check()
|
|
{
|
|
DisposedCheck();
|
|
try
|
|
{
|
|
InitArchiveFileData(false);
|
|
var archiveStream = GetArchiveStream(true);
|
|
var openCallback = GetArchiveOpenCallback();
|
|
if (!OpenArchive(archiveStream, openCallback))
|
|
{
|
|
return false;
|
|
}
|
|
using (var aec = GetArchiveExtractCallback("", (int)_filesCount, null))
|
|
{
|
|
try
|
|
{
|
|
CheckedExecute(
|
|
_archive.Extract(null, UInt32.MaxValue, 1, aec),
|
|
SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec);
|
|
}
|
|
finally
|
|
{
|
|
FreeArchiveExtractCallback(aec);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
if (_archive != null)
|
|
{
|
|
_archive.Close();
|
|
}
|
|
((InStreamWrapper)_archiveStream).Dispose();
|
|
_archiveStream = null;
|
|
_opened = false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#region ExtractFile overloads
|
|
/// <summary>
|
|
/// Unpacks the file by its name to the specified stream.
|
|
/// </summary>
|
|
/// <param name="fileName">The file full name in the archive file table.</param>
|
|
/// <param name="stream">The stream where the file is to be unpacked.</param>
|
|
public void ExtractFile(string fileName, Stream stream)
|
|
{
|
|
DisposedCheck();
|
|
InitArchiveFileData(false);
|
|
int index = -1;
|
|
foreach (ArchiveFileInfo afi in _archiveFileData)
|
|
{
|
|
if (afi.FileName == fileName && !afi.IsDirectory)
|
|
{
|
|
index = afi.Index;
|
|
break;
|
|
}
|
|
}
|
|
if (index == -1)
|
|
{
|
|
if (!ThrowException(null, new ArgumentOutOfRangeException(
|
|
"fileName",
|
|
"The specified file name was not found in the archive file table.")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ExtractFile(index, stream);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unpacks the file by its index to the specified stream.
|
|
/// </summary>
|
|
/// <param name="index">Index in the archive file table.</param>
|
|
/// <param name="stream">The stream where the file is to be unpacked.</param>
|
|
public void ExtractFile(int index, Stream stream)
|
|
{
|
|
DisposedCheck();
|
|
ClearExceptions();
|
|
if (!CheckIndexes(index))
|
|
{
|
|
if (!ThrowException(null, new ArgumentException("The index must be more or equal to zero.", "index")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if (!stream.CanWrite)
|
|
{
|
|
if (!ThrowException(null, new ArgumentException("The specified stream can not be written.", "stream")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
InitArchiveFileData(false);
|
|
if (index > _filesCount - 1)
|
|
{
|
|
if (!ThrowException(null, new ArgumentOutOfRangeException(
|
|
"index", "The specified index is greater than the archive files count.")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
var indexes = new[] {(uint) index};
|
|
if (_isSolid.Value)
|
|
{
|
|
indexes = SolidIndexes(indexes);
|
|
}
|
|
var archiveStream = GetArchiveStream(false);
|
|
var openCallback = GetArchiveOpenCallback();
|
|
if (!OpenArchive(archiveStream, openCallback))
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
using (var aec = GetArchiveExtractCallback(stream, (uint) index, indexes.Length))
|
|
{
|
|
try
|
|
{
|
|
CheckedExecute(
|
|
_archive.Extract(indexes, (uint) indexes.Length, 0, aec),
|
|
SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec);
|
|
}
|
|
finally
|
|
{
|
|
FreeArchiveExtractCallback(aec);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
if (openCallback.ThrowException())
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
OnEvent(ExtractionFinished, EventArgs.Empty, false);
|
|
ThrowUserException();
|
|
}
|
|
#endregion
|
|
|
|
#region ExtractFiles overloads
|
|
/// <summary>
|
|
/// Unpacks files by their indices to the specified directory.
|
|
/// </summary>
|
|
/// <param name="indexes">indexes of the files in the archive file table.</param>
|
|
/// <param name="directory">Directory where the files are to be unpacked.</param>
|
|
public void ExtractFiles(string directory, params int[] indexes)
|
|
{
|
|
DisposedCheck();
|
|
ClearExceptions();
|
|
if (!CheckIndexes(indexes))
|
|
{
|
|
if (
|
|
!ThrowException(null, new ArgumentException("The indexes must be more or equal to zero.", "indexes")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
InitArchiveFileData(false);
|
|
|
|
#region Indexes stuff
|
|
|
|
var uindexes = new uint[indexes.Length];
|
|
for (int i = 0; i < indexes.Length; i++)
|
|
{
|
|
uindexes[i] = (uint) indexes[i];
|
|
}
|
|
#if CS4
|
|
if (uindexes.Where(i => i >= _filesCount).Any(
|
|
i => !ThrowException(null,
|
|
new ArgumentOutOfRangeException("indexes",
|
|
"Index must be less than " +
|
|
_filesCount.Value.ToString(
|
|
CultureInfo.InvariantCulture) + "!"))))
|
|
{
|
|
return;
|
|
}
|
|
#else
|
|
foreach (uint i in uindexes)
|
|
{
|
|
if (i >= _filesCount)
|
|
{
|
|
if (!ThrowException(null,
|
|
new ArgumentOutOfRangeException("indexes",
|
|
"Index must be less than " +
|
|
_filesCount.Value.ToString(
|
|
CultureInfo.InvariantCulture) + "!")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
var origIndexes = new List<uint>(uindexes);
|
|
origIndexes.Sort();
|
|
uindexes = origIndexes.ToArray();
|
|
if (_isSolid.Value)
|
|
{
|
|
uindexes = SolidIndexes(uindexes);
|
|
}
|
|
|
|
#endregion
|
|
|
|
try
|
|
{
|
|
IInStream archiveStream;
|
|
using ((archiveStream = GetArchiveStream(origIndexes.Count != 1)) as IDisposable)
|
|
{
|
|
var openCallback = GetArchiveOpenCallback();
|
|
if (!OpenArchive(archiveStream, openCallback))
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
using (var aec = GetArchiveExtractCallback(directory, (int) _filesCount, origIndexes))
|
|
{
|
|
try
|
|
{
|
|
CheckedExecute(
|
|
_archive.Extract(uindexes, (uint) uindexes.Length, 0, aec),
|
|
SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec);
|
|
}
|
|
finally
|
|
{
|
|
FreeArchiveExtractCallback(aec);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
if (openCallback.ThrowException())
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
OnEvent(ExtractionFinished, EventArgs.Empty, false);
|
|
}
|
|
finally
|
|
{
|
|
if (origIndexes.Count > 1)
|
|
{
|
|
if (_archive != null)
|
|
{
|
|
_archive.Close();
|
|
}
|
|
_archiveStream = null;
|
|
_opened = false;
|
|
}
|
|
}
|
|
ThrowUserException();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unpacks files by their full names to the specified directory.
|
|
/// </summary>
|
|
/// <param name="fileNames">Full file names in the archive file table.</param>
|
|
/// <param name="directory">Directory where the files are to be unpacked.</param>
|
|
public void ExtractFiles(string directory, params string[] fileNames)
|
|
{
|
|
DisposedCheck();
|
|
InitArchiveFileData(false);
|
|
var indexes = new List<int>(fileNames.Length);
|
|
var archiveFileNames = new List<string>(ArchiveFileNames);
|
|
foreach (string fn in fileNames)
|
|
{
|
|
if (!archiveFileNames.Contains(fn))
|
|
{
|
|
if (
|
|
!ThrowException(null,
|
|
new ArgumentOutOfRangeException("fileNames",
|
|
"File \"" + fn +
|
|
"\" was not found in the archive file table.")))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (ArchiveFileInfo afi in _archiveFileData)
|
|
{
|
|
if (afi.FileName == fn && !afi.IsDirectory)
|
|
{
|
|
indexes.Add(afi.Index);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ExtractFiles(directory, indexes.ToArray());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extracts files from the archive, giving a callback the choice what
|
|
/// to do with each file. The order of the files is given by the archive.
|
|
/// 7-Zip (and any other solid) archives are NOT supported.
|
|
/// </summary>
|
|
/// <param name="extractFileCallback">The callback to call for each file in the archive.</param>
|
|
public void ExtractFiles(ExtractFileCallback extractFileCallback)
|
|
{
|
|
DisposedCheck();
|
|
InitArchiveFileData(false);
|
|
if (IsSolid)
|
|
{
|
|
// solid strategy
|
|
}
|
|
else
|
|
{
|
|
foreach (ArchiveFileInfo archiveFileInfo in ArchiveFileData)
|
|
{
|
|
var extractFileCallbackArgs = new ExtractFileCallbackArgs(archiveFileInfo);
|
|
extractFileCallback(extractFileCallbackArgs);
|
|
if (extractFileCallbackArgs.CancelExtraction)
|
|
{
|
|
break;
|
|
}
|
|
if (extractFileCallbackArgs.ExtractToStream != null || extractFileCallbackArgs.ExtractToFile != null)
|
|
{
|
|
bool callDone = false;
|
|
try
|
|
{
|
|
if (extractFileCallbackArgs.ExtractToStream != null)
|
|
{
|
|
ExtractFile(archiveFileInfo.Index, extractFileCallbackArgs.ExtractToStream);
|
|
}
|
|
else
|
|
{
|
|
using (var file = new FileStream(extractFileCallbackArgs.ExtractToFile, FileMode.CreateNew,
|
|
FileAccess.Write, FileShare.None, 8192))
|
|
{
|
|
ExtractFile(archiveFileInfo.Index, file);
|
|
}
|
|
}
|
|
callDone = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
extractFileCallbackArgs.Exception = ex;
|
|
extractFileCallbackArgs.Reason = ExtractFileCallbackReason.Failure;
|
|
extractFileCallback(extractFileCallbackArgs);
|
|
if (!ThrowException(null, ex))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if (callDone)
|
|
{
|
|
extractFileCallbackArgs.Reason = ExtractFileCallbackReason.Done;
|
|
extractFileCallback(extractFileCallbackArgs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// Unpacks the whole archive to the specified directory.
|
|
/// </summary>
|
|
/// <param name="directory">The directory where the files are to be unpacked.</param>
|
|
public void ExtractArchive(string directory)
|
|
{
|
|
DisposedCheck();
|
|
ClearExceptions();
|
|
InitArchiveFileData(false);
|
|
try
|
|
{
|
|
IInStream archiveStream;
|
|
using ((archiveStream = GetArchiveStream(true)) as IDisposable)
|
|
{
|
|
var openCallback = GetArchiveOpenCallback();
|
|
if (!OpenArchive(archiveStream, openCallback))
|
|
{
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
using (var aec = GetArchiveExtractCallback(directory, (int) _filesCount, null))
|
|
{
|
|
try
|
|
{
|
|
CheckedExecute(
|
|
_archive.Extract(null, UInt32.MaxValue, 0, aec),
|
|
SevenZipExtractionFailedException.DEFAULT_MESSAGE, aec);
|
|
OnEvent(ExtractionFinished, EventArgs.Empty, false);
|
|
}
|
|
finally
|
|
{
|
|
FreeArchiveExtractCallback(aec);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
if (openCallback.ThrowException())
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (_archive != null)
|
|
{
|
|
_archive.Close();
|
|
}
|
|
_archiveStream = null;
|
|
_opened = false;
|
|
}
|
|
ThrowUserException();
|
|
}
|
|
#endregion
|
|
|
|
#endif
|
|
|
|
#region LZMA SDK functions
|
|
|
|
internal static byte[] GetLzmaProperties(Stream inStream, out long outSize)
|
|
{
|
|
var lzmAproperties = new byte[5];
|
|
if (inStream.Read(lzmAproperties, 0, 5) != 5)
|
|
{
|
|
throw new LzmaException();
|
|
}
|
|
outSize = 0;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
int b = inStream.ReadByte();
|
|
if (b < 0)
|
|
{
|
|
throw new LzmaException();
|
|
}
|
|
outSize |= ((long) (byte) b) << (i << 3);
|
|
}
|
|
return lzmAproperties;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decompress the specified stream (C# inside)
|
|
/// </summary>
|
|
/// <param name="inStream">The source compressed stream</param>
|
|
/// <param name="outStream">The destination uncompressed stream</param>
|
|
/// <param name="inLength">The length of compressed data (null for inStream.Length)</param>
|
|
/// <param name="codeProgressEvent">The event for handling the code progress</param>
|
|
public static void DecompressStream(Stream inStream, Stream outStream, int? inLength,
|
|
EventHandler<ProgressEventArgs> codeProgressEvent)
|
|
{
|
|
if (!inStream.CanRead || !outStream.CanWrite)
|
|
{
|
|
throw new ArgumentException("The specified streams are invalid.");
|
|
}
|
|
var decoder = new Decoder();
|
|
long outSize, inSize = (inLength.HasValue ? inLength.Value : inStream.Length) - inStream.Position;
|
|
decoder.SetDecoderProperties(GetLzmaProperties(inStream, out outSize));
|
|
decoder.Code(
|
|
inStream, outStream, inSize, outSize,
|
|
new LzmaProgressCallback(inSize, codeProgressEvent));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decompress byte array compressed with LZMA algorithm (C# inside)
|
|
/// </summary>
|
|
/// <param name="data">Byte array to decompress</param>
|
|
/// <returns>Decompressed byte array</returns>
|
|
public static byte[] ExtractBytes(byte[] data)
|
|
{
|
|
using (var inStream = new MemoryStream(data))
|
|
{
|
|
var decoder = new Decoder();
|
|
inStream.Seek(0, 0);
|
|
using (var outStream = new MemoryStream())
|
|
{
|
|
long outSize;
|
|
decoder.SetDecoderProperties(GetLzmaProperties(inStream, out outSize));
|
|
decoder.Code(inStream, outStream, inStream.Length - inStream.Position, outSize, null);
|
|
return outStream.ToArray();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |