using System; using System.Collections.Generic; using System.IO; using System.Linq; using SharpCompress.Common; using SharpCompress.Reader; namespace SharpCompress.Archive { internal abstract class AbstractArchive : IArchive, IArchiveExtractionListener where TEntry : IArchiveEntry where TVolume : IVolume { private readonly LazyReadOnlyCollection lazyVolumes; private readonly LazyReadOnlyCollection lazyEntries; public event EventHandler> EntryExtractionBegin; public event EventHandler> EntryExtractionEnd; public event EventHandler CompressedBytesRead; public event EventHandler FilePartExtractionBegin; protected string Password { get; private set; } #if !PORTABLE && !NETFX_CORE internal AbstractArchive(ArchiveType type, FileInfo fileInfo, Options options, string password) { Type = type; Password = password; if (!fileInfo.Exists) { throw new ArgumentException("File does not exist: " + fileInfo.FullName); } options = (Options) FlagUtility.SetFlag(options, Options.KeepStreamsOpen, false); lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(fileInfo, options)); lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes)); } protected abstract IEnumerable LoadVolumes(FileInfo file, Options options); #endif internal AbstractArchive(ArchiveType type, IEnumerable streams, Options options, string password) { Type = type; Password = password; lazyVolumes = new LazyReadOnlyCollection(LoadVolumes(streams.Select(CheckStreams), options)); lazyEntries = new LazyReadOnlyCollection(LoadEntries(Volumes)); } internal AbstractArchive(ArchiveType type) { Type = type; lazyVolumes = new LazyReadOnlyCollection(Enumerable.Empty()); lazyEntries = new LazyReadOnlyCollection(Enumerable.Empty()); } public ArchiveType Type { get; private set; } void IArchiveExtractionListener.FireEntryExtractionBegin(IArchiveEntry entry) { if (EntryExtractionBegin != null) { EntryExtractionBegin(this, new ArchiveExtractionEventArgs(entry)); } } void IArchiveExtractionListener.FireEntryExtractionEnd(IArchiveEntry entry) { if (EntryExtractionEnd != null) { EntryExtractionEnd(this, new ArchiveExtractionEventArgs(entry)); } } private static Stream CheckStreams(Stream stream) { if (!stream.CanSeek || !stream.CanRead) { throw new ArgumentException("Archive streams must be Readable and Seekable"); } return stream; } /// /// Returns an ReadOnlyCollection of all the RarArchiveEntries across the one or many parts of the RarArchive. /// /// public virtual ICollection Entries { get { return lazyEntries; } } /// /// Returns an ReadOnlyCollection of all the RarArchiveVolumes across the one or many parts of the RarArchive. /// /// public ICollection Volumes { get { return lazyVolumes; } } /// /// The total size of the files compressed in the archive. /// public long TotalSize { get { return Entries.Aggregate(0L, (total, cf) => total + cf.CompressedSize); } } protected abstract IEnumerable LoadVolumes(IEnumerable streams, Options options); protected abstract IEnumerable LoadEntries(IEnumerable volumes); IEnumerable IArchive.Entries { get { return Entries.Cast(); } } IEnumerable IArchive.Volumes { get { return lazyVolumes.Cast(); } } private bool disposed; public virtual void Dispose() { if (!disposed) { lazyVolumes.ForEach(v => v.Dispose()); lazyEntries.GetLoaded().Cast().ForEach(x => x.Close()); disposed = true; } } void IArchiveExtractionListener.EnsureEntriesLoaded() { lazyEntries.EnsureFullyLoaded(); lazyVolumes.EnsureFullyLoaded(); } void IExtractionListener.FireCompressedBytesRead(long currentPartCompressedBytes, long compressedReadBytes) { if (CompressedBytesRead != null) { CompressedBytesRead(this, new CompressedBytesReadEventArgs() { CurrentFilePartCompressedBytesRead = currentPartCompressedBytes, CompressedBytesRead = compressedReadBytes }); } } void IExtractionListener.FireFilePartExtractionBegin(string name, long size, long compressedSize) { if (FilePartExtractionBegin != null) { FilePartExtractionBegin(this, new FilePartExtractionBeginEventArgs() { CompressedSize = compressedSize, Size = size, Name = name, }); } } /// /// Use this method to extract all entries in an archive in order. /// This is primarily for SOLID Rar Archives or 7Zip Archives as they need to be /// extracted sequentially for the best performance. /// /// This method will load all entry information from the archive. /// /// WARNING: this will reuse the underlying stream for the archive. Errors may /// occur if this is used at the same time as other extraction methods on this instance. /// /// public IReader ExtractAllEntries() { ((IArchiveExtractionListener)this).EnsureEntriesLoaded(); return CreateReaderForSolidExtraction(); } protected abstract IReader CreateReaderForSolidExtraction(); /// /// Archive is SOLID (this means the Archive saved bytes by reusing information which helps for archives containing many small files). /// public virtual bool IsSolid { get { return false; } } /// /// The archive can find all the parts of the archive needed to fully extract the archive. This forces the parsing of the entire archive. /// public bool IsComplete { get { ((IArchiveExtractionListener)this).EnsureEntriesLoaded(); return Entries.All(x => x.IsComplete); } } } }