2022-06-19 22:05:52 +02:00
|
|
|
using System;
|
2019-12-30 18:58:25 +01:00
|
|
|
using System.Buffers.Binary;
|
2015-12-30 11:19:42 +00:00
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
2025-12-31 14:53:55 +00:00
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
2015-12-30 11:19:42 +00:00
|
|
|
using SharpCompress.Common.Zip.Headers;
|
2016-09-26 11:49:49 +01:00
|
|
|
using SharpCompress.Compressors;
|
|
|
|
|
using SharpCompress.Compressors.BZip2;
|
|
|
|
|
using SharpCompress.Compressors.Deflate;
|
2018-02-26 23:49:09 +01:00
|
|
|
using SharpCompress.Compressors.Deflate64;
|
2024-07-24 08:31:44 +01:00
|
|
|
using SharpCompress.Compressors.Explode;
|
2016-09-26 11:49:49 +01:00
|
|
|
using SharpCompress.Compressors.LZMA;
|
|
|
|
|
using SharpCompress.Compressors.PPMd;
|
2024-07-24 08:31:44 +01:00
|
|
|
using SharpCompress.Compressors.Reduce;
|
2024-01-27 18:28:46 +01:00
|
|
|
using SharpCompress.Compressors.Shrink;
|
2023-02-02 00:18:37 +00:00
|
|
|
using SharpCompress.Compressors.Xz;
|
2025-12-18 12:30:37 +00:00
|
|
|
using SharpCompress.Compressors.ZStandard;
|
2015-12-30 11:19:42 +00:00
|
|
|
using SharpCompress.IO;
|
|
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
namespace SharpCompress.Common.Zip;
|
|
|
|
|
|
|
|
|
|
internal abstract class ZipFilePart : FilePart
|
2015-12-30 11:19:42 +00:00
|
|
|
{
|
2023-03-21 13:14:08 +00:00
|
|
|
internal ZipFilePart(ZipFileEntry header, Stream stream)
|
|
|
|
|
: base(header.ArchiveEncoding)
|
2015-12-30 11:19:42 +00:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
Header = header;
|
|
|
|
|
header.Part = this;
|
|
|
|
|
BaseStream = stream;
|
|
|
|
|
}
|
2015-12-30 11:19:42 +00:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
internal Stream BaseStream { get; }
|
|
|
|
|
internal ZipFileEntry Header { get; set; }
|
2015-12-30 11:19:42 +00:00
|
|
|
|
2024-04-18 14:25:10 +01:00
|
|
|
internal override string? FilePartName => Header.Name;
|
2015-12-30 11:19:42 +00:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
internal override Stream GetCompressedStream()
|
|
|
|
|
{
|
|
|
|
|
if (!Header.HasData)
|
2015-12-30 11:19:42 +00:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
return Stream.Null;
|
2015-12-30 11:19:42 +00:00
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
var decompressionStream = CreateDecompressionStream(
|
|
|
|
|
GetCryptoStream(CreateBaseStream()),
|
|
|
|
|
Header.CompressionMethod
|
|
|
|
|
);
|
|
|
|
|
if (LeaveStreamOpen)
|
|
|
|
|
{
|
2025-07-20 17:35:22 +01:00
|
|
|
return SharpCompressStream.Create(decompressionStream, leaveOpen: true);
|
2022-12-20 15:06:44 +00:00
|
|
|
}
|
|
|
|
|
return decompressionStream;
|
|
|
|
|
}
|
2015-12-30 11:19:42 +00:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
internal override Stream GetRawStream()
|
|
|
|
|
{
|
|
|
|
|
if (!Header.HasData)
|
2015-12-30 11:19:42 +00:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
return Stream.Null;
|
2015-12-30 11:19:42 +00:00
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
return CreateBaseStream();
|
|
|
|
|
}
|
2015-12-30 11:19:42 +00:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
protected abstract Stream CreateBaseStream();
|
2015-12-30 11:19:42 +00:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
protected bool LeaveStreamOpen =>
|
|
|
|
|
FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) || Header.IsZip64;
|
2015-12-30 11:19:42 +00:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
protected Stream CreateDecompressionStream(Stream stream, ZipCompressionMethod method)
|
|
|
|
|
{
|
|
|
|
|
switch (method)
|
2015-12-30 11:19:42 +00:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
case ZipCompressionMethod.None:
|
2015-12-30 11:19:42 +00:00
|
|
|
{
|
2025-08-21 19:55:35 +02:00
|
|
|
if (Header.CompressedSize is 0)
|
2023-02-19 01:33:22 +01:00
|
|
|
{
|
2025-08-21 19:55:35 +02:00
|
|
|
return new DataDescriptorStream(stream);
|
2023-02-19 01:33:22 +01:00
|
|
|
}
|
|
|
|
|
|
2025-08-21 19:55:35 +02:00
|
|
|
return stream;
|
2022-12-20 15:06:44 +00:00
|
|
|
}
|
2024-01-27 18:28:46 +01:00
|
|
|
case ZipCompressionMethod.Shrink:
|
|
|
|
|
{
|
2024-01-27 18:59:56 +01:00
|
|
|
return new ShrinkStream(
|
|
|
|
|
stream,
|
|
|
|
|
CompressionMode.Decompress,
|
|
|
|
|
Header.CompressedSize,
|
|
|
|
|
Header.UncompressedSize
|
|
|
|
|
);
|
2024-01-27 18:28:46 +01:00
|
|
|
}
|
2024-06-25 11:35:11 -05:00
|
|
|
case ZipCompressionMethod.Reduce1:
|
|
|
|
|
{
|
|
|
|
|
return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 1);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Reduce2:
|
|
|
|
|
{
|
|
|
|
|
return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 2);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Reduce3:
|
|
|
|
|
{
|
|
|
|
|
return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 3);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Reduce4:
|
|
|
|
|
{
|
|
|
|
|
return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 4);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Explode:
|
|
|
|
|
{
|
2024-07-24 08:31:44 +01:00
|
|
|
return new ExplodeStream(
|
|
|
|
|
stream,
|
|
|
|
|
Header.CompressedSize,
|
|
|
|
|
Header.UncompressedSize,
|
|
|
|
|
Header.Flags
|
|
|
|
|
);
|
2024-06-25 11:35:11 -05:00
|
|
|
}
|
|
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
case ZipCompressionMethod.Deflate:
|
|
|
|
|
{
|
|
|
|
|
return new DeflateStream(stream, CompressionMode.Decompress);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Deflate64:
|
|
|
|
|
{
|
|
|
|
|
return new Deflate64Stream(stream, CompressionMode.Decompress);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.BZip2:
|
|
|
|
|
{
|
|
|
|
|
return new BZip2Stream(stream, CompressionMode.Decompress, false);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.LZMA:
|
|
|
|
|
{
|
|
|
|
|
if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted))
|
2022-12-20 13:45:47 +00:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
throw new NotSupportedException("LZMA with pkware encryption.");
|
2022-12-20 13:45:47 +00:00
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
var reader = new BinaryReader(stream);
|
|
|
|
|
reader.ReadUInt16(); //LZMA version
|
|
|
|
|
var props = new byte[reader.ReadUInt16()];
|
|
|
|
|
reader.Read(props, 0, props.Length);
|
|
|
|
|
return new LzmaStream(
|
|
|
|
|
props,
|
|
|
|
|
stream,
|
|
|
|
|
Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1,
|
|
|
|
|
FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
|
|
|
|
|
? -1
|
|
|
|
|
: Header.UncompressedSize
|
|
|
|
|
);
|
|
|
|
|
}
|
2023-02-02 00:18:37 +00:00
|
|
|
case ZipCompressionMethod.Xz:
|
|
|
|
|
{
|
|
|
|
|
return new XZStream(stream);
|
|
|
|
|
}
|
2025-07-24 00:23:55 +01:00
|
|
|
case ZipCompressionMethod.ZStandard:
|
2023-02-02 01:06:18 +00:00
|
|
|
{
|
|
|
|
|
return new DecompressionStream(stream);
|
|
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
case ZipCompressionMethod.PPMd:
|
|
|
|
|
{
|
|
|
|
|
Span<byte> props = stackalloc byte[2];
|
|
|
|
|
stream.ReadFully(props);
|
|
|
|
|
return new PpmdStream(new PpmdProperties(props), stream, false);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.WinzipAes:
|
|
|
|
|
{
|
2022-12-20 15:14:22 +00:00
|
|
|
var data = Header.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes);
|
2022-12-20 15:06:44 +00:00
|
|
|
if (data is null)
|
2022-12-20 13:45:47 +00:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
throw new InvalidFormatException("No Winzip AES extra data found.");
|
2022-12-20 13:45:47 +00:00
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
if (data.Length != 7)
|
2022-12-20 13:45:47 +00:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
throw new InvalidFormatException("Winzip data length is not 7.");
|
2022-12-20 13:45:47 +00:00
|
|
|
}
|
2022-12-20 15:14:22 +00:00
|
|
|
var compressedMethod = BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes);
|
2022-12-20 15:06:44 +00:00
|
|
|
|
|
|
|
|
if (compressedMethod != 0x01 && compressedMethod != 0x02)
|
2022-12-20 13:45:47 +00:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
throw new InvalidFormatException(
|
|
|
|
|
"Unexpected vendor version number for WinZip AES metadata"
|
2022-12-20 13:45:47 +00:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-20 15:14:22 +00:00
|
|
|
var vendorId = BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(2));
|
2022-12-20 15:06:44 +00:00
|
|
|
if (vendorId != 0x4541)
|
2022-12-20 13:45:47 +00:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
throw new InvalidFormatException(
|
|
|
|
|
"Unexpected vendor ID for WinZip AES metadata"
|
2022-12-20 13:45:47 +00:00
|
|
|
);
|
|
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
return CreateDecompressionStream(
|
|
|
|
|
stream,
|
|
|
|
|
(ZipCompressionMethod)
|
|
|
|
|
BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
2022-12-20 15:14:22 +00:00
|
|
|
throw new NotSupportedException("CompressionMethod: " + Header.CompressionMethod);
|
2015-12-30 11:19:42 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected Stream GetCryptoStream(Stream plainStream)
|
|
|
|
|
{
|
|
|
|
|
var isFileEncrypted = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted);
|
2015-12-30 11:19:42 +00:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
if (Header.CompressedSize == 0 && isFileEncrypted)
|
2016-11-24 17:11:43 +03:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
throw new NotSupportedException("Cannot encrypt file with unknown size at start.");
|
|
|
|
|
}
|
2016-11-24 20:41:08 +03:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
if (
|
|
|
|
|
(
|
|
|
|
|
Header.CompressedSize == 0
|
|
|
|
|
&& FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)
|
|
|
|
|
) || Header.IsZip64
|
|
|
|
|
)
|
|
|
|
|
{
|
2025-07-20 17:35:22 +01:00
|
|
|
plainStream = SharpCompressStream.Create(plainStream, leaveOpen: true); //make sure AES doesn't close
|
2022-12-20 15:06:44 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close
|
|
|
|
|
}
|
2016-11-24 17:11:43 +03:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
if (isFileEncrypted)
|
|
|
|
|
{
|
|
|
|
|
switch (Header.CompressionMethod)
|
2016-11-24 17:11:43 +03:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
case ZipCompressionMethod.None:
|
2024-01-27 18:28:46 +01:00
|
|
|
case ZipCompressionMethod.Shrink:
|
2024-06-25 11:35:11 -05:00
|
|
|
case ZipCompressionMethod.Reduce1:
|
|
|
|
|
case ZipCompressionMethod.Reduce2:
|
|
|
|
|
case ZipCompressionMethod.Reduce3:
|
|
|
|
|
case ZipCompressionMethod.Reduce4:
|
2022-12-20 15:06:44 +00:00
|
|
|
case ZipCompressionMethod.Deflate:
|
|
|
|
|
case ZipCompressionMethod.Deflate64:
|
|
|
|
|
case ZipCompressionMethod.BZip2:
|
|
|
|
|
case ZipCompressionMethod.LZMA:
|
|
|
|
|
case ZipCompressionMethod.PPMd:
|
|
|
|
|
{
|
|
|
|
|
return new PkwareTraditionalCryptoStream(
|
|
|
|
|
plainStream,
|
|
|
|
|
Header.ComposeEncryptionData(plainStream),
|
|
|
|
|
CryptoMode.Decrypt
|
|
|
|
|
);
|
|
|
|
|
}
|
2016-11-24 17:11:43 +03:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
case ZipCompressionMethod.WinzipAes:
|
2016-11-24 17:11:43 +03:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
if (Header.WinzipAesEncryptionData != null)
|
2022-12-20 13:45:47 +00:00
|
|
|
{
|
2022-12-20 15:06:44 +00:00
|
|
|
return new WinzipAesCryptoStream(
|
2022-12-20 13:45:47 +00:00
|
|
|
plainStream,
|
2022-12-20 15:06:44 +00:00
|
|
|
Header.WinzipAesEncryptionData,
|
|
|
|
|
Header.CompressedSize - 10
|
2022-12-20 13:45:47 +00:00
|
|
|
);
|
|
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
return plainStream;
|
|
|
|
|
}
|
2016-11-24 17:11:43 +03:00
|
|
|
|
2022-12-20 15:06:44 +00:00
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException("Header.CompressionMethod is invalid");
|
2016-11-24 17:11:43 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-12-20 15:06:44 +00:00
|
|
|
return plainStream;
|
2016-11-24 17:11:43 +03:00
|
|
|
}
|
2025-12-31 14:53:55 +00:00
|
|
|
|
|
|
|
|
internal override async Task<Stream?> GetCompressedStreamAsync(
|
|
|
|
|
CancellationToken cancellationToken = default
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
if (!Header.HasData)
|
|
|
|
|
{
|
|
|
|
|
return Stream.Null;
|
|
|
|
|
}
|
|
|
|
|
var decompressionStream = await CreateDecompressionStreamAsync(
|
|
|
|
|
await GetCryptoStreamAsync(CreateBaseStream(), cancellationToken)
|
|
|
|
|
.ConfigureAwait(false),
|
|
|
|
|
Header.CompressionMethod,
|
|
|
|
|
cancellationToken
|
|
|
|
|
)
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
if (LeaveStreamOpen)
|
|
|
|
|
{
|
|
|
|
|
return SharpCompressStream.Create(decompressionStream, leaveOpen: true);
|
|
|
|
|
}
|
|
|
|
|
return decompressionStream;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected async Task<Stream> GetCryptoStreamAsync(
|
|
|
|
|
Stream plainStream,
|
|
|
|
|
CancellationToken cancellationToken = default
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
var isFileEncrypted = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted);
|
|
|
|
|
|
|
|
|
|
if (Header.CompressedSize == 0 && isFileEncrypted)
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException("Cannot encrypt file with unknown size at start.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
(
|
|
|
|
|
Header.CompressedSize == 0
|
|
|
|
|
&& FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)
|
|
|
|
|
) || Header.IsZip64
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
plainStream = SharpCompressStream.Create(plainStream, leaveOpen: true); //make sure AES doesn't close
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isFileEncrypted)
|
|
|
|
|
{
|
|
|
|
|
switch (Header.CompressionMethod)
|
|
|
|
|
{
|
|
|
|
|
case ZipCompressionMethod.None:
|
|
|
|
|
case ZipCompressionMethod.Shrink:
|
|
|
|
|
case ZipCompressionMethod.Reduce1:
|
|
|
|
|
case ZipCompressionMethod.Reduce2:
|
|
|
|
|
case ZipCompressionMethod.Reduce3:
|
|
|
|
|
case ZipCompressionMethod.Reduce4:
|
|
|
|
|
case ZipCompressionMethod.Deflate:
|
|
|
|
|
case ZipCompressionMethod.Deflate64:
|
|
|
|
|
case ZipCompressionMethod.BZip2:
|
|
|
|
|
case ZipCompressionMethod.LZMA:
|
|
|
|
|
case ZipCompressionMethod.PPMd:
|
|
|
|
|
{
|
|
|
|
|
return new PkwareTraditionalCryptoStream(
|
|
|
|
|
plainStream,
|
|
|
|
|
await Header
|
|
|
|
|
.ComposeEncryptionDataAsync(plainStream, cancellationToken)
|
|
|
|
|
.ConfigureAwait(false),
|
|
|
|
|
CryptoMode.Decrypt
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ZipCompressionMethod.WinzipAes:
|
|
|
|
|
{
|
|
|
|
|
if (Header.WinzipAesEncryptionData != null)
|
|
|
|
|
{
|
|
|
|
|
return new WinzipAesCryptoStream(
|
|
|
|
|
plainStream,
|
|
|
|
|
Header.WinzipAesEncryptionData,
|
|
|
|
|
Header.CompressedSize - 10
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return plainStream;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidOperationException("Header.CompressionMethod is invalid");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return plainStream;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected async Task<Stream> CreateDecompressionStreamAsync(
|
|
|
|
|
Stream stream,
|
|
|
|
|
ZipCompressionMethod method,
|
|
|
|
|
CancellationToken cancellationToken = default
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
switch (method)
|
|
|
|
|
{
|
|
|
|
|
case ZipCompressionMethod.None:
|
|
|
|
|
{
|
|
|
|
|
if (Header.CompressedSize is 0)
|
|
|
|
|
{
|
|
|
|
|
return new DataDescriptorStream(stream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return stream;
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Shrink:
|
|
|
|
|
{
|
|
|
|
|
return new ShrinkStream(
|
|
|
|
|
stream,
|
|
|
|
|
CompressionMode.Decompress,
|
|
|
|
|
Header.CompressedSize,
|
|
|
|
|
Header.UncompressedSize
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Reduce1:
|
|
|
|
|
{
|
|
|
|
|
return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 1);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Reduce2:
|
|
|
|
|
{
|
|
|
|
|
return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 2);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Reduce3:
|
|
|
|
|
{
|
|
|
|
|
return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 3);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Reduce4:
|
|
|
|
|
{
|
|
|
|
|
return new ReduceStream(stream, Header.CompressedSize, Header.UncompressedSize, 4);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Explode:
|
|
|
|
|
{
|
|
|
|
|
return new ExplodeStream(
|
|
|
|
|
stream,
|
|
|
|
|
Header.CompressedSize,
|
|
|
|
|
Header.UncompressedSize,
|
|
|
|
|
Header.Flags
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case ZipCompressionMethod.Deflate:
|
|
|
|
|
{
|
|
|
|
|
return new DeflateStream(stream, CompressionMode.Decompress);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Deflate64:
|
|
|
|
|
{
|
|
|
|
|
return new Deflate64Stream(stream, CompressionMode.Decompress);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.BZip2:
|
|
|
|
|
{
|
|
|
|
|
return new BZip2Stream(stream, CompressionMode.Decompress, false);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.LZMA:
|
|
|
|
|
{
|
|
|
|
|
if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted))
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException("LZMA with pkware encryption.");
|
|
|
|
|
}
|
|
|
|
|
var buffer = new byte[4];
|
|
|
|
|
await stream.ReadFullyAsync(buffer, 0, 4, cancellationToken).ConfigureAwait(false);
|
|
|
|
|
var version = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(0, 2));
|
|
|
|
|
var propsSize = BinaryPrimitives.ReadUInt16LittleEndian(buffer.AsSpan(2, 2));
|
|
|
|
|
var props = new byte[propsSize];
|
|
|
|
|
await stream
|
|
|
|
|
.ReadFullyAsync(props, 0, propsSize, cancellationToken)
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
return new LzmaStream(
|
|
|
|
|
props,
|
|
|
|
|
stream,
|
|
|
|
|
Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1,
|
|
|
|
|
FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
|
|
|
|
|
? -1
|
|
|
|
|
: Header.UncompressedSize
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.Xz:
|
|
|
|
|
{
|
|
|
|
|
return new XZStream(stream);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.ZStandard:
|
|
|
|
|
{
|
|
|
|
|
return new DecompressionStream(stream);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.PPMd:
|
|
|
|
|
{
|
|
|
|
|
var props = new byte[2];
|
|
|
|
|
await stream.ReadFullyAsync(props, 0, 2, cancellationToken).ConfigureAwait(false);
|
|
|
|
|
return new PpmdStream(new PpmdProperties(props), stream, false);
|
|
|
|
|
}
|
|
|
|
|
case ZipCompressionMethod.WinzipAes:
|
|
|
|
|
{
|
|
|
|
|
var data = Header.Extra.SingleOrDefault(x => x.Type == ExtraDataType.WinZipAes);
|
|
|
|
|
if (data is null)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidFormatException("No Winzip AES extra data found.");
|
|
|
|
|
}
|
2026-01-07 14:54:32 +00:00
|
|
|
|
2025-12-31 14:53:55 +00:00
|
|
|
if (data.Length != 7)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidFormatException("Winzip data length is not 7.");
|
|
|
|
|
}
|
2026-01-07 14:54:32 +00:00
|
|
|
|
|
|
|
|
var compressedMethod = BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes);
|
|
|
|
|
|
|
|
|
|
if (compressedMethod != 0x01 && compressedMethod != 0x02)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidFormatException(
|
|
|
|
|
"Unexpected vendor version number for WinZip AES metadata"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var vendorId = BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(2));
|
|
|
|
|
if (vendorId != 0x4541)
|
|
|
|
|
{
|
|
|
|
|
throw new InvalidFormatException(
|
|
|
|
|
"Unexpected vendor ID for WinZip AES metadata"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return await CreateDecompressionStreamAsync(
|
|
|
|
|
stream,
|
|
|
|
|
(ZipCompressionMethod)
|
|
|
|
|
BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5)),
|
|
|
|
|
cancellationToken
|
|
|
|
|
);
|
2025-12-31 14:53:55 +00:00
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
throw new NotSupportedException("CompressionMethod: " + Header.CompressionMethod);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-12-30 18:58:25 +01:00
|
|
|
}
|