#pragma warning disable 3021 /* 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 . */ #define DOTNET20 #define UNMANAGED #define COMPRESS 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 { /// /// Class to unpack data from archives supported by 7-Zip. /// /// /// using (var extr = new SevenZipExtractor(@"C:\Test.7z")) /// { /// extr.ExtractArchive(@"C:\TestDirectory"); /// } /// public sealed partial class SevenZipExtractor #if UNMANAGED : SevenZipBase, IDisposable #endif { #if UNMANAGED private List _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 _archiveFileInfoCollection; private ReadOnlyCollection _archiveProperties; private ReadOnlyCollection _volumeFileNames; /// /// This is used to lock possible Dispose() calls. /// private bool _asynchronousDisposeLock; #region Constructors /// /// General initialization function. /// /// The archive file name. 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; } } } } /// /// General initialization function. /// /// The stream to read the archive from. 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; } } } } /// /// Initializes a new instance of SevenZipExtractor class. /// /// The stream to read the archive from. /// Use SevenZipExtractor(string) to extract from disk, though it is not necessary. /// The archive format is guessed by the signature. public SevenZipExtractor(Stream archiveStream) { Init(archiveStream); } /// /// Initializes a new instance of SevenZipExtractor class. /// /// The stream to read the archive from. /// Use SevenZipExtractor(string) to extract from disk, though it is not necessary. /// Manual archive format setup. You SHOULD NOT normally specify it this way. /// Instead, use SevenZipExtractor(Stream archiveStream), that constructor /// automatically detects the archive format. public SevenZipExtractor(Stream archiveStream, InArchiveFormat format) { _format = format; Init(archiveStream); } /// /// Initializes a new instance of SevenZipExtractor class. /// /// The archive full file name. public SevenZipExtractor(string archiveFullName) { Init(archiveFullName); } /// /// Initializes a new instance of SevenZipExtractor class. /// /// The archive full file name. /// Manual archive format setup. You SHOULD NOT normally specify it this way. /// Instead, use SevenZipExtractor(string archiveFullName), that constructor /// automatically detects the archive format. public SevenZipExtractor(string archiveFullName, InArchiveFormat format) { _format = format; Init(archiveFullName); } /// /// Initializes a new instance of SevenZipExtractor class. /// /// The archive full file name. /// Password for an encrypted archive. public SevenZipExtractor(string archiveFullName, string password) : base(password) { Init(archiveFullName); } /// /// Initializes a new instance of SevenZipExtractor class. /// /// The archive full file name. /// Password for an encrypted archive. /// 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. public SevenZipExtractor(string archiveFullName, string password, InArchiveFormat format) : base(password) { _format = format; Init(archiveFullName); } /// /// Initializes a new instance of SevenZipExtractor class. /// /// The stream to read the archive from. /// Password for an encrypted archive. /// The archive format is guessed by the signature. public SevenZipExtractor(Stream archiveStream, string password) : base(password) { Init(archiveStream); } /// /// Initializes a new instance of SevenZipExtractor class. /// /// The stream to read the archive from. /// Password for an encrypted archive. /// 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. public SevenZipExtractor(Stream archiveStream, string password, InArchiveFormat format) : base(password) { _format = format; Init(archiveStream); } #endregion #region Properties /// /// Gets or sets archive full file name /// public string FileName { get { DisposedCheck(); return _fileName; } } /// /// Gets the size of the archive file /// public long PackedSize { get { DisposedCheck(); return _packedSize.HasValue ? _packedSize.Value : _fileName != null ? (new FileInfo(_fileName)).Length : -1; } } /// /// Gets the size of unpacked archive data /// public long UnpackedSize { get { DisposedCheck(); if (!_unpackedSize.HasValue) { return -1; } return _unpackedSize.Value; } } /// /// Gets a value indicating whether the archive is solid /// public bool IsSolid { get { DisposedCheck(); if (!_isSolid.HasValue) { GetArchiveInfo(true); } Debug.Assert(_isSolid != null); return _isSolid.Value; } } /// /// Gets the number of files in the archive /// [CLSCompliant(false)] public uint FilesCount { get { DisposedCheck(); if (!_filesCount.HasValue) { GetArchiveInfo(true); } Debug.Assert(_filesCount != null); return _filesCount.Value; } } /// /// Gets archive format /// public InArchiveFormat Format { get { DisposedCheck(); return _format; } } /// /// Gets or sets the value indicating whether to preserve the directory structure of extracted files. /// public bool PreserveDirectoryStructure { get; set; } #endregion /// /// Checked whether the class was disposed. /// /// 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)); } /// /// Gets the archive input stream. /// /// The archive input wrapper stream. 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; } /// /// Opens the archive and throws exceptions or returns OperationResult.DataError if any error occurs. /// /// The IInStream compliant class instance, that is, the input stream. /// The ArchiveOpenCallback instance. /// OperationResult.Ok if Open() succeeds. private OperationResult OpenArchiveInner(IInStream archiveStream, IArchiveOpenCallback openCallback) { ulong checkPos = 1 << 15; int res = _archive.Open(archiveStream, ref checkPos, openCallback); return (OperationResult)res; } /// /// Opens the archive and throws exceptions or returns OperationResult.DataError if any error occurs. /// /// The IInStream compliant class instance, that is, the input stream. /// The ArchiveOpenCallback instance. /// True if Open() succeeds; otherwise, false. 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(openCallback.VolumeFileNames); _opened = true; } return true; } /// /// Retrieves all information about the archive. /// /// 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((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(data, 0); if (fileInfo.Size == 0) { fileInfo.Size = NativeMethods.SafeCast(data, 0); } _archive.GetProperty(i, ItemPropId.Attributes, ref data); fileInfo.Attributes = NativeMethods.SafeCast(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(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((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(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(_archiveFileData); } } /// /// Ensure that _archiveFileData is loaded. /// /// Dispose the archive stream after this operation. private void InitArchiveFileData(bool disposeStream) { if (_archiveFileData == null) { GetArchiveInfo(disposeStream); } } /// /// Produces an array of indexes from 0 to the maximum value in the specified array /// /// The source array /// The array of indexes from 0 to the maximum value in the specified array 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; } /// /// Checkes whether all the indexes are valid. /// /// The indexes to check. /// True is valid; otherwise, false. 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; } /// /// Gets the IArchiveExtractCallback callback /// /// The directory where extract the files /// The number of files to be extracted /// The list of actual indexes (solid archives support) /// The ArchiveExtractCallback callback private ArchiveExtractCallback GetArchiveExtractCallback(string directory, int filesCount, List 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; } /// /// Gets the IArchiveExtractCallback callback /// /// The stream where extract the file /// The file index /// The number of files to be extracted /// The ArchiveExtractCallback callback 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 /// /// Checks if the specified stream supports extraction. /// /// The stream to check. 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); } /// /// Releases the unmanaged resources used by SevenZipExtractor. /// 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 /// /// Occurs when a new file is going to be unpacked. /// /// Occurs when 7-zip engine requests for an output stream for a new file to unpack in. public event EventHandler FileExtractionStarted; /// /// Occurs when a file has been successfully unpacked. /// public event EventHandler FileExtractionFinished; /// /// Occurs when the archive has been unpacked. /// public event EventHandler ExtractionFinished; /// /// Occurs when data are being extracted. /// /// Use this event for accurate progress handling and various ProgressBar.StepBy(e.PercentDelta) routines. public event EventHandler Extracting; /// /// Occurs during the extraction when a file already exists. /// public event EventHandler FileExists; #region Event proxies /// /// Event proxy for FileExtractionStarted. /// /// The sender of the event. /// The event arguments. private void FileExtractionStartedEventProxy(object sender, FileInfoEventArgs e) { OnEvent(FileExtractionStarted, e, true); } /// /// Event proxy for FileExtractionFinished. /// /// The sender of the event. /// The event arguments. private void FileExtractionFinishedEventProxy(object sender, FileInfoEventArgs e) { OnEvent(FileExtractionFinished, e, true); } /// /// Event proxy for Extractng. /// /// The sender of the event. /// The event arguments. private void ExtractingEventProxy(object sender, ProgressEventArgs e) { OnEvent(Extracting, e, false); } /// /// Event proxy for FileExists. /// /// The sender of the event. /// The event arguments. private void FileExistsEventProxy(object sender, FileOverwriteEventArgs e) { OnEvent(FileExists, e, true); } #endregion #endregion #region Properties /// /// Gets the collection of ArchiveFileInfo with all information about files in the archive /// public ReadOnlyCollection ArchiveFileData { get { DisposedCheck(); InitArchiveFileData(true); return _archiveFileInfoCollection; } } /// /// Gets the properties for the current archive /// public ReadOnlyCollection ArchiveProperties { get { DisposedCheck(); InitArchiveFileData(true); return _archiveProperties; } } /// /// Gets the collection of all file names contained in the archive. /// /// /// Each get recreates the collection /// public ReadOnlyCollection ArchiveFileNames { get { DisposedCheck(); InitArchiveFileData(true); var fileNames = new List(_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(fileNames); } } /// /// Gets the list of archive volume file names. /// public ReadOnlyCollection VolumeFileNames { get { DisposedCheck(); InitArchiveFileData(true); return _volumeFileNames; } } #endregion /// /// Performs the archive integrity test. /// /// True is the archive is ok; otherwise, false. 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 /// /// Unpacks the file by its name to the specified stream. /// /// The file full name in the archive file table. /// The stream where the file is to be unpacked. 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); } } /// /// Unpacks the file by its index to the specified stream. /// /// Index in the archive file table. /// The stream where the file is to be unpacked. 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 /// /// Unpacks files by their indices to the specified directory. /// /// indexes of the files in the archive file table. /// Directory where the files are to be unpacked. 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(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(); } /// /// Unpacks files by their full names to the specified directory. /// /// Full file names in the archive file table. /// Directory where the files are to be unpacked. public void ExtractFiles(string directory, params string[] fileNames) { DisposedCheck(); InitArchiveFileData(false); var indexes = new List(fileNames.Length); var archiveFileNames = new List(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()); } /// /// 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. /// /// The callback to call for each file in the archive. 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 /// /// Unpacks the whole archive to the specified directory. /// /// The directory where the files are to be unpacked. 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; } /// /// Decompress the specified stream (C# inside) /// /// The source compressed stream /// The destination uncompressed stream /// The length of compressed data (null for inStream.Length) /// The event for handling the code progress public static void DecompressStream(Stream inStream, Stream outStream, int? inLength, EventHandler 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)); } /// /// Decompress byte array compressed with LZMA algorithm (C# inside) /// /// Byte array to decompress /// Decompressed byte array 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 } }