Sfoglia il codice sorgente

ApplyMOD: SevenZipSharp ⚠ NO DEBUGGED

HOME 1 mese fa
parent
commit
6f0e11f79a

+ 818 - 0
ImageConvertService/ExternalLibs/SevenZip/ArchiveUpdateCallbackWithFileTime.cs

@@ -0,0 +1,818 @@
+#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&lt;file stream, name of the archive entry&gt;</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&lt;file stream, name of the archive entry&gt;</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

+ 146 - 4
ImageConvertService/ExternalLibs/SevenZip/SevenZipCompressor.cs

@@ -701,6 +701,19 @@ namespace SevenZip
             auc.FastCompression = FastCompression;
         }
 
+        /// <summary>
+        /// Performs the common ArchiveUpdateCallback initialization.
+        /// </summary>
+        /// <param name="auc">The ArchiveUpdateCallback instance to initialize.</param>
+        private void CommonUpdateCallbackInitWithFileTime(ArchiveUpdateCallbackWithFileTime auc)
+        {
+            auc.FileCompressionStarted += FileCompressionStartedEventProxy;
+            auc.Compressing += CompressingEventProxy;
+            auc.FileCompressionFinished += FileCompressionFinishedEventProxy;
+            auc.DefaultItemName = DefaultItemName;
+            auc.FastCompression = FastCompression;
+        }
+
         private float GetDictionarySize()
         {
             var dictionarySize = 0.001f;
@@ -853,9 +866,29 @@ namespace SevenZip
             return auc;
         }
 
+        /// <summary>
+        /// Produces  a new instance of ArchiveUpdateCallback class.
+        /// </summary>
+        /// <param name="streamDict">Dictionary&lt;name of the archive entry, stream&gt;.</param>
+        /// <param name="password">The archive password</param>
+        /// <returns></returns>
+        private ArchiveUpdateCallbackWithFileTime GetArchiveUpdateCallbackWithFileTime(
+            IDictionary<string, CompressStreamEntry> streamDict, string password)
+        {
+            SetCompressionProperties();
+            var auc = (string.IsNullOrEmpty(password))
+                ? new ArchiveUpdateCallbackWithFileTime(streamDict, this, GetUpdateData(), DirectoryStructure)
+                    { DictionarySize = GetDictionarySize() }
+                : new ArchiveUpdateCallbackWithFileTime(streamDict, password, this, GetUpdateData(), DirectoryStructure)
+                    { DictionarySize = GetDictionarySize() };
+            CommonUpdateCallbackInitWithFileTime(auc);
+
+            return auc;
+        }
+
 #endregion
 
-#region Service "Get" functions
+        #region Service "Get" functions
 
         private void FreeCompressionCallback(ArchiveUpdateCallback callback)
         {
@@ -863,7 +896,12 @@ namespace SevenZip
             callback.Compressing -= CompressingEventProxy;
             callback.FileCompressionFinished -= FileCompressionFinishedEventProxy;
         }
-
+        private void FreeCompressionCallbackWithFileTime(ArchiveUpdateCallbackWithFileTime callback)
+        {
+            callback.FileCompressionStarted -= FileCompressionStartedEventProxy;
+            callback.Compressing -= CompressingEventProxy;
+            callback.FileCompressionFinished -= FileCompressionFinishedEventProxy;
+        }
         private string GetTempArchiveFileName(string archiveName)
         {
             return Path.Combine(TempFolderPath, Path.GetFileName(archiveName) + ".~");
@@ -1581,9 +1619,113 @@ namespace SevenZip
             ThrowUserException();
         }
 
-#endregion
+        /// <summary>
+        /// Packs the specified stream dictionary.
+        /// </summary>
+        /// <param name="streamDictionary">Dictionary&lt;name of the archive entry, stream&gt;.
+        /// If a stream is null, the corresponding string becomes a directory name.</param>
+        /// <param name="archiveStream">The archive output stream.
+        /// Use CompressStreamDictionary( ... string archiveName ... ) overloads for archiving to disk.</param>
+        /// <param name="password">The archive password.</param>
+        public void CompressStreamDictionaryWithFileTime(IDictionary<string, CompressStreamEntry> streamDictionary, Stream archiveStream, string password = "")
+        {
+            ClearExceptions();
+
+            if (streamDictionary.Count > 1 &&
+                (_archiveFormat == OutArchiveFormat.BZip2 || _archiveFormat == OutArchiveFormat.GZip ||
+                 _archiveFormat == OutArchiveFormat.XZ))
+            {
+                if (!ThrowException(null,
+                    new CompressionFailedException("Can not compress more than one file/stream in this format.")))
+                {
+                    return;
+                }
+            }
+
+            if (_volumeSize == 0 || !_compressingFilesOnDisk)
+            {
+                ValidateStream(archiveStream);
+            }
+
+            UpdateCompressorPassword(password);
+
+            if (streamDictionary.Where(
+                pair => pair.Value.Stream != null && (!pair.Value.Stream.CanSeek || !pair.Value.Stream.CanRead)).Any(
+                pair => !ThrowException(null,
+                    new ArgumentException(
+                        $"The specified stream dictionary contains an invalid stream corresponding to the archive entry \"{pair.Key}\".",
+                        nameof(streamDictionary)))))
+            {
+                return;
+            }
+
+            try
+            {
+                ISequentialOutStream sequentialArchiveStream;
+
+                using ((sequentialArchiveStream = GetOutStream(archiveStream)) as IDisposable)
+                {
+                    IInStream inArchiveStream;
+
+                    using ((inArchiveStream = GetInStream()) as IDisposable)
+                    {
+                        IOutArchive outArchive;
+
+                        if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
+                        {
+                            SevenZipLibraryManager.LoadLibrary(this, _archiveFormat);
+                            outArchive = SevenZipLibraryManager.OutArchive(_archiveFormat, this);
+                        }
+                        else
+                        {
+                            // Create IInArchive, read it and convert to IOutArchive
+                            SevenZipLibraryManager.LoadLibrary(
+                                this, Formats.InForOutFormats[_archiveFormat]);
+                            if ((outArchive = MakeOutArchive(inArchiveStream)) == null)
+                            {
+                                return;
+                            }
+                        }
+
+                        using (var auc = GetArchiveUpdateCallbackWithFileTime(streamDictionary, password))
+                        {
+                            try
+                            {
+                                CheckedExecute(outArchive.UpdateItems(sequentialArchiveStream,
+                                        (uint)streamDictionary.Count + _oldFilesCount, auc),
+                                    SevenZipCompressionFailedException.DEFAULT_MESSAGE, auc);
+                            }
+                            finally
+                            {
+                                FreeCompressionCallbackWithFileTime(auc);
+                            }
+                        }
+                    }
+                }
+            }
+            finally
+            {
+                if (CompressionMode == CompressionMode.Create || !_compressingFilesOnDisk)
+                {
+                    SevenZipLibraryManager.FreeLibrary(this, _archiveFormat);
+                }
+                else
+                {
+                    SevenZipLibraryManager.FreeLibrary(this, Formats.InForOutFormats[_archiveFormat]);
+                    File.Delete(_archiveName);
+                }
+
+                _compressingFilesOnDisk = false;
+                OnEvent(CompressionFinished, EventArgs.Empty, false);
+            }
+
+            ThrowUserException();
+        }
+
+
+        #endregion
 
-#region CompressStream overloads
+        #region CompressStream overloads
 
         /// <summary>
         /// Compresses the specified stream.