123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818 |
- #if UNMANAGED
- using System.Runtime.InteropServices;
- namespace SevenZip;
- public record CompressStreamEntry(Stream? Stream, DateTimeOffset? CreateTime, DateTimeOffset? LastWrite, DateTimeOffset? LastAccess)
- {
- public CompressStreamEntry(Stream? Stream, DateTimeOffset? fileTime) : this(Stream, fileTime, fileTime, fileTime)
- {
- }
- }
- internal sealed class ArchiveUpdateCallbackWithFileTime : CallbackBase, IArchiveUpdateCallback, ICryptoGetTextPassword2,
- IDisposable
- {
- #region Fields
- /// <summary>
- /// _files.Count if do not count directories
- /// </summary>
- private int _actualFilesCount;
- /// <summary>
- /// For Compressing event.
- /// </summary>
- private long _bytesCount;
- private long _bytesWritten;
- private long _bytesWrittenOld;
- private SevenZipCompressor _compressor;
- /// <summary>
- /// No directories.
- /// </summary>
- private bool _directoryStructure;
- /// <summary>
- /// Rate of the done work from [0, 1]
- /// </summary>
- private float _doneRate;
- /// <summary>
- /// The names of the archive entries
- /// </summary>
- private string[] _entries;
- /// <summary>
- /// Array of files to pack
- /// </summary>
- private FileInfo[]? _files;
- private InStreamWrapper _fileStream;
- private uint _indexInArchive;
- private uint _indexOffset;
- /// <summary>
- /// Common root of file names length.
- /// </summary>
- private int _rootLength;
- /// <summary>
- /// Input streams to be compressed.
- /// </summary>
- private CompressStreamEntry[] _streamEntries;
- private UpdateData _updateData;
- private List<InStreamWrapper> _wrappersToDispose;
- /// <summary>
- /// Gets or sets the default item name used in MemoryStream compression.
- /// </summary>
- public string DefaultItemName { private get; set; }
- /// <summary>
- /// Gets or sets the value indicating whether to compress as fast as possible, without calling events.
- /// </summary>
- public bool FastCompression { private get; set; }
- private int _memoryPressure;
- #endregion
- #region Constructors
- /// <summary>
- /// Initializes a new instance of the ArchiveUpdateCallback class
- /// </summary>
- /// <param name="files">Array of files to pack</param>
- /// <param name="rootLength">Common file names root length</param>
- /// <param name="compressor">The owner of the callback</param>
- /// <param name="updateData">The compression parameters.</param>
- /// <param name="directoryStructure">Preserve directory structure.</param>
- public ArchiveUpdateCallbackWithFileTime(
- FileInfo[] files, int rootLength,
- SevenZipCompressor compressor, UpdateData updateData, bool directoryStructure)
- {
- Init(files, rootLength, compressor, updateData, directoryStructure);
- }
- /// <summary>
- /// Initializes a new instance of the ArchiveUpdateCallback class
- /// </summary>
- /// <param name="files">Array of files to pack</param>
- /// <param name="rootLength">Common file names root length</param>
- /// <param name="password">The archive password</param>
- /// <param name="compressor">The owner of the callback</param>
- /// <param name="updateData">The compression parameters.</param>
- /// <param name="directoryStructure">Preserve directory structure.</param>
- public ArchiveUpdateCallbackWithFileTime(
- FileInfo[] files, int rootLength, string password,
- SevenZipCompressor compressor, UpdateData updateData, bool directoryStructure)
- : base(password)
- {
- Init(files, rootLength, compressor, updateData, directoryStructure);
- }
- /// <summary>
- /// Initializes a new instance of the ArchiveUpdateCallback class
- /// </summary>
- /// <param name="stream">The input stream</param>
- /// <param name="compressor">The owner of the callback</param>
- /// <param name="updateData">The compression parameters.</param>
- /// <param name="directoryStructure">Preserve directory structure.</param>
- public ArchiveUpdateCallbackWithFileTime(
- Stream stream, SevenZipCompressor compressor, UpdateData updateData, bool directoryStructure)
- {
- Init(stream, compressor, updateData, directoryStructure);
- }
- /// <summary>
- /// Initializes a new instance of the ArchiveUpdateCallback class
- /// </summary>
- /// <param name="stream">The input stream</param>
- /// <param name="password">The archive password</param>
- /// <param name="compressor">The owner of the callback</param>
- /// <param name="updateData">The compression parameters.</param>
- /// <param name="directoryStructure">Preserve directory structure.</param>
- public ArchiveUpdateCallbackWithFileTime(
- Stream stream, string password, SevenZipCompressor compressor, UpdateData updateData,
- bool directoryStructure)
- : base(password)
- {
- Init(stream, compressor, updateData, directoryStructure);
- }
- /// <summary>
- /// Initializes a new instance of the ArchiveUpdateCallback class
- /// </summary>
- /// <param name="streamDict">Dictionary<file stream, name of the archive entry></param>
- /// <param name="compressor">The owner of the callback</param>
- /// <param name="updateData">The compression parameters.</param>
- /// <param name="directoryStructure">Preserve directory structure.</param>
- public ArchiveUpdateCallbackWithFileTime(
- IDictionary<string, CompressStreamEntry> streamDict,
- SevenZipCompressor compressor, UpdateData updateData, bool directoryStructure)
- {
- Init(streamDict, compressor, updateData, directoryStructure);
- }
- /// <summary>
- /// Initializes a new instance of the ArchiveUpdateCallback class
- /// </summary>
- /// <param name="streamDict">Dictionary<file stream, name of the archive entry></param>
- /// <param name="password">The archive password</param>
- /// <param name="compressor">The owner of the callback</param>
- /// <param name="updateData">The compression parameters.</param>
- /// <param name="directoryStructure">Preserve directory structure.</param>
- public ArchiveUpdateCallbackWithFileTime(
- IDictionary<string, CompressStreamEntry> streamDict, string password,
- SevenZipCompressor compressor, UpdateData updateData, bool directoryStructure)
- : base(password)
- {
- Init(streamDict, compressor, updateData, directoryStructure);
- }
- private void CommonInit(SevenZipCompressor compressor, UpdateData updateData, bool directoryStructure)
- {
- _compressor = compressor;
- _indexInArchive = updateData.FilesCount;
- _indexOffset = updateData.Mode != InternalCompressionMode.Append ? 0 : _indexInArchive;
- if (_compressor.ArchiveFormat == OutArchiveFormat.Zip)
- {
- _wrappersToDispose = new List<InStreamWrapper>();
- }
- _updateData = updateData;
- _directoryStructure = directoryStructure;
- DefaultItemName = "default";
- }
- private void Init(
- FileInfo[] files, int rootLength, SevenZipCompressor compressor,
- UpdateData updateData, bool directoryStructure)
- {
- _files = files;
- _rootLength = rootLength;
- if (files != null)
- {
- foreach (var fi in files)
- {
- if (fi.Exists)
- {
- _bytesCount += fi.Length;
- if ((fi.Attributes & FileAttributes.Directory) == 0)
- {
- _actualFilesCount++;
- }
- }
- }
- }
- CommonInit(compressor, updateData, directoryStructure);
- }
- private void Init(
- Stream stream, SevenZipCompressor compressor, UpdateData updateData, bool directoryStructure)
- {
- _fileStream = new InStreamWrapper(stream, false);
- _fileStream.BytesRead += IntEventArgsHandler;
- _actualFilesCount = 1;
- try
- {
- _bytesCount = stream.Length;
- }
- catch (NotSupportedException)
- {
- _bytesCount = -1;
- }
- try
- {
- stream.Seek(0, SeekOrigin.Begin);
- }
- catch (NotSupportedException)
- {
- _bytesCount = -1;
- }
- CommonInit(compressor, updateData, directoryStructure);
- }
- private void Init(
- IDictionary<string, CompressStreamEntry> streamDict,
- SevenZipCompressor compressor, UpdateData updateData, bool directoryStructure)
- {
- _streamEntries = new CompressStreamEntry[streamDict.Count];
- streamDict.Values.ToArray().CopyTo(_streamEntries, 0);
- _entries = new string[streamDict.Count];
- streamDict.Keys.CopyTo(_entries, 0);
- _actualFilesCount = streamDict.Count;
- foreach (var entry in _streamEntries)
- {
- if (entry.Stream != null)
- {
- _bytesCount += entry.Stream.Length;
- }
- }
- CommonInit(compressor, updateData, directoryStructure);
- }
- #endregion
- /// <summary>
- /// Gets or sets the dictionary size.
- /// </summary>
- public float DictionarySize
- {
- set
- {
- _memoryPressure = (int)(value * 1024 * 1024);
- GC.AddMemoryPressure(_memoryPressure);
- }
- }
- /// <summary>
- /// Raises events for the GetStream method.
- /// </summary>
- /// <param name="index">The current item index.</param>
- /// <returns>True if not cancelled; otherwise, false.</returns>
- private bool EventsForGetStream(uint index)
- {
- if (!FastCompression)
- {
- if (_fileStream != null)
- {
- _fileStream.BytesRead += IntEventArgsHandler;
- }
- _doneRate += 1.0f / _actualFilesCount;
- var fiea = new FileNameEventArgs(_files != null ? _files[index].Name : _entries[index],
- PercentDoneEventArgs.ProducePercentDone(_doneRate));
- OnFileCompression(fiea);
- if (fiea.Cancel)
- {
- Canceled = true;
- return false;
- }
- }
- return true;
- }
- #region Events
- /// <summary>
- /// Occurs when the next file is going to be packed.
- /// </summary>
- /// <remarks>Occurs when 7-zip engine requests for an input stream for the next file to pack it</remarks>
- public event EventHandler<FileNameEventArgs> FileCompressionStarted;
- /// <summary>
- /// Occurs when data are being compressed.
- /// </summary>
- public event EventHandler<ProgressEventArgs> Compressing;
- /// <summary>
- /// Occurs when the current file was compressed.
- /// </summary>
- public event EventHandler FileCompressionFinished;
- private void OnFileCompression(FileNameEventArgs e)
- {
- FileCompressionStarted?.Invoke(this, e);
- }
- private void OnCompressing(ProgressEventArgs e)
- {
- Compressing?.Invoke(this, e);
- }
- private void OnFileCompressionFinished(EventArgs e)
- {
- FileCompressionFinished?.Invoke(this, e);
- }
- #endregion
- #region IArchiveUpdateCallback Members
- public void SetTotal(ulong total) { }
- public void SetCompleted(ref ulong completeValue) { }
- public int GetUpdateItemInfo(uint index, ref int newData, ref int newProperties, ref uint indexInArchive)
- {
- switch (_updateData.Mode)
- {
- case InternalCompressionMode.Create:
- newData = 1;
- newProperties = 1;
- indexInArchive = uint.MaxValue;
- break;
- case InternalCompressionMode.Append:
- if (index < _indexInArchive)
- {
- newData = 0;
- newProperties = 0;
- indexInArchive = index;
- }
- else
- {
- newData = 1;
- newProperties = 1;
- indexInArchive = uint.MaxValue;
- }
- break;
- case InternalCompressionMode.Modify:
- newData = 0;
- newProperties = Convert.ToInt32(_updateData.FileNamesToModify.ContainsKey((int)index)
- && _updateData.FileNamesToModify[(int)index] != null);
- if (_updateData.FileNamesToModify.ContainsKey((int)index)
- && _updateData.FileNamesToModify[(int)index] == null)
- {
- indexInArchive = (uint)_updateData.ArchiveFileData.Count;
- foreach (var pairModification in _updateData.FileNamesToModify)
- {
- if (pairModification.Key <= index && (pairModification.Value == null))
- {
- do
- {
- indexInArchive--;
- } while (indexInArchive > 0
- && _updateData.FileNamesToModify.ContainsKey((int)indexInArchive)
- && _updateData.FileNamesToModify[(int)indexInArchive] == null);
- }
- }
- }
- else
- {
- indexInArchive = index;
- }
- break;
- }
- return 0;
- }
- public int GetProperty(uint index, ItemPropId propID, ref PropVariant value)
- {
- index -= _indexOffset;
- try
- {
- switch (propID)
- {
- case ItemPropId.IsAnti:
- value.VarType = VarEnum.VT_BOOL;
- value.UInt64Value = 0;
- break;
- case ItemPropId.Path:
- #region Path
- value.VarType = VarEnum.VT_BSTR;
- string val = DefaultItemName;
- if (_updateData.Mode != InternalCompressionMode.Modify)
- {
- if (_files == null)
- {
- if (_entries != null)
- {
- val = _entries[index];
- }
- }
- else
- {
- if (_directoryStructure)
- {
- if (_rootLength > 0)
- {
- val = _files[index].FullName.Substring(_rootLength);
- }
- else
- {
- val = _files[index].FullName[0] + _files[index].FullName.Substring(2);
- }
- }
- else
- {
- val = _files[index].Name;
- }
- }
- }
- else
- {
- val = _updateData.FileNamesToModify[(int)index];
- }
- value.Value = Marshal.StringToBSTR(val);
- #endregion
- break;
- case ItemPropId.IsDirectory:
- value.VarType = VarEnum.VT_BOOL;
- if (_updateData.Mode != InternalCompressionMode.Modify)
- {
- if (_files == null)
- {
- if (_streamEntries == null)
- {
- value.UInt64Value = 0;
- }
- else
- {
- value.UInt64Value = (ulong)(_streamEntries[index] == null ? 1 : 0);
- }
- }
- else
- {
- value.UInt64Value = (byte)(_files[index].Attributes & FileAttributes.Directory);
- }
- }
- else
- {
- value.UInt64Value = Convert.ToUInt64(_updateData.ArchiveFileData[(int)index].IsDirectory);
- }
- break;
- case ItemPropId.Size:
- #region Size
- value.VarType = VarEnum.VT_UI8;
- ulong size;
- if (_updateData.Mode != InternalCompressionMode.Modify)
- {
- if (_files == null)
- {
- if (_streamEntries == null)
- {
- size = _bytesCount > 0 ? (ulong)_bytesCount : 0;
- }
- else
- {
- size = (ulong)(_streamEntries[index].Stream == null ? 0 : _streamEntries[index].Stream.Length);
- }
- }
- else
- {
- size = (_files[index].Attributes & FileAttributes.Directory) == 0
- ? (ulong)_files[index].Length
- : 0;
- }
- }
- else
- {
- size = _updateData.ArchiveFileData[(int)index].Size;
- }
- value.UInt64Value = size;
- #endregion
- break;
- case ItemPropId.Attributes:
- value.VarType = VarEnum.VT_UI4;
- if (_updateData.Mode != InternalCompressionMode.Modify)
- {
- if (_files == null)
- {
- if (_streamEntries == null)
- {
- value.UInt32Value = (uint)FileAttributes.Normal;
- }
- else
- {
- value.UInt32Value = (uint)(_streamEntries[index].Stream == null ? FileAttributes.Directory : FileAttributes.Normal);
- }
- }
- else
- {
- value.UInt32Value = (uint)_files[index].Attributes;
- }
- }
- else
- {
- value.UInt32Value = _updateData.ArchiveFileData[(int)index].Attributes;
- }
- break;
- #region Times
- case ItemPropId.CreationTime:
- value.VarType = VarEnum.VT_FILETIME;
- if (_updateData.Mode != InternalCompressionMode.Modify)
- {
- if (_files != null)
- value.Int64Value = _files[index].CreationTime.ToFileTime();
- else if (_streamEntries != null && _streamEntries[index].CreateTime.HasValue)
- value.Int64Value = _streamEntries[index].CreateTime.Value.ToFileTime();
- else
- value.Int64Value = DateTime.Now.ToFileTime();
- }
- else
- {
- value.Int64Value = _updateData.ArchiveFileData[(int)index].CreationTime.ToFileTime();
- }
- break;
- case ItemPropId.LastAccessTime:
- value.VarType = VarEnum.VT_FILETIME;
- if (_updateData.Mode != InternalCompressionMode.Modify)
- {
- if (_files != null)
- value.Int64Value = _files[index].LastAccessTime.ToFileTime();
- else if (_streamEntries != null && _streamEntries[index].LastAccess.HasValue)
- value.Int64Value = _streamEntries[index].LastAccess.Value.ToFileTime();
- else
- value.Int64Value = DateTime.Now.ToFileTime();
- }
- else
- {
- value.Int64Value = _updateData.ArchiveFileData[(int)index].LastAccessTime.ToFileTime();
- }
- break;
- case ItemPropId.LastWriteTime:
- value.VarType = VarEnum.VT_FILETIME;
- if (_updateData.Mode != InternalCompressionMode.Modify)
- {
- if (_files == null)
- value.Int64Value = DateTime.Now.ToFileTime();
- else if (_streamEntries != null && _streamEntries[index].LastWrite.HasValue)
- value.Int64Value = _streamEntries[index].LastWrite.Value.ToFileTime();
- else
- value.Int64Value = _files[index].LastWriteTime.ToFileTime();
- }
- else
- {
- value.Int64Value = _updateData.ArchiveFileData[(int)index].LastWriteTime.ToFileTime();
- }
- break;
- #endregion
- case ItemPropId.Extension:
- #region Extension
- value.VarType = VarEnum.VT_BSTR;
- if (_updateData.Mode != InternalCompressionMode.Modify)
- {
- try
- {
- val = _files != null
- ? _files[index].Extension.Substring(1)
- : _entries == null
- ? ""
- : Path.GetExtension(_entries[index]);
- value.Value = Marshal.StringToBSTR(val);
- }
- catch (ArgumentException)
- {
- value.Value = Marshal.StringToBSTR("");
- }
- }
- else
- {
- val = Path.GetExtension(_updateData.ArchiveFileData[(int)index].FileName);
- value.Value = Marshal.StringToBSTR(val);
- }
- #endregion
- break;
- }
- }
- catch (Exception e)
- {
- AddException(e);
- }
- return 0;
- }
- /// <summary>
- /// Gets the stream for 7-zip library.
- /// </summary>
- /// <param name="index">File index</param>
- /// <param name="inStream">Input file stream</param>
- /// <returns>Zero if Ok</returns>
- public int GetStream(uint index, out ISequentialInStream inStream)
- {
- index -= _indexOffset;
- if (_files != null)
- {
- _fileStream = null;
- try
- {
- if (File.Exists(_files[index].FullName))
- {
- _fileStream = new InStreamWrapper(
- new FileStream(_files[index].FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite),
- true);
- }
- }
- catch (Exception e)
- {
- AddException(e);
- inStream = null;
- return -1;
- }
- inStream = _fileStream;
- if (!EventsForGetStream(index))
- {
- return -1;
- }
- }
- else
- {
- if (_streamEntries == null)
- {
- inStream = _fileStream;
- }
- else
- {
- _fileStream = new InStreamWrapper(_streamEntries[index].Stream, true);
- inStream = _fileStream;
- if (!EventsForGetStream(index))
- {
- return -1;
- }
- }
- }
- return 0;
- }
- public long EnumProperties(IntPtr enumerator)
- {
- //Not implemented HRESULT
- return 0x80004001L;
- }
- public void SetOperationResult(OperationResult operationResult)
- {
- if (operationResult != OperationResult.Ok && ReportErrors)
- {
- switch (operationResult)
- {
- case OperationResult.CrcError:
- AddException(new ExtractionFailedException("File is corrupted. Crc check has failed."));
- break;
- case OperationResult.DataError:
- AddException(new ExtractionFailedException("File is corrupted. Data error has occurred."));
- break;
- case OperationResult.UnsupportedMethod:
- AddException(new ExtractionFailedException("Unsupported method error has occurred."));
- break;
- case OperationResult.Unavailable:
- AddException(new ExtractionFailedException("File is unavailable."));
- break;
- case OperationResult.UnexpectedEnd:
- AddException(new ExtractionFailedException("Unexpected end of file."));
- break;
- case OperationResult.DataAfterEnd:
- AddException(new ExtractionFailedException("Data after end of archive."));
- break;
- case OperationResult.IsNotArc:
- AddException(new ExtractionFailedException("File is not archive."));
- break;
- case OperationResult.HeadersError:
- AddException(new ExtractionFailedException("Archive headers error."));
- break;
- case OperationResult.WrongPassword:
- AddException(new ExtractionFailedException("Wrong password."));
- break;
- default:
- AddException(new ExtractionFailedException($"Unexpected operation result: {operationResult}"));
- break;
- }
- }
- if (_fileStream != null)
- {
- _fileStream.BytesRead -= IntEventArgsHandler;
- //Specific Zip implementation - can not Dispose files for Zip.
- if (_compressor.ArchiveFormat != OutArchiveFormat.Zip)
- {
- try
- {
- _fileStream.Dispose();
- }
- catch (ObjectDisposedException) { }
- }
- else
- {
- _wrappersToDispose.Add(_fileStream);
- }
- _fileStream = null;
- }
- OnFileCompressionFinished(EventArgs.Empty);
- }
- #endregion
- #region ICryptoGetTextPassword2 Members
- public int CryptoGetTextPassword2(ref int passwordIsDefined, out string password)
- {
- passwordIsDefined = string.IsNullOrEmpty(Password) ? 0 : 1;
- password = Password;
- return 0;
- }
- #endregion
- #region IDisposable Members
- public void Dispose()
- {
- GC.RemoveMemoryPressure(_memoryPressure);
- if (_fileStream != null)
- {
- try
- {
- _fileStream.Dispose();
- }
- catch (ObjectDisposedException) { }
- }
- if (_wrappersToDispose == null)
- {
- return;
- }
- foreach (var wrapper in _wrappersToDispose)
- {
- try
- {
- wrapper.Dispose();
- }
- catch (ObjectDisposedException) { }
- }
- }
- #endregion
- private void IntEventArgsHandler(object sender, IntEventArgs e)
- {
- var lockObject = ((object)_files ?? _streamEntries) ?? _fileStream;
- lock (lockObject)
- {
- var pOld = (byte)(_bytesWrittenOld * 100 / _bytesCount);
- _bytesWritten += e.Value;
- byte pNow;
- if (_bytesCount < _bytesWritten) //Holy shit, this check for ZIP is golden
- {
- pNow = 100;
- }
- else
- {
- pNow = (byte)((_bytesWritten * 100) / _bytesCount);
- }
- if (pNow > pOld)
- {
- _bytesWrittenOld = _bytesWritten;
- OnCompressing(new ProgressEventArgs(pNow, (byte)(pNow - pOld)));
- }
- }
- }
- }
- #endif
|