mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-09-25 00:15:15 +00:00
Properly integrated zip multivolume and split support.
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archives
|
||||
@@ -23,28 +24,14 @@ namespace SharpCompress.Archives
|
||||
protected ReaderOptions ReaderOptions { get; }
|
||||
|
||||
private bool disposed;
|
||||
protected SourceStream SrcStream;
|
||||
|
||||
internal AbstractArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerOptions)
|
||||
internal AbstractArchive(ArchiveType type, SourceStream srcStream)
|
||||
{
|
||||
Type = type;
|
||||
if (!fileInfo.Exists)
|
||||
{
|
||||
throw new ArgumentException("File does not exist: " + fileInfo.FullName);
|
||||
}
|
||||
ReaderOptions = readerOptions;
|
||||
readerOptions.LeaveStreamOpen = false;
|
||||
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(fileInfo));
|
||||
lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes));
|
||||
}
|
||||
|
||||
|
||||
protected abstract IEnumerable<TVolume> LoadVolumes(FileInfo file);
|
||||
|
||||
internal AbstractArchive(ArchiveType type, IEnumerable<Stream> streams, ReaderOptions readerOptions)
|
||||
{
|
||||
Type = type;
|
||||
ReaderOptions = readerOptions;
|
||||
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(streams.Select(CheckStreams)));
|
||||
ReaderOptions = srcStream.ReaderOptions;
|
||||
SrcStream = srcStream;
|
||||
lazyVolumes = new LazyReadOnlyCollection<TVolume>(LoadVolumes(SrcStream));
|
||||
lazyEntries = new LazyReadOnlyCollection<TEntry>(LoadEntries(Volumes));
|
||||
}
|
||||
|
||||
@@ -98,7 +85,7 @@ namespace SharpCompress.Archives
|
||||
/// </summary>
|
||||
public virtual long TotalUncompressSize => Entries.Aggregate(0L, (total, cf) => total + cf.Size);
|
||||
|
||||
protected abstract IEnumerable<TVolume> LoadVolumes(IEnumerable<Stream> streams);
|
||||
protected abstract IEnumerable<TVolume> LoadVolumes(SourceStream srcStream);
|
||||
protected abstract IEnumerable<TEntry> LoadEntries(IEnumerable<TVolume> volumes);
|
||||
|
||||
IEnumerable<IArchiveEntry> IArchive.Entries => Entries.Cast<IArchiveEntry>();
|
||||
@@ -111,6 +98,8 @@ namespace SharpCompress.Archives
|
||||
{
|
||||
lazyVolumes.ForEach(v => v.Dispose());
|
||||
lazyEntries.GetLoaded().Cast<Entry>().ForEach(x => x.Close());
|
||||
if (SrcStream != null)
|
||||
SrcStream.Dispose();
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Writers;
|
||||
|
||||
@@ -40,22 +41,11 @@ namespace SharpCompress.Archives
|
||||
{
|
||||
}
|
||||
|
||||
internal AbstractWritableArchive(ArchiveType type, Stream stream, ReaderOptions readerFactoryOptions)
|
||||
: base(type, stream.AsEnumerable(), readerFactoryOptions)
|
||||
internal AbstractWritableArchive(ArchiveType type, SourceStream srcStream)
|
||||
: base(type, srcStream)
|
||||
{
|
||||
}
|
||||
|
||||
internal AbstractWritableArchive(ArchiveType type, FileInfo fileInfo, ReaderOptions readerFactoryOptions)
|
||||
: base(type, fileInfo, readerFactoryOptions)
|
||||
{
|
||||
}
|
||||
|
||||
internal AbstractWritableArchive(ArchiveType type, IEnumerable<Stream> streams, ReaderOptions readerFactoryOptions)
|
||||
: base(type, streams, readerFactoryOptions)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public override ICollection<TEntry> Entries
|
||||
{
|
||||
get
|
||||
|
||||
@@ -137,34 +137,54 @@ namespace SharpCompress.Archives
|
||||
switch (type.Value)
|
||||
{
|
||||
case ArchiveType.Zip:
|
||||
using (Stream prt2z = files[1].OpenRead())
|
||||
{
|
||||
try
|
||||
{
|
||||
prt2z.Position += 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
|
||||
if (ZipArchive.IsZipFile(prt2z)) //if part2 is a zip then it's multi not split zip, z01,z02... zip is moved to the end when opened
|
||||
return ZipArchive.Open(fileInfos.Select(a => a.OpenRead()), options);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return ZipArchive.Open(new SplitStream(files), options);
|
||||
return ZipArchive.Open(files, options);
|
||||
case ArchiveType.SevenZip:
|
||||
return SevenZipArchive.Open(new SplitStream(files), options);
|
||||
return SevenZipArchive.Open(files, options);
|
||||
case ArchiveType.GZip:
|
||||
return GZipArchive.Open(new SplitStream(files), options);
|
||||
return GZipArchive.Open(files, options);
|
||||
case ArchiveType.Rar:
|
||||
using (Stream prt2 = files[1].OpenRead())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (RarArchive.IsRarFile(prt2, options)) //if part2 is a rar then it's multi not split
|
||||
return RarArchive.Open(files.Select(f => File.OpenRead(f.FullName))); //multipart read
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
return RarArchive.Open(new SplitStream(files), options);
|
||||
return RarArchive.Open(files, options);
|
||||
case ArchiveType.Tar:
|
||||
return TarArchive.Open(new SplitStream(files), options);
|
||||
return TarArchive.Open(files, options);
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with IEnumerable FileInfo objects, multi and split support.
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="options"></param>
|
||||
public static IArchive Open(IEnumerable<Stream> streams, ReaderOptions? options = null)
|
||||
{
|
||||
streams.CheckNotNull(nameof(streams));
|
||||
if (streams.Count() == 0)
|
||||
throw new InvalidOperationException("No streams");
|
||||
if (streams.Count() == 1)
|
||||
return Open(streams.First(), options);
|
||||
|
||||
|
||||
options ??= new ReaderOptions();
|
||||
|
||||
ArchiveType? type;
|
||||
using (Stream stream = streams.First())
|
||||
IsArchive(stream, out type); //test and reset stream position
|
||||
|
||||
if (type != null)
|
||||
{
|
||||
switch (type.Value)
|
||||
{
|
||||
case ArchiveType.Zip:
|
||||
return ZipArchive.Open(streams, options);
|
||||
case ArchiveType.SevenZip:
|
||||
return SevenZipArchive.Open(streams, options);
|
||||
case ArchiveType.GZip:
|
||||
return GZipArchive.Open(streams, options);
|
||||
case ArchiveType.Rar:
|
||||
return RarArchive.Open(streams, options);
|
||||
case ArchiveType.Tar:
|
||||
return TarArchive.Open(streams, options);
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Archive Formats: Zip, GZip, Tar, Rar, 7Zip");
|
||||
@@ -235,5 +255,42 @@ namespace SharpCompress.Archives
|
||||
|
||||
return type != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From a passed in archive (zip, rar, 7z, 001), return all parts.
|
||||
/// </summary>
|
||||
/// <param name="part1"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<string> GetFileParts(string part1)
|
||||
{
|
||||
part1.CheckNotNullOrEmpty(nameof(part1));
|
||||
return GetFileParts(new FileInfo(part1)).Select(a => a.FullName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// From a passed in archive (zip, rar, 7z, 001), return all parts.
|
||||
/// </summary>
|
||||
/// <param name="part1"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<FileInfo> GetFileParts(FileInfo part1)
|
||||
{
|
||||
part1.CheckNotNull(nameof(part1));
|
||||
yield return part1;
|
||||
int i = 1;
|
||||
|
||||
FileInfo? part = RarArchiveVolumeFactory.GetFilePart(i++, part1);
|
||||
if (part != null)
|
||||
{
|
||||
yield return part;
|
||||
while ((part = RarArchiveVolumeFactory.GetFilePart(i++, part1)) != null) //tests split too
|
||||
yield return part;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = 1;
|
||||
while ((part = ZipArchiveVolumeFactory.GetFilePart(i++, part1)) != null) //tests split too
|
||||
yield return part;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
src/SharpCompress/Archives/ArchiveVolumeFactory.cs
Normal file
29
src/SharpCompress/Archives/ArchiveVolumeFactory.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Readers;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SharpCompress.Archives
|
||||
{
|
||||
internal abstract class ArchiveVolumeFactory
|
||||
{
|
||||
internal static FileInfo? GetFilePart(int index, FileInfo part1) //base the name on the first part
|
||||
{
|
||||
FileInfo? item = null;
|
||||
|
||||
//split 001, 002 ...
|
||||
Match m = Regex.Match(part1.Name, @"^(.*\.)([0-9]+)$", RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
item = new FileInfo(Path.Combine(part1.DirectoryName!, String.Concat(m.Groups[1].Value, (index + 1).ToString().PadLeft(m.Groups[2].Value.Length, '0'))));
|
||||
|
||||
if (item != null && item.Exists)
|
||||
return item;
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.GZip;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.GZip;
|
||||
using SharpCompress.Writers;
|
||||
@@ -32,7 +33,31 @@ namespace SharpCompress.Archives.GZip
|
||||
public static GZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
fileInfo.CheckNotNull(nameof(fileInfo));
|
||||
return new GZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
|
||||
return new GZipArchive(new SourceStream(fileInfo, i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with all file parts passed in
|
||||
/// </summary>
|
||||
/// <param name="fileInfos"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static GZipArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
fileInfos.CheckNotNull(nameof(fileInfos));
|
||||
FileInfo[] files = fileInfos.ToArray();
|
||||
return new GZipArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with all stream parts passed in
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static GZipArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
streams.CheckNotNull(nameof(streams));
|
||||
Stream[] strms = streams.ToArray();
|
||||
return new GZipArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -43,7 +68,7 @@ namespace SharpCompress.Archives.GZip
|
||||
public static GZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull(nameof(stream));
|
||||
return new GZipArchive(stream, readerOptions ?? new ReaderOptions());
|
||||
return new GZipArchive(new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
public static GZipArchive Create()
|
||||
@@ -52,20 +77,20 @@ namespace SharpCompress.Archives.GZip
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// Constructor with a SourceStream able to handle FileInfo and Streams.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="srcStream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal GZipArchive(FileInfo fileInfo, ReaderOptions options)
|
||||
: base(ArchiveType.GZip, fileInfo, options)
|
||||
internal GZipArchive(SourceStream srcStream)
|
||||
: base(ArchiveType.Tar, srcStream)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<GZipVolume> LoadVolumes(FileInfo file)
|
||||
protected override IEnumerable<GZipVolume> LoadVolumes(SourceStream srcStream)
|
||||
{
|
||||
return new GZipVolume(file, ReaderOptions).AsEnumerable();
|
||||
srcStream.LoadAllParts();
|
||||
return srcStream.Streams.Select(a => new GZipVolume(a, ReaderOptions));
|
||||
}
|
||||
|
||||
public static bool IsGZipFile(string filePath)
|
||||
{
|
||||
return IsGZipFile(new FileInfo(filePath));
|
||||
@@ -114,16 +139,6 @@ namespace SharpCompress.Archives.GZip
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes multiple seekable Streams for a multi-part archive
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal GZipArchive(Stream stream, ReaderOptions options)
|
||||
: base(ArchiveType.GZip, stream, options)
|
||||
{
|
||||
}
|
||||
|
||||
internal GZipArchive()
|
||||
: base(ArchiveType.GZip)
|
||||
{
|
||||
@@ -160,11 +175,6 @@ namespace SharpCompress.Archives.GZip
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<GZipVolume> LoadVolumes(IEnumerable<Stream> streams)
|
||||
{
|
||||
return new GZipVolume(streams.First(), ReaderOptions).AsEnumerable();
|
||||
}
|
||||
|
||||
protected override IEnumerable<GZipArchiveEntry> LoadEntries(IEnumerable<GZipVolume> volumes)
|
||||
{
|
||||
Stream stream = volumes.Single().Stream;
|
||||
|
||||
@@ -5,6 +5,7 @@ using SharpCompress.Common;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.Compressors.Rar;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
using SharpCompress.Readers.Rar;
|
||||
|
||||
@@ -16,28 +17,14 @@ namespace SharpCompress.Archives.Rar
|
||||
internal Lazy<IRarUnpack> UnpackV2017 { get; } = new Lazy<IRarUnpack>(() => new SharpCompress.Compressors.Rar.UnpackV2017.Unpack());
|
||||
internal Lazy<IRarUnpack> UnpackV1 { get; } = new Lazy<IRarUnpack>(() => new SharpCompress.Compressors.Rar.UnpackV1.Unpack());
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="options"></param>
|
||||
internal RarArchive(FileInfo fileInfo, ReaderOptions options)
|
||||
: base(ArchiveType.Rar, fileInfo, options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<RarVolume> LoadVolumes(FileInfo file)
|
||||
{
|
||||
return RarArchiveVolumeFactory.GetParts(file, ReaderOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes multiple seekable Streams for a multi-part archive
|
||||
/// Constructor with a SourceStream able to handle FileInfo and Streams.
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="srcStream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal RarArchive(IEnumerable<Stream> streams, ReaderOptions options)
|
||||
: base(ArchiveType.Rar, streams, options)
|
||||
internal RarArchive(SourceStream srcStream)
|
||||
: base(ArchiveType.Rar, srcStream)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -46,9 +33,20 @@ namespace SharpCompress.Archives.Rar
|
||||
return RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
|
||||
}
|
||||
|
||||
protected override IEnumerable<RarVolume> LoadVolumes(IEnumerable<Stream> streams)
|
||||
protected override IEnumerable<RarVolume> LoadVolumes(SourceStream srcStream)
|
||||
{
|
||||
return RarArchiveVolumeFactory.GetParts(streams, ReaderOptions);
|
||||
base.SrcStream.LoadAllParts(); //request all streams
|
||||
Stream[] streams = base.SrcStream.Streams.ToArray();
|
||||
if (streams.Length > 1 && IsRarFile(streams[1], ReaderOptions)) //test part 2 - true = multipart not split
|
||||
{
|
||||
base.SrcStream.IsVolumes = true;
|
||||
streams[1].Position = 0;
|
||||
base.SrcStream.Position = 0;
|
||||
|
||||
return srcStream.Streams.Select(a => new StreamRarArchiveVolume(a, ReaderOptions));
|
||||
}
|
||||
else //split mode or single file
|
||||
return new StreamRarArchiveVolume(base.SrcStream, ReaderOptions).AsEnumerable();
|
||||
}
|
||||
|
||||
protected override IReader CreateReaderForSolidExtraction()
|
||||
@@ -69,7 +67,8 @@ namespace SharpCompress.Archives.Rar
|
||||
public static RarArchive Open(string filePath, ReaderOptions? options = null)
|
||||
{
|
||||
filePath.CheckNotNullOrEmpty(nameof(filePath));
|
||||
return new RarArchive(new FileInfo(filePath), options ?? new ReaderOptions());
|
||||
FileInfo fileInfo = new FileInfo(filePath);
|
||||
return new RarArchive(new SourceStream(fileInfo, i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo), options ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -80,7 +79,7 @@ namespace SharpCompress.Archives.Rar
|
||||
public static RarArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
|
||||
{
|
||||
fileInfo.CheckNotNull(nameof(fileInfo));
|
||||
return new RarArchive(fileInfo, options ?? new ReaderOptions());
|
||||
return new RarArchive(new SourceStream(fileInfo, i => RarArchiveVolumeFactory.GetFilePart(i, fileInfo), options ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -91,20 +90,34 @@ namespace SharpCompress.Archives.Rar
|
||||
public static RarArchive Open(Stream stream, ReaderOptions? options = null)
|
||||
{
|
||||
stream.CheckNotNull(nameof(stream));
|
||||
return Open(stream.AsEnumerable(), options ?? new ReaderOptions());
|
||||
return new RarArchive(new SourceStream(stream, i => null, options ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes multiple seekable Streams for a multi-part archive
|
||||
/// Constructor with all file parts passed in
|
||||
/// </summary>
|
||||
/// <param name="fileInfos"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static RarArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
fileInfos.CheckNotNull(nameof(fileInfos));
|
||||
FileInfo[] files = fileInfos.ToArray();
|
||||
return new RarArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with all stream parts passed in
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="options"></param>
|
||||
public static RarArchive Open(IEnumerable<Stream> streams, ReaderOptions? options = null)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static RarArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
streams.CheckNotNull(nameof(streams));
|
||||
return new RarArchive(streams, options ?? new ReaderOptions());
|
||||
Stream[] strms = streams.ToArray();
|
||||
return new RarArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
|
||||
public static bool IsRarFile(string filePath)
|
||||
{
|
||||
return IsRarFile(new FileInfo(filePath));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Rar;
|
||||
@@ -6,135 +6,35 @@ using SharpCompress.Readers;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SharpCompress.Archives.Rar
|
||||
{
|
||||
internal static class RarArchiveVolumeFactory
|
||||
{
|
||||
internal static IEnumerable<RarVolume> GetParts(IEnumerable<Stream> streams, ReaderOptions options)
|
||||
internal static FileInfo? GetFilePart(int index, FileInfo part1) //base the name on the first part
|
||||
{
|
||||
foreach (Stream s in streams)
|
||||
{
|
||||
if (!s.CanRead || !s.CanSeek)
|
||||
{
|
||||
throw new ArgumentException("Stream is not readable and seekable");
|
||||
}
|
||||
StreamRarArchiveVolume part = new StreamRarArchiveVolume(s, options);
|
||||
yield return part;
|
||||
}
|
||||
}
|
||||
FileInfo? item = null;
|
||||
|
||||
internal static IEnumerable<RarVolume> GetParts(FileInfo fileInfo, ReaderOptions options)
|
||||
{
|
||||
FileInfoRarArchiveVolume part = new FileInfoRarArchiveVolume(fileInfo, options);
|
||||
yield return part;
|
||||
|
||||
ArchiveHeader ah = part.ArchiveHeader;
|
||||
if (!ah.IsVolume)
|
||||
{
|
||||
yield break; //if file isn't volume then there is no reason to look
|
||||
}
|
||||
fileInfo = GetNextFileInfo(ah, part.FileParts.FirstOrDefault() as FileInfoRarFilePart)!;
|
||||
//we use fileinfo because rar is dumb and looks at file names rather than archive info for another volume
|
||||
while (fileInfo != null && fileInfo.Exists)
|
||||
{
|
||||
part = new FileInfoRarArchiveVolume(fileInfo, options);
|
||||
|
||||
fileInfo = GetNextFileInfo(ah, part.FileParts.FirstOrDefault() as FileInfoRarFilePart)!;
|
||||
yield return part;
|
||||
}
|
||||
}
|
||||
|
||||
private static FileInfo? GetNextFileInfo(ArchiveHeader ah, FileInfoRarFilePart? currentFilePart)
|
||||
{
|
||||
if (currentFilePart is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
bool oldNumbering = ah.OldNumberingFormat
|
||||
|| currentFilePart.MarkHeader.OldNumberingFormat;
|
||||
if (oldNumbering)
|
||||
{
|
||||
return FindNextFileWithOldNumbering(currentFilePart.FileInfo);
|
||||
}
|
||||
//new style rar - ..part1 | /part01 | part001 ....
|
||||
Match m = Regex.Match(part1.Name, @"^(.*\.part)([0-9]+)(\.rar)$", RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
item = new FileInfo(Path.Combine(part1.DirectoryName!, String.Concat(m.Groups[1].Value, (index + 1).ToString().PadLeft(m.Groups[2].Value.Length, '0'), m.Groups[3].Value)));
|
||||
else
|
||||
{
|
||||
return FindNextFileWithNewNumbering(currentFilePart.FileInfo);
|
||||
//old style - ...rar, .r00, .r01 ...
|
||||
m = Regex.Match(part1.Name, @"^(.*\.r)(ar|[0-9]+)$", RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
item = new FileInfo(Path.Combine(part1.DirectoryName!, String.Concat(m.Groups[1].Value, index == 0 ? "ar" : (index - 1).ToString().PadLeft(m.Groups[2].Value.Length, '0'))));
|
||||
else //split .001, .002 ....
|
||||
return ArchiveVolumeFactory.GetFilePart(index, part1);
|
||||
}
|
||||
|
||||
if (item != null && item.Exists)
|
||||
return item;
|
||||
|
||||
return null; //no more items
|
||||
}
|
||||
|
||||
private static FileInfo FindNextFileWithOldNumbering(FileInfo currentFileInfo)
|
||||
{
|
||||
// .rar, .r00, .r01, ...
|
||||
string extension = currentFileInfo.Extension;
|
||||
|
||||
var buffer = new StringBuilder(currentFileInfo.FullName.Length);
|
||||
buffer.Append(currentFileInfo.FullName.Substring(0,
|
||||
currentFileInfo.FullName.Length - extension.Length));
|
||||
if (string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
buffer.Append(".r00");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (int.TryParse(extension.Substring(2, 2), out int num))
|
||||
{
|
||||
num++;
|
||||
buffer.Append(".r");
|
||||
if (num < 10)
|
||||
{
|
||||
buffer.Append('0');
|
||||
}
|
||||
buffer.Append(num);
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowInvalidFileName(currentFileInfo);
|
||||
}
|
||||
}
|
||||
return new FileInfo(buffer.ToString());
|
||||
}
|
||||
|
||||
private static FileInfo FindNextFileWithNewNumbering(FileInfo currentFileInfo)
|
||||
{
|
||||
// part1.rar, part2.rar, ...
|
||||
string extension = currentFileInfo.Extension;
|
||||
if (!string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new ArgumentException("Invalid extension, expected 'rar': " + currentFileInfo.FullName);
|
||||
}
|
||||
int startIndex = currentFileInfo.FullName.LastIndexOf(".part", StringComparison.OrdinalIgnoreCase);
|
||||
if (startIndex < 0)
|
||||
{
|
||||
ThrowInvalidFileName(currentFileInfo);
|
||||
}
|
||||
StringBuilder buffer = new StringBuilder(currentFileInfo.FullName.Length);
|
||||
buffer.Append(currentFileInfo.FullName, 0, startIndex);
|
||||
string numString = currentFileInfo.FullName.Substring(startIndex + 5,
|
||||
currentFileInfo.FullName.IndexOf('.', startIndex + 5) -
|
||||
startIndex - 5);
|
||||
buffer.Append(".part");
|
||||
if (int.TryParse(numString, out int num))
|
||||
{
|
||||
num++;
|
||||
for (int i = 0; i < numString.Length - num.ToString().Length; i++)
|
||||
{
|
||||
buffer.Append('0');
|
||||
}
|
||||
buffer.Append(num);
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowInvalidFileName(currentFileInfo);
|
||||
}
|
||||
buffer.Append(".rar");
|
||||
return new FileInfo(buffer.ToString());
|
||||
}
|
||||
|
||||
private static void ThrowInvalidFileName(FileInfo fileInfo)
|
||||
{
|
||||
throw new ArgumentException("Filename invalid or next archive could not be found:"
|
||||
+ fileInfo.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,33 @@ namespace SharpCompress.Archives.SevenZip
|
||||
public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
|
||||
{
|
||||
fileInfo.CheckNotNull("fileInfo");
|
||||
return new SevenZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
|
||||
return new SevenZipArchive(new SourceStream(fileInfo, i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with all file parts passed in
|
||||
/// </summary>
|
||||
/// <param name="fileInfos"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static SevenZipArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions readerOptions = null)
|
||||
{
|
||||
fileInfos.CheckNotNull(nameof(fileInfos));
|
||||
FileInfo[] files = fileInfos.ToArray();
|
||||
return new SevenZipArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with all stream parts passed in
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static SevenZipArchive Open(IEnumerable<Stream> streams, ReaderOptions readerOptions = null)
|
||||
{
|
||||
streams.CheckNotNull(nameof(streams));
|
||||
Stream[] strms = streams.ToArray();
|
||||
return new SevenZipArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a seekable Stream as a source
|
||||
/// </summary>
|
||||
@@ -44,17 +69,23 @@ namespace SharpCompress.Archives.SevenZip
|
||||
public static SevenZipArchive Open(Stream stream, ReaderOptions readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new SevenZipArchive(stream, readerOptions ?? new ReaderOptions());
|
||||
return new SevenZipArchive(new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
internal SevenZipArchive(FileInfo fileInfo, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.SevenZip, fileInfo, readerOptions)
|
||||
/// <summary>
|
||||
/// Constructor with a SourceStream able to handle FileInfo and Streams.
|
||||
/// </summary>
|
||||
/// <param name="srcStream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal SevenZipArchive(SourceStream srcStream)
|
||||
: base(ArchiveType.SevenZip, srcStream)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<SevenZipVolume> LoadVolumes(FileInfo file)
|
||||
protected override IEnumerable<SevenZipVolume> LoadVolumes(SourceStream srcStream)
|
||||
{
|
||||
return new SevenZipVolume(file.OpenRead(), ReaderOptions).AsEnumerable();
|
||||
base.SrcStream.LoadAllParts(); //request all streams
|
||||
return new SevenZipVolume(srcStream, ReaderOptions).AsEnumerable(); //simple single volume or split, multivolume not supported
|
||||
}
|
||||
|
||||
public static bool IsSevenZipFile(string filePath)
|
||||
@@ -74,29 +105,11 @@ namespace SharpCompress.Archives.SevenZip
|
||||
}
|
||||
}
|
||||
|
||||
internal SevenZipArchive(Stream stream, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.SevenZip, stream.AsEnumerable(), readerOptions)
|
||||
{
|
||||
}
|
||||
|
||||
internal SevenZipArchive()
|
||||
: base(ArchiveType.SevenZip)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<SevenZipVolume> LoadVolumes(IEnumerable<Stream> streams)
|
||||
{
|
||||
foreach (Stream s in streams)
|
||||
{
|
||||
if (!s.CanRead || !s.CanSeek)
|
||||
{
|
||||
throw new ArgumentException("Stream is not readable and seekable");
|
||||
}
|
||||
SevenZipVolume volume = new SevenZipVolume(s, ReaderOptions);
|
||||
yield return volume;
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<SevenZipArchiveEntry> LoadEntries(IEnumerable<SevenZipVolume> volumes)
|
||||
{
|
||||
var stream = volumes.Single().Stream;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -34,7 +34,31 @@ namespace SharpCompress.Archives.Tar
|
||||
public static TarArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
fileInfo.CheckNotNull(nameof(fileInfo));
|
||||
return new TarArchive(fileInfo, readerOptions ?? new ReaderOptions());
|
||||
return new TarArchive(new SourceStream(fileInfo, i => ArchiveVolumeFactory.GetFilePart(i, fileInfo), readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with all file parts passed in
|
||||
/// </summary>
|
||||
/// <param name="fileInfos"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static TarArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
fileInfos.CheckNotNull(nameof(fileInfos));
|
||||
FileInfo[] files = fileInfos.ToArray();
|
||||
return new TarArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with all stream parts passed in
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static TarArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
streams.CheckNotNull(nameof(streams));
|
||||
Stream[] strms = streams.ToArray();
|
||||
return new TarArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -45,7 +69,7 @@ namespace SharpCompress.Archives.Tar
|
||||
public static TarArchive Open(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull(nameof(stream));
|
||||
return new TarArchive(stream, readerOptions ?? new ReaderOptions());
|
||||
return new TarArchive(new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
public static bool IsTarFile(string filePath)
|
||||
@@ -80,28 +104,19 @@ namespace SharpCompress.Archives.Tar
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
internal TarArchive(FileInfo fileInfo, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.Tar, fileInfo, readerOptions)
|
||||
protected override IEnumerable<TarVolume> LoadVolumes(SourceStream srcStream)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<TarVolume> LoadVolumes(FileInfo file)
|
||||
{
|
||||
return new TarVolume(file.OpenRead(), ReaderOptions).AsEnumerable();
|
||||
base.SrcStream.LoadAllParts(); //request all streams
|
||||
return new TarVolume(srcStream, ReaderOptions).AsEnumerable(); //simple single volume or split, multivolume not supported
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes multiple seekable Streams for a multi-part archive
|
||||
/// Constructor with a SourceStream able to handle FileInfo and Streams.
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
internal TarArchive(Stream stream, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.Tar, stream, readerOptions)
|
||||
/// <param name="srcStream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal TarArchive(SourceStream srcStream)
|
||||
: base(ArchiveType.Tar, srcStream)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -110,11 +125,6 @@ namespace SharpCompress.Archives.Tar
|
||||
{
|
||||
}
|
||||
|
||||
protected override IEnumerable<TarVolume> LoadVolumes(IEnumerable<Stream> streams)
|
||||
{
|
||||
return new TarVolume(streams.First(), ReaderOptions).AsEnumerable();
|
||||
}
|
||||
|
||||
protected override IEnumerable<TarArchiveEntry> LoadEntries(IEnumerable<TarVolume> volumes)
|
||||
{
|
||||
Stream stream = volumes.Single().Stream;
|
||||
|
||||
@@ -26,6 +26,17 @@ namespace SharpCompress.Archives.Zip
|
||||
/// </summary>
|
||||
public CompressionLevel DeflateCompressionLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a SourceStream able to handle FileInfo and Streams.
|
||||
/// </summary>
|
||||
/// <param name="srcStream"></param>
|
||||
/// <param name="options"></param>
|
||||
internal ZipArchive(SourceStream srcStream)
|
||||
: base(ArchiveType.Zip, srcStream)
|
||||
{
|
||||
headerFactory = new SeekableZipHeaderFactory(srcStream.ReaderOptions.Password, srcStream.ReaderOptions.ArchiveEncoding);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor expects a filepath to an existing file.
|
||||
/// </summary>
|
||||
@@ -45,32 +56,33 @@ namespace SharpCompress.Archives.Zip
|
||||
public static ZipArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
fileInfo.CheckNotNull(nameof(fileInfo));
|
||||
return new ZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
|
||||
return new ZipArchive(new SourceStream(fileInfo, i => ZipArchiveVolumeFactory.GetFilePart(i, fileInfo), readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes multiple seekable Streams for a multi-part archive
|
||||
/// Constructor with all file parts passed in
|
||||
/// </summary>
|
||||
/// <param name="fileInfos"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static ZipArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
fileInfos.CheckNotNull(nameof(fileInfos));
|
||||
FileInfo[] files = fileInfos.ToArray();
|
||||
return new ZipArchive(new SourceStream(files[0], i => i < files.Length ? files[i] : null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with all stream parts passed in
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="options"></param>
|
||||
public static ZipArchive Open(IEnumerable<Stream> streams, ReaderOptions? options = null)
|
||||
/// <param name="readerOptions"></param>
|
||||
public static ZipArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
streams.CheckNotNull(nameof(streams));
|
||||
return new ZipArchive(streams, options ?? new ReaderOptions());
|
||||
Stream[] strms = streams.ToArray();
|
||||
return new ZipArchive(new SourceStream(strms[0], i => i < strms.Length ? strms[i] : null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes multiple seekable Streams for a multi-part archive
|
||||
/// </summary>
|
||||
/// <param name="streams"></param>
|
||||
/// <param name="options"></param>
|
||||
internal ZipArchive(IEnumerable<Stream> streams, ReaderOptions options)
|
||||
: base(ArchiveType.Zip, streams, options)
|
||||
{
|
||||
headerFactory = new SeekableZipHeaderFactory(options.Password, options.ArchiveEncoding);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Takes a seekable Stream as a source
|
||||
/// </summary>
|
||||
@@ -79,7 +91,7 @@ namespace SharpCompress.Archives.Zip
|
||||
public static ZipArchive Open(Stream stream, ReaderOptions? readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull(nameof(stream));
|
||||
return new ZipArchive(stream, readerOptions ?? new ReaderOptions());
|
||||
return new ZipArchive(new SourceStream(stream, i => null, readerOptions ?? new ReaderOptions()));
|
||||
}
|
||||
|
||||
public static bool IsZipFile(string filePath, string? password = null)
|
||||
@@ -150,20 +162,32 @@ namespace SharpCompress.Archives.Zip
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with a FileInfo object to an existing file.
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
internal ZipArchive(FileInfo fileInfo, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.Zip, fileInfo, readerOptions)
|
||||
protected override IEnumerable<ZipVolume> LoadVolumes(SourceStream srcStream)
|
||||
{
|
||||
headerFactory = new SeekableZipHeaderFactory(readerOptions.Password, readerOptions.ArchiveEncoding);
|
||||
}
|
||||
base.SrcStream.LoadAllParts(); //request all streams
|
||||
base.SrcStream.Position = 0;
|
||||
|
||||
protected override IEnumerable<ZipVolume> LoadVolumes(FileInfo file)
|
||||
{
|
||||
return new ZipVolume(file.OpenRead(), ReaderOptions).AsEnumerable();
|
||||
List<Stream> streams = base.SrcStream.Streams.ToList();
|
||||
if (streams.Count > 1) //test part 2 - true = multipart not split
|
||||
{
|
||||
streams[1].Position += 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
|
||||
bool isZip = IsZipFile(streams[1], ReaderOptions.Password);
|
||||
streams[1].Position -= 4;
|
||||
if (isZip)
|
||||
{
|
||||
base.SrcStream.IsVolumes = true;
|
||||
|
||||
var tmp = streams[0]; //arcs as zip, z01 ... swap the zip the end
|
||||
streams.RemoveAt(0);
|
||||
streams.Add(tmp);
|
||||
|
||||
//streams[0].Position = 4; //skip the POST_DATA_DESCRIPTOR to prevent an exception
|
||||
return streams.Select(a => new ZipVolume(a, ReaderOptions));
|
||||
}
|
||||
}
|
||||
|
||||
//split mode or single file
|
||||
return new ZipVolume(base.SrcStream, ReaderOptions).AsEnumerable();
|
||||
}
|
||||
|
||||
internal ZipArchive()
|
||||
@@ -171,28 +195,6 @@ namespace SharpCompress.Archives.Zip
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes multiple seekable Streams for a multi-part archive
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
internal ZipArchive(Stream stream, ReaderOptions readerOptions)
|
||||
: base(ArchiveType.Zip, stream, readerOptions)
|
||||
{
|
||||
headerFactory = new SeekableZipHeaderFactory(readerOptions.Password, readerOptions.ArchiveEncoding);
|
||||
}
|
||||
|
||||
protected override IEnumerable<ZipVolume> LoadVolumes(IEnumerable<Stream> streams)
|
||||
{
|
||||
var st = streams.ToList(); //swap the zip to the end
|
||||
var tmp = st[0];
|
||||
st.RemoveAt(0);
|
||||
st.Add(tmp);
|
||||
|
||||
foreach (var s in st)
|
||||
yield return new ZipVolume(s, base.ReaderOptions);
|
||||
}
|
||||
|
||||
protected override IEnumerable<ZipArchiveEntry> LoadEntries(IEnumerable<ZipVolume> volumes)
|
||||
{
|
||||
var vols = volumes.ToArray();
|
||||
@@ -207,7 +209,10 @@ namespace SharpCompress.Archives.Zip
|
||||
DirectoryEntryHeader deh = (DirectoryEntryHeader)h;
|
||||
Stream s;
|
||||
if (deh.RelativeOffsetOfEntryHeader + deh.CompressedSize > vols[deh.DiskNumberStart].Stream.Length)
|
||||
s = new SplitStream(vols.Skip(deh.DiskNumberStart).Select(a => a.Stream));
|
||||
{
|
||||
var v = vols.Skip(deh.DiskNumberStart).ToArray();
|
||||
s = new SourceStream(v[0].Stream, i => i < v.Length ? v[i].Stream : null, new ReaderOptions() { LeaveStreamOpen = true });
|
||||
}
|
||||
else
|
||||
s = vols[deh.DiskNumberStart].Stream;
|
||||
yield return new ZipArchiveEntry(this, new SeekableZipFilePart(headerFactory, deh, s));
|
||||
|
||||
34
src/SharpCompress/Archives/Zip/ZipArchiveVolumeFactory.cs
Normal file
34
src/SharpCompress/Archives/Zip/ZipArchiveVolumeFactory.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.Readers;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace SharpCompress.Archives.Zip
|
||||
{
|
||||
internal static class ZipArchiveVolumeFactory
|
||||
{
|
||||
internal static FileInfo? GetFilePart(int index, FileInfo part1) //base the name on the first part
|
||||
{
|
||||
FileInfo? item = null;
|
||||
|
||||
//load files with zip/zipx first. Swapped to end once loaded in ZipArchive
|
||||
//new style .zip, z01.. | .zipx, zx01 - if the numbers go beyond 99 then they use 100 ...1000 etc
|
||||
Match m = Regex.Match(part1.Name, @"^(.*\.)(zipx?|zx?[0-9]+)$", RegexOptions.IgnoreCase);
|
||||
if (m.Success)
|
||||
item = new FileInfo(Path.Combine(part1.DirectoryName!, String.Concat(m.Groups[1].Value, Regex.Replace(m.Groups[2].Value, @"[^xz]", ""), index.ToString().PadLeft(2, '0'))));
|
||||
else //split - 001, 002 ...
|
||||
return ArchiveVolumeFactory.GetFilePart(index, part1);
|
||||
|
||||
if (item != null && item.Exists)
|
||||
return item;
|
||||
|
||||
return null; //no more items
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
215
src/SharpCompress/IO/SourceStream.cs
Normal file
215
src/SharpCompress/IO/SourceStream.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.IO
|
||||
{
|
||||
public class SourceStream : Stream
|
||||
{
|
||||
private long _prevSize;
|
||||
private List<FileInfo> _files;
|
||||
private List<Stream> _streams;
|
||||
private Func<int, FileInfo?> _getFilePart;
|
||||
private Func<int, Stream?> _getStreamPart;
|
||||
private int _stream;
|
||||
|
||||
public SourceStream(FileInfo file, Func<int, FileInfo?> getPart, ReaderOptions options) : this(null, null, file, getPart, options)
|
||||
{
|
||||
}
|
||||
|
||||
public SourceStream(Stream stream, Func<int, Stream?> getPart, ReaderOptions options) : this(stream, getPart, null, null, options)
|
||||
{
|
||||
}
|
||||
|
||||
private SourceStream(Stream? stream, Func<int, Stream?>? getStreamPart, FileInfo? file, Func<int, FileInfo?>? getFilePart, ReaderOptions options)
|
||||
{
|
||||
this.ReaderOptions = options;
|
||||
_files = new List<FileInfo>();
|
||||
_streams = new List<Stream>();
|
||||
IsFileMode = file != null;
|
||||
IsVolumes = false;
|
||||
|
||||
if (!IsFileMode)
|
||||
{
|
||||
_streams.Add(stream!);
|
||||
_getStreamPart = getStreamPart!;
|
||||
_getFilePart = new Func<int, FileInfo>(a => null!);
|
||||
if (stream! is FileStream)
|
||||
_files.Add(new FileInfo(((FileStream)stream!).Name));
|
||||
}
|
||||
else
|
||||
{
|
||||
_files.Add(file!);
|
||||
_streams.Add(_files[0].OpenRead());
|
||||
_getFilePart = getFilePart!;
|
||||
_getStreamPart = new Func<int, Stream>(a => null!);
|
||||
}
|
||||
_stream = 0;
|
||||
_prevSize = 0;
|
||||
}
|
||||
|
||||
public void LoadAllParts()
|
||||
{
|
||||
for (int i = 1; SetStream(i); i++)
|
||||
{
|
||||
}
|
||||
SetStream(0);
|
||||
}
|
||||
|
||||
public bool IsVolumes { get; set; }
|
||||
|
||||
public ReaderOptions ReaderOptions { get; }
|
||||
public bool IsFileMode { get; }
|
||||
|
||||
public IEnumerable<FileInfo> Files => _files;
|
||||
public IEnumerable<Stream> Streams => _streams;
|
||||
|
||||
private Stream Current => _streams[_stream];
|
||||
public bool LoadStream(int index) //ensure all parts to id are loaded
|
||||
{
|
||||
while (_streams.Count <= index)
|
||||
{
|
||||
if (IsFileMode)
|
||||
{
|
||||
FileInfo? f = _getFilePart(_streams.Count);
|
||||
if (f == null)
|
||||
{
|
||||
_stream = _streams.Count - 1;
|
||||
return false;
|
||||
}
|
||||
//throw new Exception($"File part {idx} not available.");
|
||||
_files.Add(f);
|
||||
_streams.Add(_files.Last().OpenRead());
|
||||
}
|
||||
else
|
||||
{
|
||||
Stream? s = _getStreamPart(_streams.Count);
|
||||
if (s == null)
|
||||
{
|
||||
_stream = _streams.Count - 1;
|
||||
return false;
|
||||
}
|
||||
//throw new Exception($"Stream part {idx} not available.");
|
||||
_streams.Add(s);
|
||||
if (s is FileStream)
|
||||
_files.Add(new FileInfo(((FileStream)s).Name));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public bool SetStream(int idx) //allow caller to switch part in multipart
|
||||
{
|
||||
if (LoadStream(idx))
|
||||
_stream = idx;
|
||||
return _stream == idx;
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => true;
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override long Length => (!IsVolumes ? _streams.Sum(a => a.Length) : Current.Length);
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => _prevSize + Current.Position; //_prevSize is 0 for multivolume
|
||||
set => Seek(value, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
Current.Flush();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (count <= 0)
|
||||
return 0;
|
||||
|
||||
int total = count;
|
||||
int r = -1;
|
||||
|
||||
while (count != 0 && r != 0)
|
||||
{
|
||||
r = Current.Read(buffer, offset, (int)Math.Min(count, Current.Length - Current.Position));
|
||||
count -= r;
|
||||
offset += r;
|
||||
|
||||
if (!IsVolumes && count != 0 && Current.Position == Current.Length)
|
||||
{
|
||||
_prevSize += Current.Length;
|
||||
SetStream(_stream + 1); //will load next file
|
||||
Current.Seek(0, SeekOrigin.Begin);
|
||||
}
|
||||
}
|
||||
|
||||
return total - count;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
long pos = this.Position;
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin: pos = offset; break;
|
||||
case SeekOrigin.Current: pos += offset; break;
|
||||
case SeekOrigin.End: pos = Length + offset; break;
|
||||
}
|
||||
|
||||
_prevSize = 0;
|
||||
if (!IsVolumes)
|
||||
{
|
||||
SetStream(0);
|
||||
while (_prevSize + Current.Length < pos)
|
||||
{
|
||||
_prevSize += Current.Length;
|
||||
SetStream(_stream + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (pos != _prevSize + Current.Position)
|
||||
Current.Seek(pos - _prevSize, SeekOrigin.Begin);
|
||||
return pos;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
if (this.IsFileMode || !this.ReaderOptions.LeaveStreamOpen) //close if file mode or options specify it
|
||||
{
|
||||
foreach (Stream stream in _streams)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (stream != null)
|
||||
stream.Dispose();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
_streams.Clear();
|
||||
_files.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
this.Close();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharpCompress.IO
|
||||
{
|
||||
public class SplitStream : Stream
|
||||
{
|
||||
private int _idx;
|
||||
private long _size;
|
||||
private bool _isSplit;
|
||||
private long _prevSize;
|
||||
private long _pos;
|
||||
private long _streamCount;
|
||||
private FileInfo[]? _files;
|
||||
private Stream[]? _streams;
|
||||
private Stream _stream;
|
||||
|
||||
|
||||
public SplitStream(IEnumerable<FileInfo> files) : this(null, files)
|
||||
{
|
||||
}
|
||||
|
||||
public SplitStream(IEnumerable<Stream> streams) : this(streams, null)
|
||||
{
|
||||
}
|
||||
|
||||
private SplitStream(IEnumerable<Stream>? streams, IEnumerable<FileInfo>? files)
|
||||
{
|
||||
if (streams != null)
|
||||
{
|
||||
_streams = streams.ToArray();
|
||||
_isSplit = _streams.Length > 1;
|
||||
_size = _streams.Sum(a => a.Length);
|
||||
_streamCount = _streams.Length;
|
||||
}
|
||||
else if (files != null)
|
||||
{
|
||||
_files = files.ToArray();
|
||||
_isSplit = _files.Length > 1;
|
||||
_size = _files.Sum(a => a.Length);
|
||||
_streamCount = _files.Length;
|
||||
}
|
||||
_idx = 0;
|
||||
_prevSize = 0;
|
||||
_stream = OpenStream(0);
|
||||
}
|
||||
|
||||
private Stream OpenStream(int idx)
|
||||
{
|
||||
if (_streams != null)
|
||||
_stream = _streams[idx];
|
||||
else if (_files != null)
|
||||
{
|
||||
if (_stream != null)
|
||||
_stream.Dispose();
|
||||
|
||||
_stream = File.OpenRead(_files[idx].FullName);
|
||||
}
|
||||
|
||||
_idx = idx;
|
||||
_pos = 0;
|
||||
return _stream;
|
||||
}
|
||||
|
||||
private long StreamLen(int idx)
|
||||
{
|
||||
if (_streams != null)
|
||||
return _streams[idx].Length;
|
||||
else if (_files != null)
|
||||
return _files[idx].Length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
|
||||
public override bool CanSeek => true;
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override long Length => _size;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => _prevSize + _pos;
|
||||
set => Seek(value, SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
_stream.Flush();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
count = (int)Math.Min(count, _size - this.Position);
|
||||
|
||||
if (count <= 0)
|
||||
return 0;
|
||||
|
||||
|
||||
int total = count;
|
||||
int r = -1;
|
||||
|
||||
while (count != 0 && r != 0)
|
||||
{
|
||||
r = _stream.Read(buffer, offset, count);
|
||||
_pos += (long)r;
|
||||
count -= r;
|
||||
offset += r;
|
||||
|
||||
if (_isSplit && _pos == StreamLen(_idx))
|
||||
Seek(0, SeekOrigin.Current); //will load next file
|
||||
}
|
||||
|
||||
return total - count;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
long pos = this.Position;
|
||||
switch (origin)
|
||||
{
|
||||
case SeekOrigin.Begin: pos = offset; break;
|
||||
case SeekOrigin.Current: pos += offset; break;
|
||||
case SeekOrigin.End: pos = Length + offset; break;
|
||||
}
|
||||
|
||||
if (_isSplit)
|
||||
{
|
||||
_prevSize = 0;
|
||||
for (int i = 0; i < _streamCount; i++)
|
||||
{
|
||||
if (_prevSize + StreamLen(i) > pos)
|
||||
{
|
||||
if (_idx != i)
|
||||
_stream = OpenStream(i);
|
||||
break;
|
||||
}
|
||||
_prevSize += StreamLen(i);
|
||||
}
|
||||
}
|
||||
|
||||
_pos = pos - _prevSize;
|
||||
|
||||
if (_pos != _stream.Position && this.Position != this.Length)
|
||||
_stream.Seek(_pos, SeekOrigin.Begin);
|
||||
return pos;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_stream != null)
|
||||
_stream.Close();
|
||||
}
|
||||
catch { }
|
||||
base.Close();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_stream != null)
|
||||
_stream.Dispose();
|
||||
}
|
||||
catch { }
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,8 @@
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<IsTrimmable>true</IsTrimmable>
|
||||
<RunAnalyzersDuringLiveAnalysis>False</RunAnalyzersDuringLiveAnalysis>
|
||||
<RunAnalyzersDuringBuild>False</RunAnalyzersDuringBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -12,6 +12,15 @@ namespace SharpCompress.Test
|
||||
{
|
||||
public class ArchiveTests : ReaderTests
|
||||
{
|
||||
protected void ArchiveGetParts(IEnumerable<string> testArchives)
|
||||
{
|
||||
string[] arcs = testArchives.Select(a => Path.Combine(TEST_ARCHIVES_PATH, a)).ToArray();
|
||||
string[] found = ArchiveFactory.GetFileParts(arcs[0]).ToArray();
|
||||
Assert.Equal(arcs.Length, found.Length);
|
||||
for (int i = 0; i < arcs.Length; i++)
|
||||
Assert.Equal(arcs[i], found[i]);
|
||||
}
|
||||
|
||||
protected void ArchiveStreamReadExtractAll(string testArchive, CompressionType compression)
|
||||
{
|
||||
testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive);
|
||||
@@ -95,38 +104,6 @@ namespace SharpCompress.Test
|
||||
}
|
||||
}
|
||||
|
||||
protected void ArchiveStreamSplitRead(ReaderOptions readerOptions = null, params string[] testArchives)
|
||||
{
|
||||
ArchiveStreamSplitRead(readerOptions, testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)));
|
||||
}
|
||||
|
||||
protected void ArchiveStreamSplitRead(ReaderOptions readerOptions, IEnumerable<string> testArchives)
|
||||
{
|
||||
using (SplitStream stream = new SplitStream(testArchives.Select(f => new FileInfo(f))))
|
||||
{
|
||||
using (var archive = ArchiveFactory.Open(stream, readerOptions))
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
entry.WriteToDirectory(SCRATCH_FILES_PATH,
|
||||
new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (IndexOutOfRangeException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
VerifyFiles();
|
||||
}
|
||||
}
|
||||
|
||||
protected void ArchiveStreamMultiRead(ReaderOptions readerOptions = null, params string[] testArchives)
|
||||
{
|
||||
ArchiveStreamMultiRead(readerOptions, testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x)));
|
||||
|
||||
@@ -435,6 +435,45 @@ namespace SharpCompress.Test.Rar
|
||||
ArchiveFileRead("Rar4.rar");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rar_GetPartsSplit()
|
||||
{
|
||||
//uses first part to search for all parts and compares against this array
|
||||
ArchiveGetParts(new string[] {
|
||||
"Rar4.split.001",
|
||||
"Rar4.split.002",
|
||||
"Rar4.split.003",
|
||||
"Rar4.split.004",
|
||||
"Rar4.split.005",
|
||||
"Rar4.split.006"});
|
||||
}
|
||||
[Fact]
|
||||
public void Rar_GetPartsOld()
|
||||
{
|
||||
//uses first part to search for all parts and compares against this array
|
||||
ArchiveGetParts(new string[] {
|
||||
"Rar2.multi.rar",
|
||||
"Rar2.multi.r00",
|
||||
"Rar2.multi.r01",
|
||||
"Rar2.multi.r02",
|
||||
"Rar2.multi.r03",
|
||||
"Rar2.multi.r04",
|
||||
"Rar2.multi.r05"});
|
||||
}
|
||||
[Fact]
|
||||
public void Rar_GetPartsNew()
|
||||
{
|
||||
//uses first part to search for all parts and compares against this array
|
||||
ArchiveGetParts(new string[] {
|
||||
"Rar4.multi.part01.rar",
|
||||
"Rar4.multi.part02.rar",
|
||||
"Rar4.multi.part03.rar",
|
||||
"Rar4.multi.part04.rar",
|
||||
"Rar4.multi.part05.rar",
|
||||
"Rar4.multi.part06.rar",
|
||||
"Rar4.multi.part07.rar"});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rar4_Multi_ArchiveStreamRead()
|
||||
{
|
||||
@@ -452,7 +491,7 @@ namespace SharpCompress.Test.Rar
|
||||
[Fact]
|
||||
public void Rar4_Split_ArchiveStreamRead()
|
||||
{
|
||||
ArchiveStreamSplitRead(null, new string[] {
|
||||
ArchiveStreamMultiRead(null, new string[] {
|
||||
"Rar4.split.001",
|
||||
"Rar4.split.002",
|
||||
"Rar4.split.003",
|
||||
@@ -460,6 +499,42 @@ namespace SharpCompress.Test.Rar
|
||||
"Rar4.split.005",
|
||||
"Rar4.split.006"});
|
||||
}
|
||||
//will detect and load other files
|
||||
[Fact]
|
||||
public void Rar4_Multi_ArchiveFirstFileRead()
|
||||
{
|
||||
ArchiveFileRead("Rar4.multi.part01.rar");
|
||||
//"Rar4.multi.part02.rar",
|
||||
//"Rar4.multi.part03.rar",
|
||||
//"Rar4.multi.part04.rar",
|
||||
//"Rar4.multi.part05.rar",
|
||||
//"Rar4.multi.part06.rar",
|
||||
//"Rar4.multi.part07.rar"
|
||||
}
|
||||
//will detect and load other files
|
||||
[Fact]
|
||||
public void Rar4_Split_ArchiveFirstFileRead()
|
||||
{
|
||||
ArchiveFileRead("Rar4.split.001");
|
||||
//"Rar4.split.002",
|
||||
//"Rar4.split.003",
|
||||
//"Rar4.split.004",
|
||||
//"Rar4.split.005",
|
||||
//"Rar4.split.006"
|
||||
}
|
||||
//will detect and load other files
|
||||
[Fact]
|
||||
public void Rar4_Split_ArchiveStreamFirstFileRead()
|
||||
{
|
||||
ArchiveStreamMultiRead(null, new string[] {
|
||||
"Rar4.split.001",
|
||||
//"Rar4.split.002",
|
||||
//"Rar4.split.003",
|
||||
//"Rar4.split.004",
|
||||
//"Rar4.split.005",
|
||||
//"Rar4.split.006"
|
||||
});
|
||||
}
|
||||
|
||||
//open with ArchiveFactory.Open and stream
|
||||
[Fact]
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace SharpCompress.Test.SevenZip
|
||||
[Fact]
|
||||
public void SevenZipArchive_BZip2_Split_Working()
|
||||
{
|
||||
ArchiveStreamSplitRead(null, "7Zip.BZip2.split.001",
|
||||
ArchiveStreamMultiRead(null, "7Zip.BZip2.split.001",
|
||||
"7Zip.BZip2.split.002",
|
||||
"7Zip.BZip2.split.003",
|
||||
"7Zip.BZip2.split.004",
|
||||
@@ -125,5 +125,19 @@ namespace SharpCompress.Test.SevenZip
|
||||
"7Zip.BZip2.split.007");
|
||||
}
|
||||
|
||||
//will detect and load other files
|
||||
[Fact]
|
||||
public void SevenZipArchive_BZip2_Split_FirstFileRead()
|
||||
{
|
||||
ArchiveFileRead("7Zip.BZip2.split.001");
|
||||
//"7Zip.BZip2.split.002",
|
||||
//"7Zip.BZip2.split.003",
|
||||
//"7Zip.BZip2.split.004",
|
||||
//"7Zip.BZip2.split.005",
|
||||
//"7Zip.BZip2.split.006",
|
||||
//"7Zip.BZip2.split.007"
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,10 +134,63 @@ namespace SharpCompress.Test.Zip
|
||||
{
|
||||
ArchiveFileRead("Zip.deflate.zip");
|
||||
}
|
||||
//will detect and load other files
|
||||
[Fact]
|
||||
public void Zip_Deflate_Multi_ArchiveFirstFileRead()
|
||||
{
|
||||
ArchiveFileRead("WinZip26.nocomp.multi.zip");
|
||||
//"WinZip26.nocomp.multi.z01"
|
||||
}
|
||||
//will detect and load other files
|
||||
[Fact]
|
||||
public void ZipX_Deflate_Multi_ArchiveFirstFileRead()
|
||||
{
|
||||
ArchiveFileRead("WinZip26.nocomp.multi.zipx");
|
||||
//"WinZip26.nocomp.multi.zx01"
|
||||
}
|
||||
[Fact]
|
||||
public void Zip_GetParts()
|
||||
{
|
||||
//uses first part to search for all parts and compares against this array
|
||||
ArchiveGetParts(new string[] {
|
||||
"Infozip.nocomp.multi.zip",
|
||||
"Infozip.nocomp.multi.z01"});
|
||||
}
|
||||
[Fact]
|
||||
public void ZipX_GetParts()
|
||||
{
|
||||
//uses first part to search for all parts and compares against this array
|
||||
ArchiveGetParts(new string[] {
|
||||
"WinZip26.nocomp.multi.zipx",
|
||||
"WinZip26.nocomp.multi.zx01"});
|
||||
}
|
||||
[Fact]
|
||||
public void Zip_GetPartsSplit()
|
||||
{
|
||||
//uses first part to search for all parts and compares against this array
|
||||
ArchiveGetParts(new string[] {
|
||||
"Zip.deflate.split.001",
|
||||
"Zip.deflate.split.002",
|
||||
"Zip.deflate.split.003",
|
||||
"Zip.deflate.split.004",
|
||||
"Zip.deflate.split.005",
|
||||
"Zip.deflate.split.006"});
|
||||
}
|
||||
//will detect and load other files
|
||||
[Fact]
|
||||
public void Zip_Deflate_Split_ArchiveFirstFileRead()
|
||||
{
|
||||
ArchiveFileRead("Zip.deflate.split.001");
|
||||
//"Zip.deflate.split.002",
|
||||
//"Zip.deflate.split.003",
|
||||
//"Zip.deflate.split.004",
|
||||
//"Zip.deflate.split.005",
|
||||
//"Zip.deflate.split.006"
|
||||
}
|
||||
[Fact]
|
||||
public void Zip_Deflate_Split_ArchiveFileRead()
|
||||
{
|
||||
ArchiveStreamSplitRead(null, "Zip.deflate.split.001",
|
||||
ArchiveStreamMultiRead(null, "Zip.deflate.split.001",
|
||||
"Zip.deflate.split.002",
|
||||
"Zip.deflate.split.003",
|
||||
"Zip.deflate.split.004",
|
||||
|
||||
Reference in New Issue
Block a user