mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-04 13:34:59 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a64fe28b0 | ||
|
|
e320ccfa9a | ||
|
|
9628ff9456 | ||
|
|
d540f78cfc | ||
|
|
66420cd299 | ||
|
|
dd0594471f | ||
|
|
844ba228ee | ||
|
|
7efc701b32 | ||
|
|
d7e29f7c4d | ||
|
|
f26ba91386 | ||
|
|
c73ac2039c | ||
|
|
671f9cd0cb | ||
|
|
131b5b9714 | ||
|
|
74af0889b9 | ||
|
|
e5ee399045 | ||
| deeb7a0f64 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,3 +13,4 @@ project.lock.json
|
||||
test/TestArchives/Scratch
|
||||
.vs
|
||||
tools
|
||||
.vscode
|
||||
|
||||
@@ -10,7 +10,7 @@ Writer classes allow forward-only Writing
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Rar | Rar | Decompress (1) | RarArchive | RarReader | N/A |
|
||||
| Zip (2) | None, DEFLATE, BZip2, LZMA/LZMA2, PPMd | Both | ZipArchive | ZipReader | ZipWriter |
|
||||
| Tar | None, BZip2, GZip | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar | None, BZip2, GZip, LZip | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| GZip (single file) | GZip | Both | GZipArchive | GZipReader | GZipWriter |
|
||||
| 7Zip (4) | LZMA, LZMA2, BZip2, PPMd, BCJ, BCJ2, Deflate | Decompress | SevenZipArchive | N/A | N/A |
|
||||
|
||||
@@ -28,4 +28,6 @@ For those who want to directly compress/decompress bits
|
||||
| GZipStream | Both |
|
||||
| DeflateStream | Both |
|
||||
| LZMAStream | Both |
|
||||
| PPMdStream | Both |
|
||||
| PPMdStream | Both |
|
||||
| ADCStream | Decompress |
|
||||
| LZipStream | Decompress |
|
||||
|
||||
17
README.md
17
README.md
@@ -9,7 +9,7 @@ The major feature is support for non-seekable streams so large files can be proc
|
||||
## Need Help?
|
||||
Post Issues on Github!
|
||||
|
||||
Check the [Supported Formats](FORMATS.md) and [basic usage.](USAGE.md)
|
||||
Check the [Supported Formats](FORMATS.md) and [Basic Usage.](USAGE.md)
|
||||
|
||||
## A Simple Request
|
||||
|
||||
@@ -31,25 +31,40 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un
|
||||
|
||||
## Version Log
|
||||
|
||||
### Version 0.14.0
|
||||
|
||||
* [Support for LZip reading in for Tars](https://github.com/adamhathcock/sharpcompress/pull/191)
|
||||
|
||||
### Version 0.13.1
|
||||
|
||||
* [Fix null password on ReaderFactory. Fix null options on SevenZipArchive](https://github.com/adamhathcock/sharpcompress/pull/188)
|
||||
* [Make PpmdProperties lazy to avoid unnecessary allocations.](https://github.com/adamhathcock/sharpcompress/pull/185)
|
||||
|
||||
### Version 0.13.0
|
||||
|
||||
* Breaking change: Big refactor of Options on API.
|
||||
* 7Zip supports Deflate
|
||||
|
||||
### Version 0.12.4
|
||||
|
||||
* Forward only zip issue fix https://github.com/adamhathcock/sharpcompress/issues/160
|
||||
* Try to fix frameworks again by copying targets from JSON.NET
|
||||
|
||||
### Version 0.12.3
|
||||
|
||||
* 7Zip fixes https://github.com/adamhathcock/sharpcompress/issues/73
|
||||
* Maybe all profiles will work with project.json now
|
||||
|
||||
### Version 0.12.2
|
||||
|
||||
* Support Profile 259 again
|
||||
|
||||
### Version 0.12.1
|
||||
|
||||
* Support Silverlight 5
|
||||
|
||||
### Version 0.12.0
|
||||
|
||||
* .NET Core RTM!
|
||||
* Bug fix for Tar long paths
|
||||
|
||||
|
||||
6
USAGE.md
6
USAGE.md
@@ -80,7 +80,7 @@ using (var archive = RarArchive.Open("Test.rar"))
|
||||
### Use ReaderFactory to autodetect archive type and Open the entry stream
|
||||
|
||||
```C#
|
||||
using (Stream stream = File.OpenRead("Tar.tar.bz2")))
|
||||
using (Stream stream = File.OpenRead("Tar.tar.bz2"))
|
||||
using (var reader = ReaderFactory.Open(stream))
|
||||
{
|
||||
while (reader.MoveToNextEntry())
|
||||
@@ -101,7 +101,7 @@ using (var reader = ReaderFactory.Open(stream))
|
||||
### Use ReaderFactory to autodetect archive type and Open the entry stream
|
||||
|
||||
```C#
|
||||
using (Stream stream = File.OpenRead("Tar.tar.bz2")))
|
||||
using (Stream stream = File.OpenRead("Tar.tar.bz2"))
|
||||
using (var reader = ReaderFactory.Open(stream))
|
||||
{
|
||||
while (reader.MoveToNextEntry())
|
||||
@@ -128,4 +128,4 @@ using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, new WriterOption
|
||||
{
|
||||
writer.WriteAll("D:\\temp", "*", SearchOption.AllDirectories);
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace SharpCompress.Archives.SevenZip
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="readerOptions"></param>
|
||||
public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions)
|
||||
public static SevenZipArchive Open(FileInfo fileInfo, ReaderOptions readerOptions = null)
|
||||
{
|
||||
fileInfo.CheckNotNull("fileInfo");
|
||||
return new SevenZipArchive(fileInfo, readerOptions ?? new ReaderOptions());
|
||||
@@ -44,7 +44,7 @@ namespace SharpCompress.Archives.SevenZip
|
||||
public static SevenZipArchive Open(Stream stream, ReaderOptions readerOptions = null)
|
||||
{
|
||||
stream.CheckNotNull("stream");
|
||||
return new SevenZipArchive(stream, readerOptions);
|
||||
return new SevenZipArchive(stream, readerOptions ?? new ReaderOptions());
|
||||
}
|
||||
|
||||
#if !NO_FILE
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
LZMA,
|
||||
BCJ,
|
||||
BCJ2,
|
||||
LZip,
|
||||
Unknown
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,10 @@ namespace SharpCompress.Common
|
||||
{
|
||||
private readonly Stream actualStream;
|
||||
|
||||
internal Volume(Stream stream, ReaderOptions readerFactoryOptions)
|
||||
internal Volume(Stream stream, ReaderOptions readerOptions)
|
||||
{
|
||||
actualStream = stream;
|
||||
ReaderOptions = readerFactoryOptions;
|
||||
ReaderOptions = readerOptions;
|
||||
}
|
||||
|
||||
internal Stream Stream { get { return new NonDisposingStream(actualStream); } }
|
||||
|
||||
153
src/SharpCompress/Compressors/LZMA/LZipStream.cs
Normal file
153
src/SharpCompress/Compressors/LZMA/LZipStream.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.LZMA
|
||||
{
|
||||
// TODO:
|
||||
// - Write as well as read
|
||||
// - Multi-volume support
|
||||
// - Use of the data size / member size values at the end of the stream
|
||||
|
||||
/// <summary>
|
||||
/// Stream supporting the LZIP format, as documented at http://www.nongnu.org/lzip/manual/lzip_manual.html
|
||||
/// </summary>
|
||||
public class LZipStream : Stream
|
||||
{
|
||||
private readonly Stream stream;
|
||||
private bool disposed;
|
||||
private readonly bool leaveOpen;
|
||||
|
||||
public LZipStream(Stream stream, CompressionMode mode)
|
||||
: this(stream, mode, false)
|
||||
{
|
||||
}
|
||||
|
||||
public LZipStream(Stream stream, CompressionMode mode, bool leaveOpen)
|
||||
{
|
||||
if (mode != CompressionMode.Decompress)
|
||||
{
|
||||
throw new NotImplementedException("Only LZip decompression is currently supported");
|
||||
}
|
||||
Mode = mode;
|
||||
this.leaveOpen = leaveOpen;
|
||||
int dictionarySize = ValidateAndReadSize(stream);
|
||||
if (dictionarySize == 0)
|
||||
{
|
||||
throw new IOException("Not an LZip stream");
|
||||
}
|
||||
byte[] properties = GetProperties(dictionarySize);
|
||||
this.stream = new LzmaStream(properties, stream);
|
||||
}
|
||||
|
||||
#region Stream methods
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
disposed = true;
|
||||
if (disposing && !leaveOpen)
|
||||
{
|
||||
stream.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
public CompressionMode Mode { get; }
|
||||
|
||||
public override bool CanRead => stream.CanRead;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
stream.Flush();
|
||||
}
|
||||
|
||||
// TODO: Both Length and Position are sometimes feasible, but would require
|
||||
// reading the output length when we initialize.
|
||||
public override long Length { get { throw new NotImplementedException(); } }
|
||||
|
||||
public override long Position { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) => stream.Read(buffer, offset, count);
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the given stream is positioned at the start of a v1 LZip
|
||||
/// file, as indicated by the ASCII characters "LZIP" and a version byte
|
||||
/// of 1, followed by at least one byte.
|
||||
/// </summary>
|
||||
/// <param name="stream">The stream to read from. Must not be null.</param>
|
||||
/// <returns><c>true</c> if the given stream is an LZip file, <c>false</c> otherwise.</returns>
|
||||
public static bool IsLZipFile(Stream stream) => ValidateAndReadSize(stream) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Reads the 6-byte header of the stream, and returns 0 if either the header
|
||||
/// couldn't be read or it isn't a validate LZIP header, or the dictionary
|
||||
/// size if it *is* a valid LZIP file.
|
||||
/// </summary>
|
||||
private static int ValidateAndReadSize(Stream stream)
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
// Read the header
|
||||
byte[] header = new byte[6];
|
||||
int n = stream.Read(header, 0, header.Length);
|
||||
|
||||
// TODO: Handle reading only part of the header?
|
||||
|
||||
if (n != 6)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (header[0] != 'L' || header[1] != 'Z' || header[2] != 'I' || header[3] != 'P' || header[4] != 1 /* version 1 */)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int basePower = header[5] & 0x1F;
|
||||
int subtractionNumerator = (header[5] & 0xE0) >> 5;
|
||||
return (1 << basePower) - subtractionNumerator * (1 << (basePower - 4));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a byte array to communicate the parameters and dictionary size to LzmaStream.
|
||||
/// </summary>
|
||||
private static byte[] GetProperties(int dictionarySize) =>
|
||||
new byte[]
|
||||
{
|
||||
// Parameters as per http://www.nongnu.org/lzip/manual/lzip_manual.html#Stream-format
|
||||
// but encoded as a single byte in the format LzmaStream expects.
|
||||
// literal_context_bits = 3
|
||||
// literal_pos_state_bits = 0
|
||||
// pos_state_bits = 2
|
||||
93,
|
||||
// Dictionary size as 4-byte little-endian value
|
||||
(byte)(dictionarySize & 0xff),
|
||||
(byte)((dictionarySize >> 8) & 0xff),
|
||||
(byte)((dictionarySize >> 16) & 0xff),
|
||||
(byte)((dictionarySize >> 24) & 0xff)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,6 @@ using SharpCompress.Converters;
|
||||
|
||||
namespace SharpCompress.Compressors.PPMd
|
||||
{
|
||||
public enum PpmdVersion
|
||||
{
|
||||
H,
|
||||
H7z,
|
||||
I1
|
||||
}
|
||||
|
||||
public class PpmdProperties
|
||||
{
|
||||
public PpmdVersion Version = PpmdVersion.I1;
|
||||
|
||||
9
src/SharpCompress/Compressors/PPMd/PpmdVersion.cs
Normal file
9
src/SharpCompress/Compressors/PPMd/PpmdVersion.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace SharpCompress.Compressors.PPMd
|
||||
{
|
||||
public enum PpmdVersion
|
||||
{
|
||||
H,
|
||||
H7z,
|
||||
I1
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace SharpCompress.Readers
|
||||
Options = options;
|
||||
}
|
||||
|
||||
internal ReaderOptions Options { get; private set; }
|
||||
internal ReaderOptions Options { get; }
|
||||
|
||||
public ArchiveType ArchiveType { get; }
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ using SharpCompress.Readers.GZip;
|
||||
using SharpCompress.Readers.Rar;
|
||||
using SharpCompress.Readers.Tar;
|
||||
using SharpCompress.Readers.Zip;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
|
||||
namespace SharpCompress.Readers
|
||||
{
|
||||
@@ -33,7 +34,7 @@ namespace SharpCompress.Readers
|
||||
};
|
||||
RewindableStream rewindableStream = new RewindableStream(stream);
|
||||
rewindableStream.StartRecording();
|
||||
if (ZipArchive.IsZipFile(rewindableStream, null))
|
||||
if (ZipArchive.IsZipFile(rewindableStream, options.Password))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
return ZipReader.Open(rewindableStream, options);
|
||||
@@ -64,6 +65,18 @@ namespace SharpCompress.Readers
|
||||
}
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (LZipStream.IsLZipFile(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
LZipStream testStream = new LZipStream(rewindableStream, CompressionMode.Decompress, true);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
return new TarReader(rewindableStream, options, CompressionType.LZip);
|
||||
}
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (RarArchive.IsRarFile(rewindableStream, options))
|
||||
{
|
||||
|
||||
@@ -9,6 +9,7 @@ using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
|
||||
namespace SharpCompress.Readers.Tar
|
||||
{
|
||||
@@ -38,6 +39,10 @@ namespace SharpCompress.Readers.Tar
|
||||
{
|
||||
return new GZipStream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case CompressionType.LZip:
|
||||
{
|
||||
return new LZipStream(stream, CompressionMode.Decompress);
|
||||
}
|
||||
case CompressionType.None:
|
||||
{
|
||||
return stream;
|
||||
@@ -87,6 +92,19 @@ namespace SharpCompress.Readers.Tar
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
|
||||
rewindableStream.Rewind(false);
|
||||
if (LZipStream.IsLZipFile(rewindableStream))
|
||||
{
|
||||
rewindableStream.Rewind(false);
|
||||
LZipStream testStream = new LZipStream(rewindableStream, CompressionMode.Decompress, false);
|
||||
if (TarArchive.IsTarFile(testStream))
|
||||
{
|
||||
rewindableStream.Rewind(true);
|
||||
return new TarReader(rewindableStream, options, CompressionType.LZip);
|
||||
}
|
||||
throw new InvalidFormatException("Not a tar file.");
|
||||
}
|
||||
rewindableStream.Rewind(true);
|
||||
return new TarReader(rewindableStream, options, CompressionType.None);
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@ namespace SharpCompress.Writers.Zip
|
||||
{
|
||||
private readonly CompressionType compressionType;
|
||||
private readonly CompressionLevel compressionLevel;
|
||||
private readonly PpmdProperties ppmdProperties = new PpmdProperties(); // Caching properties to speed up PPMd
|
||||
private readonly List<ZipCentralDirectoryEntry> entries = new List<ZipCentralDirectoryEntry>();
|
||||
private readonly string zipComment;
|
||||
private long streamPosition;
|
||||
private PpmdProperties ppmdProps;
|
||||
|
||||
public ZipWriter(Stream destination, ZipWriterOptions zipWriterOptions)
|
||||
: base(ArchiveType.Zip)
|
||||
@@ -34,6 +34,18 @@ namespace SharpCompress.Writers.Zip
|
||||
InitalizeStream(destination, !zipWriterOptions.LeaveStreamOpen);
|
||||
}
|
||||
|
||||
private PpmdProperties PpmdProperties
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ppmdProps == null)
|
||||
{
|
||||
ppmdProps = new PpmdProperties();
|
||||
}
|
||||
return ppmdProps;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
if (isDisposing)
|
||||
@@ -252,8 +264,8 @@ namespace SharpCompress.Writers.Zip
|
||||
}
|
||||
case ZipCompressionMethod.PPMd:
|
||||
{
|
||||
counting.Write(writer.ppmdProperties.Properties, 0, 2);
|
||||
return new PpmdStream(writer.ppmdProperties, counting, true);
|
||||
counting.Write(writer.PpmdProperties.Properties, 0, 2);
|
||||
return new PpmdStream(writer.PpmdProperties, counting, true);
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.13.0",
|
||||
"version": "0.14.0",
|
||||
"title": "SharpCompress - Pure C# Decompression/Compression",
|
||||
"authors": [ "Adam Hathcock" ],
|
||||
"language": "en-US",
|
||||
|
||||
@@ -33,6 +33,12 @@ namespace SharpCompress.Test
|
||||
Read("Tar.tar.gz", CompressionType.GZip);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tar_LZip_Reader()
|
||||
{
|
||||
Read("Tar.tar.lz", CompressionType.LZip);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Tar_BZip2_Entry_Stream()
|
||||
{
|
||||
|
||||
@@ -228,6 +228,8 @@ namespace SharpCompress.Test
|
||||
[Fact]
|
||||
public void TestSharpCompressWithEmptyStream()
|
||||
{
|
||||
ResetScratch();
|
||||
|
||||
MemoryStream stream = new NonSeekableMemoryStream();
|
||||
|
||||
using (IWriter zipWriter = WriterFactory.Open(stream, ArchiveType.Zip, CompressionType.Deflate))
|
||||
@@ -250,7 +252,9 @@ namespace SharpCompress.Test
|
||||
byte[] buf = new byte[bufSize];
|
||||
int bytesRead = 0;
|
||||
while ((bytesRead = entry.Read(buf, 0, bufSize)) > 0)
|
||||
{
|
||||
tempStream.Write(buf, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
test/TestArchives/Archives/Tar.tar.lz
Normal file
BIN
test/TestArchives/Archives/Tar.tar.lz
Normal file
Binary file not shown.
Reference in New Issue
Block a user