mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-10-01 19:33:00 +00:00
Renamespace for proper pluralization
This commit is contained in:
266
src/SharpCompress/Readers/AbstractReader.cs
Normal file
266
src/SharpCompress/Readers/AbstractReader.cs
Normal file
@@ -0,0 +1,266 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Readers
|
||||
{
|
||||
/// <summary>
|
||||
/// A generic push reader that reads unseekable comrpessed streams.
|
||||
/// </summary>
|
||||
public abstract class AbstractReader<TEntry, TVolume> : IReader, IReaderExtractionListener
|
||||
where TEntry : Entry
|
||||
where TVolume : Volume
|
||||
{
|
||||
private bool completed;
|
||||
private IEnumerator<TEntry> entriesForCurrentReadStream;
|
||||
private bool wroteCurrentEntry;
|
||||
|
||||
public event EventHandler<ReaderExtractionEventArgs<IEntry>> EntryExtractionBegin;
|
||||
public event EventHandler<ReaderExtractionEventArgs<IEntry>> EntryExtractionEnd;
|
||||
|
||||
public event EventHandler<CompressedBytesReadEventArgs> CompressedBytesRead;
|
||||
public event EventHandler<FilePartExtractionBeginEventArgs> FilePartExtractionBegin;
|
||||
|
||||
internal AbstractReader(Options options, ArchiveType archiveType)
|
||||
{
|
||||
ArchiveType = archiveType;
|
||||
Options = options;
|
||||
}
|
||||
|
||||
internal Options Options { get; private set; }
|
||||
|
||||
public ArchiveType ArchiveType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Current volume that the current entry resides in
|
||||
/// </summary>
|
||||
public abstract TVolume Volume { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Current file entry
|
||||
/// </summary>
|
||||
public TEntry Entry { get { return entriesForCurrentReadStream.Current; } }
|
||||
|
||||
#region IDisposable Members
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (entriesForCurrentReadStream != null)
|
||||
{
|
||||
entriesForCurrentReadStream.Dispose();
|
||||
}
|
||||
Volume.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public bool Cancelled { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the remaining entries are not required.
|
||||
/// On dispose of an EntryStream, the stream will not skip to the end of the entry.
|
||||
/// An attempt to move to the next entry will throw an exception, as the compressed stream is not positioned at an entry boundary.
|
||||
/// </summary>
|
||||
public void Cancel()
|
||||
{
|
||||
if (!completed)
|
||||
{
|
||||
Cancelled = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveToNextEntry()
|
||||
{
|
||||
if (completed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (Cancelled)
|
||||
{
|
||||
throw new InvalidOperationException("Reader has been cancelled.");
|
||||
}
|
||||
if (entriesForCurrentReadStream == null)
|
||||
{
|
||||
return LoadStreamForReading(RequestInitialStream());
|
||||
}
|
||||
if (!wroteCurrentEntry)
|
||||
{
|
||||
SkipEntry();
|
||||
}
|
||||
wroteCurrentEntry = false;
|
||||
if (NextEntryForCurrentStream())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
completed = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal bool LoadStreamForReading(Stream stream)
|
||||
{
|
||||
if (entriesForCurrentReadStream != null)
|
||||
{
|
||||
entriesForCurrentReadStream.Dispose();
|
||||
}
|
||||
if ((stream == null) || (!stream.CanRead))
|
||||
{
|
||||
throw new MultipartStreamRequiredException("File is split into multiple archives: '"
|
||||
+ Entry.Key +
|
||||
"'. A new readable stream is required. Use Cancel if it was intended.");
|
||||
}
|
||||
entriesForCurrentReadStream = GetEntries(stream).GetEnumerator();
|
||||
if (entriesForCurrentReadStream.MoveNext())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal virtual Stream RequestInitialStream()
|
||||
{
|
||||
return Volume.Stream;
|
||||
}
|
||||
|
||||
internal virtual bool NextEntryForCurrentStream()
|
||||
{
|
||||
return entriesForCurrentReadStream.MoveNext();
|
||||
}
|
||||
|
||||
internal abstract IEnumerable<TEntry> GetEntries(Stream stream);
|
||||
|
||||
#region Entry Skip/Write
|
||||
|
||||
private void SkipEntry()
|
||||
{
|
||||
if (!Entry.IsDirectory)
|
||||
{
|
||||
Skip();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly byte[] skipBuffer = new byte[4096];
|
||||
|
||||
private void Skip()
|
||||
{
|
||||
if (!Entry.IsSolid)
|
||||
{
|
||||
var rawStream = Entry.Parts.First().GetRawStream();
|
||||
|
||||
if (rawStream != null)
|
||||
{
|
||||
var bytesToAdvance = Entry.CompressedSize;
|
||||
for (var i = 0; i < bytesToAdvance / skipBuffer.Length; i++)
|
||||
{
|
||||
rawStream.Read(skipBuffer, 0, skipBuffer.Length);
|
||||
}
|
||||
rawStream.Read(skipBuffer, 0, (int)(bytesToAdvance % skipBuffer.Length));
|
||||
return;
|
||||
}
|
||||
}
|
||||
using (var s = OpenEntryStream())
|
||||
{
|
||||
while (s.Read(skipBuffer, 0, skipBuffer.Length) > 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteEntryTo(Stream writableStream)
|
||||
{
|
||||
if (wroteCurrentEntry)
|
||||
{
|
||||
throw new ArgumentException("WriteEntryTo or OpenEntryStream can only be called once.");
|
||||
}
|
||||
if ((writableStream == null) || (!writableStream.CanWrite))
|
||||
{
|
||||
throw new ArgumentNullException(
|
||||
"A writable Stream was required. Use Cancel if that was intended.");
|
||||
}
|
||||
|
||||
var streamListener = this as IReaderExtractionListener;
|
||||
streamListener.FireEntryExtractionBegin(Entry);
|
||||
Write(writableStream);
|
||||
streamListener.FireEntryExtractionEnd(Entry);
|
||||
wroteCurrentEntry = true;
|
||||
}
|
||||
|
||||
internal void Write(Stream writeStream)
|
||||
{
|
||||
using (Stream s = OpenEntryStream())
|
||||
{
|
||||
s.TransferTo(writeStream);
|
||||
}
|
||||
}
|
||||
|
||||
public EntryStream OpenEntryStream()
|
||||
{
|
||||
if (wroteCurrentEntry)
|
||||
{
|
||||
throw new ArgumentException("WriteEntryTo or OpenEntryStream can only be called once.");
|
||||
}
|
||||
var stream = GetEntryStream();
|
||||
wroteCurrentEntry = true;
|
||||
return stream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retains a reference to the entry stream, so we can check whether it completed later.
|
||||
/// </summary>
|
||||
protected EntryStream CreateEntryStream(Stream decompressed)
|
||||
{
|
||||
return new EntryStream(this, decompressed);
|
||||
}
|
||||
|
||||
protected virtual EntryStream GetEntryStream()
|
||||
{
|
||||
return CreateEntryStream(Entry.Parts.First().GetCompressedStream());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
IEntry IReader.Entry { get { return Entry; } }
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void IReaderExtractionListener.FireEntryExtractionBegin(Entry entry)
|
||||
{
|
||||
if (EntryExtractionBegin != null)
|
||||
{
|
||||
EntryExtractionBegin(this, new ReaderExtractionEventArgs<IEntry>(entry));
|
||||
}
|
||||
}
|
||||
|
||||
void IReaderExtractionListener.FireEntryExtractionEnd(Entry entry)
|
||||
{
|
||||
if (EntryExtractionEnd != null)
|
||||
{
|
||||
EntryExtractionEnd(this, new ReaderExtractionEventArgs<IEntry>(entry));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/SharpCompress/Readers/GZip/GZipReader.cs
Normal file
40
src/SharpCompress/Readers/GZip/GZipReader.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.GZip;
|
||||
|
||||
namespace SharpCompress.Readers.GZip
|
||||
{
|
||||
public class GZipReader : AbstractReader<GZipEntry, GZipVolume>
|
||||
{
|
||||
internal GZipReader(Stream stream, Options options)
|
||||
: base(options, ArchiveType.GZip)
|
||||
{
|
||||
Volume = new GZipVolume(stream, options);
|
||||
}
|
||||
|
||||
public override GZipVolume Volume { get; }
|
||||
|
||||
#region Open
|
||||
|
||||
/// <summary>
|
||||
/// Opens a GZipReader for Non-seeking usage with a single volume
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static GZipReader Open(Stream stream,
|
||||
Options options = Options.KeepStreamsOpen)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new GZipReader(stream, options);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal override IEnumerable<GZipEntry> GetEntries(Stream stream)
|
||||
{
|
||||
return GZipEntry.GetEntries(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
40
src/SharpCompress/Readers/IReader.cs
Normal file
40
src/SharpCompress/Readers/IReader.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Readers
|
||||
{
|
||||
public interface IReader : IDisposable
|
||||
{
|
||||
event EventHandler<ReaderExtractionEventArgs<IEntry>> EntryExtractionBegin;
|
||||
event EventHandler<ReaderExtractionEventArgs<IEntry>> EntryExtractionEnd;
|
||||
|
||||
event EventHandler<CompressedBytesReadEventArgs> CompressedBytesRead;
|
||||
event EventHandler<FilePartExtractionBeginEventArgs> FilePartExtractionBegin;
|
||||
|
||||
ArchiveType ArchiveType { get; }
|
||||
|
||||
IEntry Entry { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Decompresses the current entry to the stream. This cannot be called twice for the current entry.
|
||||
/// </summary>
|
||||
/// <param name="writableStream"></param>
|
||||
void WriteEntryTo(Stream writableStream);
|
||||
|
||||
bool Cancelled { get; }
|
||||
void Cancel();
|
||||
|
||||
/// <summary>
|
||||
/// Moves to the next entry by reading more data from the underlying stream. This skips if data has not been read.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool MoveToNextEntry();
|
||||
|
||||
/// <summary>
|
||||
/// Opens the current entry as a stream that will decompress as it is read.
|
||||
/// Read the entire stream or use SkipEntry on EntryStream.
|
||||
/// </summary>
|
||||
EntryStream OpenEntryStream();
|
||||
}
|
||||
}
|
||||
98
src/SharpCompress/Readers/IReaderExtensions.cs
Normal file
98
src/SharpCompress/Readers/IReaderExtensions.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
#if !NO_FILE
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
#endif
|
||||
|
||||
namespace SharpCompress.Readers
|
||||
{
|
||||
public static class IReaderExtensions
|
||||
{
|
||||
#if !NO_FILE
|
||||
public static void WriteEntryTo(this IReader reader, string filePath)
|
||||
{
|
||||
using (Stream stream = File.Open(filePath, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
reader.WriteEntryTo(stream);
|
||||
}
|
||||
}
|
||||
|
||||
public static void WriteEntryTo(this IReader reader, FileInfo filePath)
|
||||
{
|
||||
using (Stream stream = filePath.Open(FileMode.Create))
|
||||
{
|
||||
reader.WriteEntryTo(stream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract all remaining unread entries to specific directory, retaining filename
|
||||
/// </summary>
|
||||
public static void WriteAllToDirectory(this IReader reader, string destinationDirectory,
|
||||
ExtractOptions options = ExtractOptions.Overwrite)
|
||||
{
|
||||
while (reader.MoveToNextEntry())
|
||||
{
|
||||
reader.WriteEntryToDirectory(destinationDirectory, options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract to specific directory, retaining filename
|
||||
/// </summary>
|
||||
public static void WriteEntryToDirectory(this IReader reader, string destinationDirectory,
|
||||
ExtractOptions options = ExtractOptions.Overwrite)
|
||||
{
|
||||
string destinationFileName = string.Empty;
|
||||
string file = Path.GetFileName(reader.Entry.Key);
|
||||
|
||||
|
||||
if (options.HasFlag(ExtractOptions.ExtractFullPath))
|
||||
{
|
||||
string folder = Path.GetDirectoryName(reader.Entry.Key);
|
||||
string destdir = Path.Combine(destinationDirectory, folder);
|
||||
if (!Directory.Exists(destdir))
|
||||
{
|
||||
Directory.CreateDirectory(destdir);
|
||||
}
|
||||
destinationFileName = Path.Combine(destdir, file);
|
||||
}
|
||||
else
|
||||
{
|
||||
destinationFileName = Path.Combine(destinationDirectory, file);
|
||||
}
|
||||
|
||||
if (!reader.Entry.IsDirectory)
|
||||
{
|
||||
reader.WriteEntryToFile(destinationFileName, options);
|
||||
}
|
||||
else if (options.HasFlag(ExtractOptions.ExtractFullPath) && !Directory.Exists(destinationFileName))
|
||||
{
|
||||
Directory.CreateDirectory(destinationFileName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract to specific file
|
||||
/// </summary>
|
||||
public static void WriteEntryToFile(this IReader reader, string destinationFileName,
|
||||
ExtractOptions options = ExtractOptions.Overwrite)
|
||||
{
|
||||
FileMode fm = FileMode.Create;
|
||||
|
||||
if (!options.HasFlag(ExtractOptions.Overwrite))
|
||||
{
|
||||
fm = FileMode.CreateNew;
|
||||
}
|
||||
using (FileStream fs = File.Open(destinationFileName, fm))
|
||||
{
|
||||
reader.WriteEntryTo(fs);
|
||||
//using (Stream s = reader.OpenEntryStream())
|
||||
//{
|
||||
// s.TransferTo(fs);
|
||||
//}
|
||||
}
|
||||
reader.Entry.PreserveExtractionOptions(destinationFileName, options);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
src/SharpCompress/Readers/IReaderExtractionListener.cs
Normal file
11
src/SharpCompress/Readers/IReaderExtractionListener.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Readers
|
||||
{
|
||||
internal interface IReaderExtractionListener : IExtractionListener
|
||||
{
|
||||
// void EnsureEntriesLoaded();
|
||||
void FireEntryExtractionBegin(Entry entry);
|
||||
void FireEntryExtractionEnd(Entry entry);
|
||||
}
|
||||
}
|
||||
124
src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs
Normal file
124
src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
|
||||
namespace SharpCompress.Readers.Rar
|
||||
{
|
||||
internal class MultiVolumeRarReader : RarReader
|
||||
{
|
||||
private readonly IEnumerator<Stream> streams;
|
||||
private Stream tempStream;
|
||||
|
||||
internal MultiVolumeRarReader(IEnumerable<Stream> streams, Options options)
|
||||
: base(options)
|
||||
{
|
||||
this.streams = streams.GetEnumerator();
|
||||
}
|
||||
|
||||
internal override void ValidateArchive(RarVolume archive)
|
||||
{
|
||||
}
|
||||
|
||||
internal override Stream RequestInitialStream()
|
||||
{
|
||||
if (streams.MoveNext())
|
||||
{
|
||||
return streams.Current;
|
||||
}
|
||||
throw new MultiVolumeExtractionException("No stream provided when requested by MultiVolumeRarReader");
|
||||
}
|
||||
|
||||
internal override bool NextEntryForCurrentStream()
|
||||
{
|
||||
if (!base.NextEntryForCurrentStream())
|
||||
{
|
||||
//if we're got another stream to try to process then do so
|
||||
if (streams.MoveNext() && LoadStreamForReading(streams.Current))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override IEnumerable<FilePart> CreateFilePartEnumerableForCurrentEntry()
|
||||
{
|
||||
var enumerator = new MultiVolumeStreamEnumerator(this, streams, tempStream);
|
||||
tempStream = null;
|
||||
return enumerator;
|
||||
}
|
||||
|
||||
private class MultiVolumeStreamEnumerator : IEnumerable<FilePart>, IEnumerator<FilePart>
|
||||
{
|
||||
private readonly MultiVolumeRarReader reader;
|
||||
private readonly IEnumerator<Stream> nextReadableStreams;
|
||||
private Stream tempStream;
|
||||
private bool isFirst = true;
|
||||
|
||||
internal MultiVolumeStreamEnumerator(MultiVolumeRarReader r, IEnumerator<Stream> nextReadableStreams,
|
||||
Stream tempStream)
|
||||
{
|
||||
reader = r;
|
||||
this.nextReadableStreams = nextReadableStreams;
|
||||
this.tempStream = tempStream;
|
||||
}
|
||||
|
||||
public IEnumerator<FilePart> GetEnumerator()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public FilePart Current { get; private set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
object IEnumerator.Current { get { return Current; } }
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
if (isFirst)
|
||||
{
|
||||
Current = reader.Entry.Parts.First();
|
||||
isFirst = false; //first stream already to go
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!reader.Entry.IsSplit)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (tempStream != null)
|
||||
{
|
||||
reader.LoadStreamForReading(tempStream);
|
||||
tempStream = null;
|
||||
}
|
||||
else if (!nextReadableStreams.MoveNext())
|
||||
{
|
||||
throw new MultiVolumeExtractionException("No stream provided when requested by MultiVolumeRarReader");
|
||||
}
|
||||
else
|
||||
{
|
||||
reader.LoadStreamForReading(nextReadableStreams.Current);
|
||||
}
|
||||
|
||||
Current = reader.Entry.Parts.First();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/SharpCompress/Readers/Rar/NonSeekableStreamFilePart.cs
Normal file
21
src/SharpCompress/Readers/Rar/NonSeekableStreamFilePart.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
|
||||
namespace SharpCompress.Readers.Rar
|
||||
{
|
||||
internal class NonSeekableStreamFilePart : RarFilePart
|
||||
{
|
||||
internal NonSeekableStreamFilePart(MarkHeader mh, FileHeader fh)
|
||||
: base(mh, fh)
|
||||
{
|
||||
}
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
return FileHeader.PackedStream;
|
||||
}
|
||||
|
||||
internal override string FilePartName { get { return "Unknown Stream - File Entry: " + FileHeader.FileName; } }
|
||||
}
|
||||
}
|
||||
83
src/SharpCompress/Readers/Rar/RarReader.cs
Normal file
83
src/SharpCompress/Readers/Rar/RarReader.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Compressors.Rar;
|
||||
|
||||
namespace SharpCompress.Readers.Rar
|
||||
{
|
||||
/// <summary>
|
||||
/// This class faciliates Reading a Rar Archive in a non-seekable forward-only manner
|
||||
/// </summary>
|
||||
public abstract class RarReader : AbstractReader<RarReaderEntry, RarVolume>
|
||||
{
|
||||
public string Password { get; set; }
|
||||
private RarVolume volume;
|
||||
private readonly Unpack pack = new Unpack();
|
||||
|
||||
internal RarReader(Options options)
|
||||
: base(options, ArchiveType.Rar)
|
||||
{
|
||||
}
|
||||
|
||||
internal abstract void ValidateArchive(RarVolume archive);
|
||||
|
||||
public override RarVolume Volume { get { return volume; } }
|
||||
|
||||
#region Open
|
||||
|
||||
/// <summary>
|
||||
/// Opens a RarReader for Non-seeking usage with a single volume
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static RarReader Open(Stream stream, Options options = Options.KeepStreamsOpen)
|
||||
{
|
||||
return Open(stream, null, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a RarReader for Non-seeking usage with multiple volumes
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static RarReader Open(IEnumerable<Stream> streams, Options options = Options.KeepStreamsOpen)
|
||||
{
|
||||
streams.CheckNotNull("streams");
|
||||
return new MultiVolumeRarReader(streams, options);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal override IEnumerable<RarReaderEntry> GetEntries(Stream stream)
|
||||
{
|
||||
volume = new RarReaderVolume(stream, Password, Options);
|
||||
foreach (RarFilePart fp in volume.ReadFileParts())
|
||||
{
|
||||
ValidateArchive(volume);
|
||||
yield return new RarReaderEntry(volume.IsSolidArchive, fp);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IEnumerable<FilePart> CreateFilePartEnumerableForCurrentEntry()
|
||||
{
|
||||
return Entry.Parts;
|
||||
}
|
||||
|
||||
protected override EntryStream GetEntryStream()
|
||||
{
|
||||
return CreateEntryStream(new RarStream(pack, Entry.FileHeader,
|
||||
new MultiVolumeReadOnlyStream(
|
||||
CreateFilePartEnumerableForCurrentEntry().Cast<RarFilePart>(), this)));
|
||||
}
|
||||
|
||||
public static RarReader Open(Stream stream, string password, Options options = Options.KeepStreamsOpen)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new SingleVolumeRarReader(stream, password, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/SharpCompress/Readers/Rar/RarReaderEntry.cs
Normal file
34
src/SharpCompress/Readers/Rar/RarReaderEntry.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System.Collections.Generic;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
|
||||
namespace SharpCompress.Readers.Rar
|
||||
{
|
||||
public class RarReaderEntry : RarEntry
|
||||
{
|
||||
internal RarReaderEntry(bool solid, RarFilePart part)
|
||||
{
|
||||
Part = part;
|
||||
IsSolid = solid;
|
||||
}
|
||||
|
||||
internal RarFilePart Part { get; }
|
||||
|
||||
internal override IEnumerable<FilePart> Parts { get { return Part.AsEnumerable<FilePart>(); } }
|
||||
|
||||
internal override FileHeader FileHeader { get { return Part.FileHeader; } }
|
||||
|
||||
public override CompressionType CompressionType { get { return CompressionType.Rar; } }
|
||||
|
||||
/// <summary>
|
||||
/// The compressed file size
|
||||
/// </summary>
|
||||
public override long CompressedSize { get { return Part.FileHeader.CompressedSize; } }
|
||||
|
||||
/// <summary>
|
||||
/// The uncompressed file size
|
||||
/// </summary>
|
||||
public override long Size { get { return Part.FileHeader.UncompressedSize; } }
|
||||
}
|
||||
}
|
||||
27
src/SharpCompress/Readers/Rar/RarReaderVolume.cs
Normal file
27
src/SharpCompress/Readers/Rar/RarReaderVolume.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Readers.Rar
|
||||
{
|
||||
public class RarReaderVolume : RarVolume
|
||||
{
|
||||
internal RarReaderVolume(Stream stream, string password, Options options)
|
||||
: base(StreamingMode.Streaming, stream, password, options)
|
||||
{
|
||||
}
|
||||
|
||||
internal override RarFilePart CreateFilePart(FileHeader fileHeader, MarkHeader markHeader)
|
||||
{
|
||||
return new NonSeekableStreamFilePart(markHeader, fileHeader);
|
||||
}
|
||||
|
||||
internal override IEnumerable<RarFilePart> ReadFileParts()
|
||||
{
|
||||
return GetVolumeFileParts();
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs
Normal file
32
src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
|
||||
namespace SharpCompress.Readers.Rar
|
||||
{
|
||||
internal class SingleVolumeRarReader : RarReader
|
||||
{
|
||||
private readonly Stream stream;
|
||||
|
||||
internal SingleVolumeRarReader(Stream stream, string password, Options options)
|
||||
: base(options)
|
||||
{
|
||||
Password = password;
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
internal override void ValidateArchive(RarVolume archive)
|
||||
{
|
||||
if (archive.IsMultiVolume)
|
||||
{
|
||||
throw new MultiVolumeExtractionException(
|
||||
"Streamed archive is a Multi-volume archive. Use different RarReader method to extract.");
|
||||
}
|
||||
}
|
||||
|
||||
internal override Stream RequestInitialStream()
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
80
src/SharpCompress/Readers/ReaderFactory.cs
Normal file
80
src/SharpCompress/Readers/ReaderFactory.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Archives.GZip;
|
||||
using SharpCompress.Archives.Rar;
|
||||
using SharpCompress.Archives.Tar;
|
||||
using SharpCompress.Archives.Zip;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers.GZip;
|
||||
using SharpCompress.Readers.Rar;
|
||||
using SharpCompress.Readers.Tar;
|
||||
using SharpCompress.Readers.Zip;
|
||||
|
||||
namespace SharpCompress.Readers
|
||||
{
|
||||
public static class ReaderFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Opens a Reader for Non-seeking usage
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static IReader Open(Stream stream, Options options = Options.KeepStreamsOpen)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
|
||||
RewindableStream rewindableStream = new RewindableStream(stream);
|
||||
rewindableStream.StartRecording();
|
||||
if (ZipArchive.IsZipFile(rewindableStream, null))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
return ZipReader.Open(rewindableStream, null, options);
|
||||
}
|
||||
rewindableStream.Rewind(false);
|
||||
if (GZipArchive.IsGZipFile(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
GZipStream testStream = new GZipStream(rewindableStream, CompressionMode.Decompress);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
return new TarReader(rewindableStream, CompressionType.GZip, options);
|
||||
}
|
||||
rewindableStream.Rewind(true);
|
||||
return GZipReader.Open(rewindableStream, options);
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (BZip2Stream.IsBZip2(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
BZip2Stream testStream = new BZip2Stream(rewindableStream, CompressionMode.Decompress, true);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
return new TarReader(rewindableStream, CompressionType.BZip2, options);
|
||||
}
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (RarArchive.IsRarFile(rewindableStream, options))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
return RarReader.Open(rewindableStream, options);
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (TarArchive.IsTarFile(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
return TarReader.Open(rewindableStream, options);
|
||||
}
|
||||
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Reader Formats: Zip, GZip, BZip2, Tar, Rar");
|
||||
}
|
||||
}
|
||||
}
|
||||
102
src/SharpCompress/Readers/Tar/TarReader.cs
Normal file
102
src/SharpCompress/Readers/Tar/TarReader.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Archives.GZip;
|
||||
using SharpCompress.Archives.Tar;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Tar;
|
||||
using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Readers.Tar
|
||||
{
|
||||
public class TarReader : AbstractReader<TarEntry, TarVolume>
|
||||
{
|
||||
private readonly CompressionType compressionType;
|
||||
|
||||
internal TarReader(Stream stream, CompressionType compressionType,
|
||||
Options options)
|
||||
: base(options, ArchiveType.Tar)
|
||||
{
|
||||
this.compressionType = compressionType;
|
||||
Volume = new TarVolume(stream, options);
|
||||
}
|
||||
|
||||
public override TarVolume Volume { get; }
|
||||
|
||||
internal override Stream RequestInitialStream()
|
||||
{
|
||||
var stream = base.RequestInitialStream();
|
||||
switch (compressionType)
|
||||
{
|
||||
case CompressionType.BZip2:
|
||||
{
|
||||
return new BZip2Stream(stream, CompressionMode.Decompress, false);
|
||||
}
|
||||
case CompressionType.GZip:
|
||||
{
|
||||
return new GZipStream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case CompressionType.None:
|
||||
{
|
||||
return stream;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new NotSupportedException("Invalid compression type: " + compressionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Open
|
||||
|
||||
/// <summary>
|
||||
/// Opens a TarReader for Non-seeking usage with a single volume
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <returns></returns>
|
||||
public static TarReader Open(Stream stream, Options options = Options.KeepStreamsOpen)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
|
||||
RewindableStream rewindableStream = new RewindableStream(stream);
|
||||
rewindableStream.StartRecording();
|
||||
if (GZipArchive.IsGZipFile(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
GZipStream testStream = new GZipStream(rewindableStream, CompressionMode.Decompress);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
return new TarReader(rewindableStream, CompressionType.GZip, options);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (BZip2Stream.IsBZip2(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
BZip2Stream testStream = new BZip2Stream(rewindableStream, CompressionMode.Decompress, false);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
return new TarReader(rewindableStream, CompressionType.BZip2, options);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
rewindableStream.Rewind(true);
|
||||
return new TarReader(rewindableStream, CompressionType.None, options);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal override IEnumerable<TarEntry> GetEntries(Stream stream)
|
||||
{
|
||||
return TarEntry.GetEntries(StreamingMode.Streaming, stream, compressionType);
|
||||
}
|
||||
}
|
||||
}
|
||||
63
src/SharpCompress/Readers/Zip/ZipReader.cs
Normal file
63
src/SharpCompress/Readers/Zip/ZipReader.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Zip;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
|
||||
namespace SharpCompress.Readers.Zip
|
||||
{
|
||||
public class ZipReader : AbstractReader<ZipEntry, ZipVolume>
|
||||
{
|
||||
private readonly StreamingZipHeaderFactory headerFactory;
|
||||
|
||||
internal ZipReader(Stream stream, Options options, string password)
|
||||
: base(options, ArchiveType.Zip)
|
||||
{
|
||||
Volume = new ZipVolume(stream, options);
|
||||
headerFactory = new StreamingZipHeaderFactory(password);
|
||||
}
|
||||
|
||||
public override ZipVolume Volume { get; }
|
||||
|
||||
#region Open
|
||||
|
||||
/// <summary>
|
||||
/// Opens a ZipReader for Non-seeking usage with a single volume
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <returns></returns>
|
||||
public static ZipReader Open(Stream stream, string password = null,
|
||||
Options options = Options.KeepStreamsOpen)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new ZipReader(stream, options, password);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal override IEnumerable<ZipEntry> GetEntries(Stream stream)
|
||||
{
|
||||
foreach (ZipHeader h in headerFactory.ReadStreamHeader(stream))
|
||||
{
|
||||
if (h != null)
|
||||
{
|
||||
switch (h.ZipHeaderType)
|
||||
{
|
||||
case ZipHeaderType.LocalEntry:
|
||||
{
|
||||
yield return new ZipEntry(new StreamingZipFilePart(h as LocalEntryHeader,
|
||||
stream));
|
||||
}
|
||||
break;
|
||||
case ZipHeaderType.DirectoryEnd:
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user