private void UnpackRegularFile(Pack1Meta.FileEntry file, Action <double> onProgress, CancellationToken cancellationToken, string destinationDirPath = null)
        {
            string destPath = Path.Combine(destinationDirPath == null ? _destinationDirPath : destinationDirPath, file.Name + _suffix);

            DebugLogger.LogFormat("Unpacking regular file {0} to {1}", file, destPath);

            if (file.Size == null)
            {
                throw new NullReferenceException("File size cannot be null for regular file.");
            }

            if (file.Offset == null)
            {
                throw new NullReferenceException("File offset cannot be null for regular file.");
            }

            Files.CreateParents(destPath);

            var rijn = new RijndaelManaged
            {
                Mode    = CipherMode.CBC,
                Padding = PaddingMode.None,
                KeySize = 256
            };

            var aesAlg = new AesCryptoServiceProvider
            {
                IV  = _iv,
                Key = _key
            };

            ICryptoTransform decryptor = aesAlg.CreateDecryptor(
                aesAlg.Key,
                aesAlg.IV);

            DecompressorCreator decompressorCreator = ResolveDecompressor(_metaData);

            using (var fs = new FileStream(_packagePath, FileMode.Open))
            {
                fs.Seek(file.Offset.Value - _range.Start, SeekOrigin.Begin);

                using (var limitedStream = new BufferedStream(new LimitedStream(fs, file.Size.Value), 2 * 1024 * 1024))
                {
                    //using (var bufferedLimitedStream = new ThreadBufferedStream(limitedStream, 8 * 1024 * 1024))
                    {
                        using (var target = new FileStream(destPath, FileMode.Create))
                        {
                            ExtractFileFromStream(limitedStream, target, file.Size.Value, decryptor, decompressorCreator, onProgress, cancellationToken);
                        }
                    }

                    if (Platform.IsPosix())
                    {
                        Chmod.SetMode(file.Mode.Substring(3), destPath);
                    }
                }
            }

            DebugLogger.Log("File " + file.Name + " unpacked successfully!");
        }
        private void ExtractFileFromStream(
            Stream sourceStream,
            Stream targetStream,
            long fileSize,
            ICryptoTransform decryptor,
            DecompressorCreator createDecompressor,
            Action <double> onProgress,
            CancellationToken cancellationToken)
        {
            using (var cryptoStream = new CryptoStream(sourceStream, decryptor, CryptoStreamMode.Read))
            {
                using (var bufferedCryptoStream = new BufferedStream(new ThreadBufferedStream(cryptoStream, 2 * 1024 * 1024), 2 * 1024 * 1024))
                {
                    //using (var wrapperStream = new GZipReadWrapperStream(bufferedCryptoStream))
                    {
                        using (Stream decompressionStream = createDecompressor(bufferedCryptoStream))
                        {
                            using (var bufferedDecompressionStream = new ThreadBufferedStream(decompressionStream, 4 * 1024 * 1024))
                            {
                                try
                                {
                                    const int bufferSize = 2 * 1024 * 1024;
                                    var       buffer     = new byte[bufferSize];
                                    int       count;

                                    while ((count = bufferedDecompressionStream.Read(buffer, 0, buffer.Length)) > 0)
                                    {
                                        cancellationToken.ThrowIfCancellationRequested();
                                        targetStream.Write(buffer, 0, count);

                                        long bytesProcessed = sourceStream.Position;
                                        onProgress(bytesProcessed / (double)fileSize);
                                    }
                                }
                                catch (OperationCanceledException)
                                {
                                    throw;
                                }
                                catch (Exception e)
                                {
                                    DebugLogger.LogException(e);

                                    PatcherLogManager        logManager     = PatcherLogManager.Instance;
                                    PatcherLogSentryRegistry sentryRegistry = logManager.SentryRegistry;
                                    RavenClient ravenClient = sentryRegistry.RavenClient;

                                    var sentryEvent = new SentryEvent(e);
                                    PatcherLogSentryRegistry.AddDataToSentryEvent(sentryEvent,
                                                                                  logManager.Storage.Guid.ToString());
                                    ravenClient.Capture(sentryEvent);

                                    throw;
                                }
                            }
                        }
                    }
                }
            }
        }