mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-06 13:34:58 +00:00
Compare commits
168 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b1ba9a00c | ||
|
|
b02584ef9e | ||
|
|
88cd6bfd1a | ||
|
|
89a3da14c9 | ||
|
|
619d987492 | ||
|
|
744e410a1a | ||
|
|
6e51967993 | ||
|
|
7989ab2e28 | ||
|
|
a3570a568d | ||
|
|
c0cd998836 | ||
|
|
1a452acd1c | ||
|
|
6c54083b08 | ||
|
|
76105ebdaf | ||
|
|
2c452201fa | ||
|
|
9fc7fc73f9 | ||
|
|
e7417e35ba | ||
|
|
19eb4c7cba | ||
|
|
1f39a0c9da | ||
|
|
a480a8893c | ||
|
|
d3a9e341a5 | ||
|
|
95caffe607 | ||
|
|
fdeca61284 | ||
|
|
45dc653191 | ||
|
|
6e48302b7b | ||
|
|
a7918d7b11 | ||
|
|
ec21253af9 | ||
|
|
97cdda8663 | ||
|
|
35ac2b9d71 | ||
|
|
14affd8ffa | ||
|
|
c48409c903 | ||
|
|
227f66f299 | ||
|
|
c2d9bf94d1 | ||
|
|
8f03841161 | ||
|
|
344a1ed912 | ||
|
|
ff71993b31 | ||
|
|
18bb773b2c | ||
|
|
7f905c7940 | ||
|
|
8a4ba6fc56 | ||
|
|
e0b275c01c | ||
|
|
5926db85df | ||
|
|
a4715a10e7 | ||
|
|
de0f5c0fcb | ||
|
|
88d85ce6ac | ||
|
|
131c171d3e | ||
|
|
c5ddef6ef7 | ||
|
|
f36486d006 | ||
|
|
eaf466c5c3 | ||
|
|
b41bcc349e | ||
|
|
825c61bdcd | ||
|
|
f13f49bd71 | ||
|
|
88f5d4544b | ||
|
|
b7432a20f0 | ||
|
|
ab57c85e66 | ||
|
|
c7c41bc0d8 | ||
|
|
c66f9b06a4 | ||
|
|
a3ac7e7ca7 | ||
|
|
187b762f8a | ||
|
|
3a7fbdfa52 | ||
|
|
bbeb46b37f | ||
|
|
5450b9a700 | ||
|
|
353b28647c | ||
|
|
2411f4f870 | ||
|
|
34cd059423 | ||
|
|
4b4ec12c87 | ||
|
|
441147c0dc | ||
|
|
08ceac9f46 | ||
|
|
6bc690b50b | ||
|
|
c41888b338 | ||
|
|
6d3c980b39 | ||
|
|
eb5db176fa | ||
|
|
a77c03fcaf | ||
|
|
04b25011e9 | ||
|
|
96b34aec10 | ||
|
|
d560b46c85 | ||
|
|
94789ce455 | ||
|
|
55797e5873 | ||
|
|
675cab3074 | ||
|
|
8d63ab646e | ||
|
|
37a2fa1cdc | ||
|
|
79ed9650b3 | ||
|
|
b6dc58164e | ||
|
|
f9a974c1fe | ||
|
|
91e672befb | ||
|
|
c14d18b9df | ||
|
|
d2cfc1844c | ||
|
|
2fb3243a1a | ||
|
|
6f3124a84f | ||
|
|
59eb5bd9c4 | ||
|
|
ad2b6bb4f7 | ||
|
|
51d64ee10e | ||
|
|
1159efc6cc | ||
|
|
3cfb76a56f | ||
|
|
7a1ad6688a | ||
|
|
4f4af6e3fd | ||
|
|
2736b79097 | ||
|
|
8da2ffa433 | ||
|
|
5bf3d6dc32 | ||
|
|
30d4380afe | ||
|
|
c8b5aad482 | ||
|
|
fa1d440947 | ||
|
|
a89fc3a276 | ||
|
|
3875f62453 | ||
|
|
23e1447ab6 | ||
|
|
91364c6a7c | ||
|
|
a070493fba | ||
|
|
ca0a6ab72c | ||
|
|
10e0562a82 | ||
|
|
44998a2a2b | ||
|
|
f8e033e560 | ||
|
|
5842da84af | ||
|
|
10cc270170 | ||
|
|
f51bdd56aa | ||
|
|
7f79c49f93 | ||
|
|
e4920255f0 | ||
|
|
f8e8273d39 | ||
|
|
ceddab98d1 | ||
|
|
0faa176791 | ||
|
|
9b0e0ee536 | ||
|
|
881d2756db | ||
|
|
ab5af40999 | ||
|
|
6aeef8dcf9 | ||
|
|
5d62196dde | ||
|
|
7fe27ac310 | ||
|
|
1e300349ce | ||
|
|
6b01a7b08e | ||
|
|
34d948df18 | ||
|
|
27091c4f1d | ||
|
|
970a3d7f2a | ||
|
|
2bedbbfc54 | ||
|
|
8de33f0db3 | ||
|
|
df4eab67dc | ||
|
|
2d13bc0046 | ||
|
|
704a0cb35d | ||
|
|
06a983e445 | ||
|
|
2d10df8b87 | ||
|
|
baf66db9dc | ||
|
|
3545693999 | ||
|
|
84fb99c2c8 | ||
|
|
21e2983ae1 | ||
|
|
004e0941d5 | ||
|
|
188a426dde | ||
|
|
6fcfae8bfe | ||
|
|
9515350f52 | ||
|
|
6b88f82656 | ||
|
|
e42d953f47 | ||
|
|
471a3f63fe | ||
|
|
9c257faf26 | ||
|
|
d18cad6d76 | ||
|
|
061273be22 | ||
|
|
b89de6caad | ||
|
|
9bc0a1d7c7 | ||
|
|
eee518b7fa | ||
|
|
b7b78edaa3 | ||
|
|
3eaac68ab4 | ||
|
|
a7672190e9 | ||
|
|
4e4e89b6eb | ||
|
|
33dd519f56 | ||
|
|
5c1149aa8b | ||
|
|
9061e92af6 | ||
|
|
49f5ceaa9b | ||
|
|
525b309d37 | ||
|
|
bdb3a787fc | ||
|
|
a9601ef848 | ||
|
|
5d9c99508d | ||
|
|
e4d5b56951 | ||
|
|
e31238d121 | ||
|
|
a283d99e1b | ||
|
|
8cb621ebed |
@@ -3,10 +3,11 @@
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"csharpier": {
|
||||
"version": "0.28.1",
|
||||
"version": "1.0.2",
|
||||
"commands": [
|
||||
"dotnet-csharpier"
|
||||
]
|
||||
"csharpier"
|
||||
],
|
||||
"rollForward": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,19 @@
|
||||
<Project>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Bullseye" Version="5.0.0" />
|
||||
<PackageVersion Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageVersion Include="Bullseye" Version="6.0.0" />
|
||||
<PackageVersion Include="AwesomeAssertions" Version="9.0.0" />
|
||||
<PackageVersion Include="Glob" Version="1.1.9" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
|
||||
<PackageVersion Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
<PackageVersion Include="SimpleExec" Version="12.0.0" />
|
||||
<PackageVersion Include="System.Memory" Version="4.5.5" />
|
||||
<PackageVersion Include="System.Buffers" Version="4.6.0" />
|
||||
<PackageVersion Include="System.Memory" Version="4.6.0" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||
<PackageVersion Include="xunit" Version="2.7.1" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.8" />
|
||||
<PackageVersion Include="xunit.SkippableFact" Version="1.4.13" />
|
||||
<PackageVersion Include="ZstdSharp.Port" Version="0.8.0" />
|
||||
<PackageVersion Include="xunit" Version="2.9.3" />
|
||||
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.0" />
|
||||
<PackageVersion Include="xunit.SkippableFact" Version="1.5.23" />
|
||||
<PackageVersion Include="ZstdSharp.Port" Version="0.8.5" />
|
||||
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
| Archive Format | Compression Format(s) | Compress/Decompress | Archive API | Reader API | Writer API |
|
||||
| ---------------------- | ------------------------------------------------- | ------------------- | --------------- | ---------- | ------------- |
|
||||
| Rar | Rar | Decompress (1) | RarArchive | RarReader | N/A |
|
||||
| Zip (2) | None, DEFLATE, Deflate64, BZip2, LZMA/LZMA2, PPMd | Both | ZipArchive | ZipReader | ZipWriter |
|
||||
| Zip (2) | None, Shrink, Reduce, Implode, DEFLATE, Deflate64, BZip2, LZMA/LZMA2, PPMd | Both | ZipArchive | ZipReader | ZipWriter |
|
||||
| Tar | None | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.GZip | DEFLATE | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
| Tar.BZip2 | BZip2 | Both | TarArchive | TarReader | TarWriter (3) |
|
||||
|
||||
@@ -20,6 +20,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Config", "Config", "{CDB425
|
||||
.editorconfig = .editorconfig
|
||||
Directory.Packages.props = Directory.Packages.props
|
||||
NuGet.config = NuGet.config
|
||||
.github\workflows\dotnetcore.yml = .github\workflows\dotnetcore.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
|
||||
30
USAGE.md
30
USAGE.md
@@ -27,7 +27,7 @@ To deal with the "correct" rules as well as the expectations of users, I've deci
|
||||
|
||||
To be explicit though, consider always using the overloads that use `ReaderOptions` or `WriterOptions` and explicitly set `LeaveStreamOpen` the way you want.
|
||||
|
||||
If using Compression Stream classes directly and you don't want the wrapped stream to be closed. Use the `NonDisposingStream` as a wrapped to prevent the stream being disposed. The change in 0.21 simplified a lot even though the usage is a bit more convoluted.
|
||||
If using Compression Stream classes directly and you don't want the wrapped stream to be closed. Use the `NonDisposingStream` as a wrapper to prevent the stream being disposed. The change in 0.21 simplified a lot even though the usage is a bit more convoluted.
|
||||
|
||||
## Samples
|
||||
|
||||
@@ -71,18 +71,34 @@ using (var archive = ZipArchive.Create())
|
||||
memoryStream.Position = 0;
|
||||
```
|
||||
|
||||
### Extract all files from a Rar file to a directory using RarArchive
|
||||
### Extract all files from a rar file to a directory using RarArchive
|
||||
|
||||
Note: Extracting a solid rar or 7z file needs to be done in sequential order to get acceptable decompression speed.
|
||||
It is explicitly recommended to use `ExtractAllEntries` when extracting an entire `IArchive` instead of iterating over all its `Entries`.
|
||||
Alternatively, use `IArchive.WriteToDirectory`.
|
||||
|
||||
```C#
|
||||
using (var archive = RarArchive.Open("Test.rar"))
|
||||
{
|
||||
using (var reader = archive.ExtractAllEntries())
|
||||
{
|
||||
reader.WriteAllToDirectory(@"D:\temp", new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Iterate over all files from a Rar file using RarArchive
|
||||
|
||||
```C#
|
||||
using (var archive = RarArchive.Open("Test.rar"))
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
entry.WriteToDirectory("D:\\temp", new ExtractionOptions()
|
||||
{
|
||||
ExtractFullPath = true,
|
||||
Overwrite = true
|
||||
});
|
||||
Console.WriteLine($"{entry.Key}: {entry.Size} bytes");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -15,7 +15,7 @@ const string Publish = "publish";
|
||||
|
||||
Target(
|
||||
Clean,
|
||||
ForEach("**/bin", "**/obj"),
|
||||
["**/bin", "**/obj"],
|
||||
dir =>
|
||||
{
|
||||
IEnumerable<string> GetDirectories(string d)
|
||||
@@ -44,14 +44,14 @@ Target(
|
||||
() =>
|
||||
{
|
||||
Run("dotnet", "tool restore");
|
||||
Run("dotnet", "csharpier --check .");
|
||||
Run("dotnet", "csharpier check .");
|
||||
}
|
||||
);
|
||||
Target(Restore, DependsOn(Format), () => Run("dotnet", "restore"));
|
||||
Target(Restore, [Format], () => Run("dotnet", "restore"));
|
||||
|
||||
Target(
|
||||
Build,
|
||||
DependsOn(Restore),
|
||||
[Restore],
|
||||
() =>
|
||||
{
|
||||
Run("dotnet", "build src/SharpCompress/SharpCompress.csproj -c Release --no-restore");
|
||||
@@ -60,8 +60,8 @@ Target(
|
||||
|
||||
Target(
|
||||
Test,
|
||||
DependsOn(Build),
|
||||
ForEach("net8.0", "net462"),
|
||||
[Build],
|
||||
["net8.0", "net48"],
|
||||
framework =>
|
||||
{
|
||||
IEnumerable<string> GetFiles(string d)
|
||||
@@ -69,7 +69,7 @@ Target(
|
||||
return Glob.Files(".", d);
|
||||
}
|
||||
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && framework == "net462")
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && framework == "net48")
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -83,13 +83,13 @@ Target(
|
||||
|
||||
Target(
|
||||
Publish,
|
||||
DependsOn(Test),
|
||||
[Test],
|
||||
() =>
|
||||
{
|
||||
Run("dotnet", "pack src/SharpCompress/SharpCompress.csproj -c Release -o artifacts/");
|
||||
}
|
||||
);
|
||||
|
||||
Target("default", DependsOn(Publish), () => Console.WriteLine("Done!"));
|
||||
Target("default", [Publish], () => Console.WriteLine("Done!"));
|
||||
|
||||
await RunTargetsAndExitAsync(args);
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Bullseye" />
|
||||
<PackageReference Include="Glob" />
|
||||
<PackageReference Include="SimpleExec" />
|
||||
<PackageReference Include="SimpleExec" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
"net8.0": {
|
||||
"Bullseye": {
|
||||
"type": "Direct",
|
||||
"requested": "[5.0.0, )",
|
||||
"resolved": "5.0.0",
|
||||
"contentHash": "bqyt+m17ym+5aN45C5oZRAjuLDt8jKiCm/ys1XfymIXSkrTFwvI/QsbY3ucPSHDz7SF7uON7B57kXFv5H2k1ew=="
|
||||
"requested": "[6.0.0, )",
|
||||
"resolved": "6.0.0",
|
||||
"contentHash": "vgwwXfzs7jJrskWH7saHRMgPzziq/e86QZNWY1MnMxd7e+De7E7EX4K3C7yrvaK9y02SJoLxNxcLG/q5qUAghw=="
|
||||
},
|
||||
"Glob": {
|
||||
"type": "Direct",
|
||||
@@ -14,11 +14,31 @@
|
||||
"resolved": "1.1.9",
|
||||
"contentHash": "AfK5+ECWYTP7G3AAdnU8IfVj+QpGjrh9GC2mpdcJzCvtQ4pnerAGwHsxJ9D4/RnhDUz2DSzd951O/lQjQby2Sw=="
|
||||
},
|
||||
"Microsoft.SourceLink.GitHub": {
|
||||
"type": "Direct",
|
||||
"requested": "[8.0.0, )",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
|
||||
"dependencies": {
|
||||
"Microsoft.Build.Tasks.Git": "8.0.0",
|
||||
"Microsoft.SourceLink.Common": "8.0.0"
|
||||
}
|
||||
},
|
||||
"SimpleExec": {
|
||||
"type": "Direct",
|
||||
"requested": "[12.0.0, )",
|
||||
"resolved": "12.0.0",
|
||||
"contentHash": "ptxlWtxC8vM6Y6e3h9ZTxBBkOWnWrm/Sa1HT+2i1xcXY3Hx2hmKDZP5RShPf8Xr9D+ivlrXNy57ktzyH8kyt+Q=="
|
||||
},
|
||||
"Microsoft.Build.Tasks.Git": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
|
||||
},
|
||||
"Microsoft.SourceLink.Common": {
|
||||
"type": "Transitive",
|
||||
"resolved": "8.0.0",
|
||||
"contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ internal static class Adler32 // From https://github.com/SixLabors/ImageSharp/bl
|
||||
4,
|
||||
3,
|
||||
2,
|
||||
1 // tap2
|
||||
1, // tap2
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ public abstract class AbstractArchive<TEntry, TVolume> : IArchive, IArchiveExtra
|
||||
{
|
||||
if (!stream.CanSeek || !stream.CanRead)
|
||||
{
|
||||
throw new ArgumentException("Archive streams must be Readable and Seekable");
|
||||
throw new ArchiveException("Archive streams must be Readable and Seekable");
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
|
||||
{
|
||||
if (!source.CanRead || !source.CanSeek)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
throw new ArchiveException(
|
||||
"Streams must be readable and seekable to use the Writing Archive API"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,10 +122,7 @@ public static class ArchiveFactory
|
||||
)
|
||||
{
|
||||
using var archive = Open(sourceArchive);
|
||||
foreach (var entry in archive.Entries)
|
||||
{
|
||||
entry.WriteToDirectory(destinationDirectory, options);
|
||||
}
|
||||
archive.WriteToDirectory(destinationDirectory, options);
|
||||
}
|
||||
|
||||
private static T FindFactory<T>(FileInfo finfo)
|
||||
@@ -189,9 +186,10 @@ public static class ArchiveFactory
|
||||
|
||||
foreach (var factory in Factory.Factories)
|
||||
{
|
||||
var isArchive = factory.IsArchive(stream);
|
||||
stream.Position = startPosition;
|
||||
|
||||
if (factory.IsArchive(stream, null))
|
||||
if (isArchive)
|
||||
{
|
||||
type = factory.KnownArchiveType;
|
||||
return true;
|
||||
@@ -239,4 +237,6 @@ public static class ArchiveFactory
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IArchiveFactory AutoFactory { get; } = new AutoArchiveFactory();
|
||||
}
|
||||
|
||||
27
src/SharpCompress/Archives/AutoArchiveFactory.cs
Normal file
27
src/SharpCompress/Archives/AutoArchiveFactory.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archives;
|
||||
|
||||
class AutoArchiveFactory : IArchiveFactory
|
||||
{
|
||||
public string Name => nameof(AutoArchiveFactory);
|
||||
|
||||
public ArchiveType? KnownArchiveType => null;
|
||||
|
||||
public IEnumerable<string> GetSupportedExtensions() => throw new NotSupportedException();
|
||||
|
||||
public bool IsArchive(Stream stream, string? password = null) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
public FileInfo? GetFilePart(int index, FileInfo part1) => throw new NotSupportedException();
|
||||
|
||||
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) =>
|
||||
ArchiveFactory.Open(stream, readerOptions);
|
||||
|
||||
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) =>
|
||||
ArchiveFactory.Open(fileInfo, readerOptions);
|
||||
}
|
||||
@@ -101,7 +101,7 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
/// </summary>
|
||||
/// <param name="sourceStream"></param>
|
||||
private GZipArchive(SourceStream sourceStream)
|
||||
: base(ArchiveType.Tar, sourceStream) { }
|
||||
: base(ArchiveType.GZip, sourceStream) { }
|
||||
|
||||
protected override IEnumerable<GZipVolume> LoadVolumes(SourceStream sourceStream)
|
||||
{
|
||||
@@ -162,7 +162,7 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
{
|
||||
if (Entries.Any())
|
||||
{
|
||||
throw new InvalidOperationException("Only one entry is allowed in a GZip Archive");
|
||||
throw new InvalidFormatException("Only one entry is allowed in a GZip Archive");
|
||||
}
|
||||
return new GZipWritableArchiveEntry(this, source, filePath, size, modified, closeStream);
|
||||
}
|
||||
@@ -176,7 +176,7 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
{
|
||||
if (Entries.Count > 1)
|
||||
{
|
||||
throw new InvalidOperationException("Only one entry is allowed in a GZip Archive");
|
||||
throw new InvalidFormatException("Only one entry is allowed in a GZip Archive");
|
||||
}
|
||||
using var writer = new GZipWriter(stream, new GZipWriterOptions(options));
|
||||
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
|
||||
|
||||
@@ -17,7 +17,7 @@ public class GZipArchiveEntry : GZipEntry, IArchiveEntry
|
||||
{
|
||||
part.GetRawStream().Position = part.EntryStartPosition;
|
||||
}
|
||||
return Parts.Single().GetCompressedStream();
|
||||
return Parts.Single().GetCompressedStream().NotNull();
|
||||
}
|
||||
|
||||
#region IArchiveEntry Members
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Archives;
|
||||
|
||||
@@ -18,10 +19,8 @@ public static class IArchiveExtensions
|
||||
ExtractionOptions? options = null
|
||||
)
|
||||
{
|
||||
foreach (var entry in archive.Entries.Where(x => !x.IsDirectory))
|
||||
{
|
||||
entry.WriteToDirectory(destinationDirectory, options);
|
||||
}
|
||||
using var reader = archive.ExtractAllEntries();
|
||||
reader.WriteAllToDirectory(destinationDirectory, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -54,14 +53,26 @@ public static class IArchiveExtensions
|
||||
var entry = entries.Entry;
|
||||
if (entry.IsDirectory)
|
||||
{
|
||||
var dirPath = Path.Combine(destination, entry.Key.NotNull("Entry Key is null"));
|
||||
if (
|
||||
Path.GetDirectoryName(dirPath + "/") is { } emptyDirectory
|
||||
&& seenDirectories.Add(dirPath)
|
||||
)
|
||||
{
|
||||
Directory.CreateDirectory(emptyDirectory);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create each directory
|
||||
// Create each directory if not already created
|
||||
var path = Path.Combine(destination, entry.Key.NotNull("Entry Key is null"));
|
||||
if (Path.GetDirectoryName(path) is { } directory && seenDirectories.Add(path))
|
||||
if (Path.GetDirectoryName(path) is { } directory)
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
if (!Directory.Exists(directory) && !seenDirectories.Contains(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
seenDirectories.Add(directory);
|
||||
}
|
||||
}
|
||||
|
||||
// Write file
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common.Rar;
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace SharpCompress.Archives.Rar;
|
||||
|
||||
public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
{
|
||||
private bool _disposed;
|
||||
internal Lazy<IRarUnpack> UnpackV2017 { get; } =
|
||||
new(() => new Compressors.Rar.UnpackV2017.Unpack());
|
||||
internal Lazy<IRarUnpack> UnpackV1 { get; } = new(() => new Compressors.Rar.UnpackV1.Unpack());
|
||||
@@ -25,6 +26,20 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
private RarArchive(SourceStream sourceStream)
|
||||
: base(ArchiveType.Rar, sourceStream) { }
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (UnpackV1.IsValueCreated && UnpackV1.Value is IDisposable unpackV1)
|
||||
{
|
||||
unpackV1.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<RarArchiveEntry> LoadEntries(IEnumerable<RarVolume> volumes) =>
|
||||
RarArchiveEntryFactory.GetEntries(this, volumes, ReaderOptions);
|
||||
|
||||
@@ -52,6 +67,16 @@ public class RarArchive : AbstractArchive<RarArchiveEntry, RarVolume>
|
||||
|
||||
protected override IReader CreateReaderForSolidExtraction()
|
||||
{
|
||||
if (this.IsMultipartVolume())
|
||||
{
|
||||
var streams = Volumes.Select(volume =>
|
||||
{
|
||||
volume.Stream.Position = 0;
|
||||
return volume.Stream;
|
||||
});
|
||||
return RarReader.Open(streams, ReaderOptions);
|
||||
}
|
||||
|
||||
var stream = Volumes.First().Stream;
|
||||
stream.Position = 0;
|
||||
return RarReader.Open(stream, ReaderOptions);
|
||||
|
||||
@@ -42,7 +42,7 @@ public class RarArchiveEntry : RarEntry, IArchiveEntry
|
||||
{
|
||||
CheckIncomplete();
|
||||
return BitConverter.ToUInt32(
|
||||
parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc,
|
||||
parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplitAfter).FileCrc.NotNull(),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ public class SevenZipArchive : AbstractArchive<SevenZipArchiveEntry, SevenZipVol
|
||||
{
|
||||
stream.Position = 0;
|
||||
var reader = new ArchiveReader();
|
||||
reader.Open(stream);
|
||||
reader.Open(stream, lookForHeader: ReaderOptions.LookForHeader);
|
||||
_database = reader.ReadDatabase(new PasswordProvider(ReaderOptions.Password));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public class TarArchiveEntry : TarEntry, IArchiveEntry
|
||||
internal TarArchiveEntry(TarArchive archive, TarFilePart? part, CompressionType compressionType)
|
||||
: base(part, compressionType) => Archive = archive;
|
||||
|
||||
public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream();
|
||||
public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream().NotNull();
|
||||
|
||||
#region IArchiveEntry Members
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ public class ZipArchiveEntry : ZipEntry, IArchiveEntry
|
||||
internal ZipArchiveEntry(ZipArchive archive, SeekableZipFilePart? part)
|
||||
: base(part) => Archive = archive;
|
||||
|
||||
public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream();
|
||||
public virtual Stream OpenEntryStream() => Parts.Single().GetCompressedStream().NotNull();
|
||||
|
||||
#region IArchiveEntry Members
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: CLSCompliant(true)]
|
||||
[assembly: InternalsVisibleTo(
|
||||
"SharpCompress.Test,PublicKey=0024000004800000940000000602000000240000525341310004000001000100158bebf1433f76dffc356733c138babea7a47536c65ed8009b16372c6f4edbb20554db74a62687f56b97c20a6ce8c4b123280279e33c894e7b3aa93ab3c573656fde4db576cfe07dba09619ead26375b25d2c4a8e43f7be257d712b0dd2eb546f67adb09281338618a58ac834fc038dd7e2740a7ab3591826252e4f4516306dc"
|
||||
)]
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
using System.Buffers;
|
||||
|
||||
namespace SharpCompress;
|
||||
|
||||
internal static class BufferPool
|
||||
{
|
||||
/// <summary>
|
||||
/// gets a buffer from the pool
|
||||
/// </summary>
|
||||
/// <param name="bufferSize">size of the buffer</param>
|
||||
/// <returns>the buffer</returns>
|
||||
public static byte[] Rent(int bufferSize)
|
||||
{
|
||||
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
|
||||
return ArrayPool<byte>.Shared.Rent(bufferSize);
|
||||
#else
|
||||
return new byte[bufferSize];
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns a buffer to the pool
|
||||
/// </summary>
|
||||
/// <param name="buffer">the buffer to return</param>
|
||||
public static void Return(byte[] buffer)
|
||||
{
|
||||
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
|
||||
ArrayPool<byte>.Shared.Return(buffer);
|
||||
#else
|
||||
// no-op
|
||||
#endif
|
||||
}
|
||||
}
|
||||
60
src/SharpCompress/Common/Arc/ArcEntry.cs
Normal file
60
src/SharpCompress/Common/Arc/ArcEntry.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.GZip;
|
||||
using SharpCompress.Common.Tar;
|
||||
|
||||
namespace SharpCompress.Common.Arc
|
||||
{
|
||||
public class ArcEntry : Entry
|
||||
{
|
||||
private readonly ArcFilePart? _filePart;
|
||||
|
||||
internal ArcEntry(ArcFilePart? filePart)
|
||||
{
|
||||
_filePart = filePart;
|
||||
}
|
||||
|
||||
public override long Crc
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_filePart == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return _filePart.Header.Crc16;
|
||||
}
|
||||
}
|
||||
|
||||
public override string? Key => _filePart?.Header.Name;
|
||||
|
||||
public override string? LinkTarget => null;
|
||||
|
||||
public override long CompressedSize => _filePart?.Header.CompressedSize ?? 0;
|
||||
|
||||
public override CompressionType CompressionType =>
|
||||
_filePart?.Header.CompressionMethod ?? CompressionType.Unknown;
|
||||
|
||||
public override long Size => throw new NotImplementedException();
|
||||
|
||||
public override DateTime? LastModifiedTime => null;
|
||||
|
||||
public override DateTime? CreatedTime => null;
|
||||
|
||||
public override DateTime? LastAccessedTime => null;
|
||||
|
||||
public override DateTime? ArchivedTime => null;
|
||||
|
||||
public override bool IsEncrypted => false;
|
||||
|
||||
public override bool IsDirectory => false;
|
||||
|
||||
public override bool IsSplitAfter => false;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => _filePart.Empty();
|
||||
}
|
||||
}
|
||||
76
src/SharpCompress/Common/Arc/ArcEntryHeader.cs
Normal file
76
src/SharpCompress/Common/Arc/ArcEntryHeader.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpCompress.Common.Arc
|
||||
{
|
||||
public class ArcEntryHeader
|
||||
{
|
||||
public ArchiveEncoding ArchiveEncoding { get; }
|
||||
public CompressionType CompressionMethod { get; private set; }
|
||||
public string? Name { get; private set; }
|
||||
public long CompressedSize { get; private set; }
|
||||
public DateTime DateTime { get; private set; }
|
||||
public int Crc16 { get; private set; }
|
||||
public long OriginalSize { get; private set; }
|
||||
public long DataStartPosition { get; private set; }
|
||||
|
||||
public ArcEntryHeader(ArchiveEncoding archiveEncoding)
|
||||
{
|
||||
this.ArchiveEncoding = archiveEncoding;
|
||||
}
|
||||
|
||||
public ArcEntryHeader? ReadHeader(Stream stream)
|
||||
{
|
||||
byte[] headerBytes = new byte[29];
|
||||
if (stream.Read(headerBytes, 0, headerBytes.Length) != headerBytes.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
DataStartPosition = stream.Position;
|
||||
return LoadFrom(headerBytes);
|
||||
}
|
||||
|
||||
public ArcEntryHeader LoadFrom(byte[] headerBytes)
|
||||
{
|
||||
CompressionMethod = GetCompressionType(headerBytes[1]);
|
||||
|
||||
// Read name
|
||||
int nameEnd = Array.IndexOf(headerBytes, (byte)0, 1); // Find null terminator
|
||||
Name = Encoding.UTF8.GetString(headerBytes, 2, nameEnd > 0 ? nameEnd - 2 : 12);
|
||||
|
||||
int offset = 15;
|
||||
CompressedSize = BitConverter.ToUInt32(headerBytes, offset);
|
||||
offset += 4;
|
||||
uint rawDateTime = BitConverter.ToUInt32(headerBytes, offset);
|
||||
DateTime = ConvertToDateTime(rawDateTime);
|
||||
offset += 4;
|
||||
Crc16 = BitConverter.ToUInt16(headerBytes, offset);
|
||||
offset += 2;
|
||||
OriginalSize = BitConverter.ToUInt32(headerBytes, offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
private CompressionType GetCompressionType(byte value)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
1 or 2 => CompressionType.None,
|
||||
3 => CompressionType.RLE90,
|
||||
4 => CompressionType.Squeezed,
|
||||
5 or 6 or 7 or 8 => CompressionType.Crunched,
|
||||
9 => CompressionType.Squashed,
|
||||
10 => CompressionType.Crushed,
|
||||
11 => CompressionType.Distilled,
|
||||
_ => CompressionType.Unknown,
|
||||
};
|
||||
}
|
||||
|
||||
public static DateTime ConvertToDateTime(long rawDateTime)
|
||||
{
|
||||
// Convert Unix timestamp to DateTime (UTC)
|
||||
return DateTimeOffset.FromUnixTimeSeconds(rawDateTime).UtcDateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
src/SharpCompress/Common/Arc/ArcFilePart.cs
Normal file
75
src/SharpCompress/Common/Arc/ArcFilePart.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common.GZip;
|
||||
using SharpCompress.Common.Tar;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.Compressors.Lzw;
|
||||
using SharpCompress.Compressors.RLE90;
|
||||
using SharpCompress.Compressors.Squeezed;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Arc
|
||||
{
|
||||
public class ArcFilePart : FilePart
|
||||
{
|
||||
private readonly Stream? _stream;
|
||||
|
||||
internal ArcFilePart(ArcEntryHeader localArcHeader, Stream? seekableStream)
|
||||
: base(localArcHeader.ArchiveEncoding)
|
||||
{
|
||||
_stream = seekableStream;
|
||||
Header = localArcHeader;
|
||||
}
|
||||
|
||||
internal ArcEntryHeader Header { get; set; }
|
||||
|
||||
internal override string? FilePartName => Header.Name;
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
if (_stream != null)
|
||||
{
|
||||
Stream compressedStream;
|
||||
switch (Header.CompressionMethod)
|
||||
{
|
||||
case CompressionType.None:
|
||||
compressedStream = new ReadOnlySubStream(
|
||||
_stream,
|
||||
Header.DataStartPosition,
|
||||
Header.CompressedSize
|
||||
);
|
||||
break;
|
||||
case CompressionType.RLE90:
|
||||
compressedStream = new RunLength90Stream(
|
||||
_stream,
|
||||
(int)Header.CompressedSize
|
||||
);
|
||||
break;
|
||||
case CompressionType.Squeezed:
|
||||
compressedStream = new SqueezeStream(_stream, (int)Header.CompressedSize);
|
||||
break;
|
||||
case CompressionType.Crunched:
|
||||
compressedStream = new ArcLzwStream(
|
||||
_stream,
|
||||
(int)Header.CompressedSize,
|
||||
true
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException(
|
||||
"CompressionMethod: " + Header.CompressionMethod
|
||||
);
|
||||
}
|
||||
return compressedStream;
|
||||
}
|
||||
return _stream.NotNull();
|
||||
}
|
||||
|
||||
internal override Stream? GetRawStream() => _stream;
|
||||
}
|
||||
}
|
||||
16
src/SharpCompress/Common/Arc/ArcVolume.cs
Normal file
16
src/SharpCompress/Common/Arc/ArcVolume.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
namespace SharpCompress.Common.Arc
|
||||
{
|
||||
public class ArcVolume : Volume
|
||||
{
|
||||
public ArcVolume(Stream stream, ReaderOptions readerOptions, int index = 0)
|
||||
: base(stream, readerOptions, index) { }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
public class ArchiveException : Exception
|
||||
{
|
||||
public ArchiveException(string message)
|
||||
: base(message) { }
|
||||
}
|
||||
@@ -6,5 +6,6 @@ public enum ArchiveType
|
||||
Zip,
|
||||
Tar,
|
||||
SevenZip,
|
||||
GZip
|
||||
GZip,
|
||||
Arc,
|
||||
}
|
||||
|
||||
@@ -16,5 +16,16 @@ public enum CompressionType
|
||||
Unknown,
|
||||
Deflate64,
|
||||
Shrink,
|
||||
Lzw
|
||||
Lzw,
|
||||
Reduce1,
|
||||
Reduce2,
|
||||
Reduce3,
|
||||
Reduce4,
|
||||
Explode,
|
||||
Squeezed,
|
||||
RLE90,
|
||||
Crunched,
|
||||
Squashed,
|
||||
Crushed,
|
||||
Distilled,
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
public class CryptographicException : Exception
|
||||
{
|
||||
public CryptographicException(string message)
|
||||
: base(message) { }
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
public class ExtractionException : Exception
|
||||
{
|
||||
public ExtractionException(string message)
|
||||
: base(message) { }
|
||||
|
||||
public ExtractionException(string message, Exception inner)
|
||||
: base(message, inner) { }
|
||||
}
|
||||
@@ -37,6 +37,7 @@ internal static class ExtractionMethods
|
||||
options ??= new ExtractionOptions() { Overwrite = true };
|
||||
|
||||
var file = Path.GetFileName(entry.Key.NotNull("Entry Key is null")).NotNull("File is null");
|
||||
file = Utility.ReplaceInvalidFileNameChars(file);
|
||||
if (options.ExtractFullPath)
|
||||
{
|
||||
var folder = Path.GetDirectoryName(entry.Key.NotNull("Entry Key is null"))
|
||||
|
||||
@@ -11,7 +11,7 @@ public abstract class FilePart
|
||||
internal abstract string? FilePartName { get; }
|
||||
public int Index { get; set; }
|
||||
|
||||
internal abstract Stream GetCompressedStream();
|
||||
internal abstract Stream? GetCompressedStream();
|
||||
internal abstract Stream? GetRawStream();
|
||||
internal bool Skipped { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
public class IncompleteArchiveException : ArchiveException
|
||||
{
|
||||
public IncompleteArchiveException(string message)
|
||||
: base(message) { }
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
public class InvalidFormatException : ExtractionException
|
||||
{
|
||||
public InvalidFormatException(string message)
|
||||
: base(message) { }
|
||||
|
||||
public InvalidFormatException(string message, Exception inner)
|
||||
: base(message, inner) { }
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
public class MultiVolumeExtractionException : ExtractionException
|
||||
{
|
||||
public MultiVolumeExtractionException(string message)
|
||||
: base(message) { }
|
||||
|
||||
public MultiVolumeExtractionException(string message, Exception inner)
|
||||
: base(message, inner) { }
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
public class MultipartStreamRequiredException : ExtractionException
|
||||
{
|
||||
public MultipartStreamRequiredException(string message)
|
||||
: base(message) { }
|
||||
}
|
||||
@@ -17,7 +17,7 @@ internal class CryptKey5 : ICryptKey
|
||||
private byte[] _pswCheck = { };
|
||||
private byte[] _hashKey = { };
|
||||
|
||||
public CryptKey5(string password, Rar5CryptoInfo rar5CryptoInfo)
|
||||
public CryptKey5(string? password, Rar5CryptoInfo rar5CryptoInfo)
|
||||
{
|
||||
_password = password ?? "";
|
||||
_cryptoInfo = rar5CryptoInfo;
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using SharpCompress.Common.Rar.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Rar.Headers;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using SharpCompress.IO;
|
||||
#if !Rar2017_64bit
|
||||
@@ -18,7 +15,7 @@ namespace SharpCompress.Common.Rar.Headers;
|
||||
|
||||
internal class FileHeader : RarHeader
|
||||
{
|
||||
private byte[] _hash;
|
||||
private byte[]? _hash;
|
||||
|
||||
public FileHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType)
|
||||
: base(header, reader, headerType) { }
|
||||
@@ -319,6 +316,10 @@ internal class FileHeader : RarHeader
|
||||
|
||||
if (NewSubHeaderType.SUBHEAD_TYPE_RR.Equals(fileNameBytes))
|
||||
{
|
||||
if (SubData is null)
|
||||
{
|
||||
throw new InvalidFormatException();
|
||||
}
|
||||
RecoverySectors =
|
||||
SubData[8]
|
||||
+ (SubData[9] << 8)
|
||||
@@ -340,12 +341,16 @@ internal class FileHeader : RarHeader
|
||||
if (RemainingHeaderBytes(reader) >= 2)
|
||||
{
|
||||
var extendedFlags = reader.ReadUInt16();
|
||||
FileLastModifiedTime = ProcessExtendedTimeV4(
|
||||
extendedFlags,
|
||||
FileLastModifiedTime,
|
||||
reader,
|
||||
0
|
||||
);
|
||||
if (FileLastModifiedTime is not null)
|
||||
{
|
||||
FileLastModifiedTime = ProcessExtendedTimeV4(
|
||||
extendedFlags,
|
||||
FileLastModifiedTime,
|
||||
reader,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
FileCreatedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 1);
|
||||
FileLastAccessedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 2);
|
||||
FileArchivedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 3);
|
||||
@@ -377,7 +382,7 @@ internal class FileHeader : RarHeader
|
||||
var dosTime = reader.ReadUInt32();
|
||||
time = Utility.DosDateToDateTime(dosTime);
|
||||
}
|
||||
if ((rmode & 4) == 0)
|
||||
if ((rmode & 4) == 0 && time is not null)
|
||||
{
|
||||
time = time.Value.AddSeconds(1);
|
||||
}
|
||||
@@ -390,7 +395,11 @@ internal class FileHeader : RarHeader
|
||||
}
|
||||
|
||||
//10^-7 to 10^-3
|
||||
return time.Value.AddMilliseconds(nanosecondHundreds * Math.Pow(10, -4));
|
||||
if (time is not null)
|
||||
{
|
||||
return time.Value.AddMilliseconds(nanosecondHundreds * Math.Pow(10, -4));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string ConvertPathV4(string path)
|
||||
@@ -406,13 +415,13 @@ internal class FileHeader : RarHeader
|
||||
return path;
|
||||
}
|
||||
|
||||
public override string ToString() => FileName;
|
||||
public override string ToString() => FileName ?? "FileHeader";
|
||||
|
||||
private ushort Flags { get; set; }
|
||||
|
||||
private bool HasFlag(ushort flag) => (Flags & flag) == flag;
|
||||
|
||||
internal byte[] FileCrc
|
||||
internal byte[]? FileCrc
|
||||
{
|
||||
get => _hash;
|
||||
private set => _hash = value;
|
||||
@@ -441,22 +450,22 @@ internal class FileHeader : RarHeader
|
||||
public bool IsRedir => RedirType != 0;
|
||||
public byte RedirFlags { get; private set; }
|
||||
public bool IsRedirDirectory => (RedirFlags & RedirFlagV5.DIRECTORY) != 0;
|
||||
public string RedirTargetName { get; private set; }
|
||||
public string? RedirTargetName { get; private set; }
|
||||
|
||||
// unused for UnpackV1 implementation (limitation)
|
||||
internal size_t WindowSize { get; private set; }
|
||||
|
||||
internal byte[] R4Salt { get; private set; }
|
||||
internal Rar5CryptoInfo Rar5CryptoInfo { get; private set; }
|
||||
internal byte[]? R4Salt { get; private set; }
|
||||
internal Rar5CryptoInfo? Rar5CryptoInfo { get; private set; }
|
||||
private byte HostOs { get; set; }
|
||||
internal uint FileAttributes { get; private set; }
|
||||
internal long CompressedSize { get; private set; }
|
||||
internal long UncompressedSize { get; private set; }
|
||||
internal string FileName { get; private set; }
|
||||
internal byte[] SubData { get; private set; }
|
||||
internal string? FileName { get; private set; }
|
||||
internal byte[]? SubData { get; private set; }
|
||||
internal int RecoverySectors { get; private set; }
|
||||
internal long DataStartPosition { get; set; }
|
||||
public Stream PackedStream { get; set; }
|
||||
public Stream? PackedStream { get; set; }
|
||||
|
||||
public bool IsSplitBefore =>
|
||||
IsRar5 ? HasHeaderFlag(HeaderFlagsV5.SPLIT_BEFORE) : HasFlag(FileFlagsV4.SPLIT_BEFORE);
|
||||
|
||||
@@ -13,7 +13,7 @@ public enum HeaderType : byte
|
||||
Sign,
|
||||
NewSub,
|
||||
EndArchive,
|
||||
Crypt
|
||||
Crypt,
|
||||
}
|
||||
|
||||
internal static class HeaderCodeV
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Rar.Headers;
|
||||
@@ -21,7 +20,7 @@ internal class RarHeader : IRarHeader
|
||||
{
|
||||
return new RarHeader(reader, isRar5, archiveEncoding);
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
catch (InvalidFormatException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common.Rar;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Readers;
|
||||
|
||||
@@ -160,10 +158,15 @@ public class RarHeaderFactory
|
||||
{
|
||||
fh.PackedStream = new RarCryptoWrapper(
|
||||
ms,
|
||||
fh.R4Salt is null ? fh.Rar5CryptoInfo.Salt : fh.R4Salt,
|
||||
fh.R4Salt is null
|
||||
? new CryptKey5(Options.Password!, fh.Rar5CryptoInfo)
|
||||
: new CryptKey3(Options.Password!)
|
||||
? fh.Rar5CryptoInfo.NotNull().Salt
|
||||
: fh.R4Salt,
|
||||
fh.R4Salt is null
|
||||
? new CryptKey5(
|
||||
Options.Password,
|
||||
fh.Rar5CryptoInfo.NotNull()
|
||||
)
|
||||
: new CryptKey3(Options.Password)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public abstract class RarEntry : Entry
|
||||
/// <summary>
|
||||
/// The File's 32 bit CRC Hash
|
||||
/// </summary>
|
||||
public override long Crc => BitConverter.ToUInt32(FileHeader.FileCrc, 0);
|
||||
public override long Crc => BitConverter.ToUInt32(FileHeader.FileCrc.NotNull(), 0);
|
||||
|
||||
/// <summary>
|
||||
/// The path of the file internal to the Rar Archive.
|
||||
@@ -68,7 +68,7 @@ public abstract class RarEntry : Entry
|
||||
|
||||
public bool IsRedir => FileHeader.IsRedir;
|
||||
|
||||
public string RedirTargetName => FileHeader.RedirTargetName;
|
||||
public string? RedirTargetName => FileHeader.RedirTargetName;
|
||||
|
||||
public override string ToString() =>
|
||||
string.Format(
|
||||
|
||||
@@ -61,9 +61,8 @@ public abstract class RarVolume : Volume
|
||||
var fh = (FileHeader)header;
|
||||
if (fh.FileName == "CMT")
|
||||
{
|
||||
var part = CreateFilePart(lastMarkHeader!, fh);
|
||||
var buffer = new byte[fh.CompressedSize];
|
||||
part.GetCompressedStream().Read(buffer, 0, buffer.Length);
|
||||
fh.PackedStream.NotNull().ReadFully(buffer);
|
||||
Comment = Encoding.UTF8.GetString(buffer, 0, buffer.Length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
public class ReaderCancelledException : Exception
|
||||
{
|
||||
public ReaderCancelledException(string message)
|
||||
: base(message) { }
|
||||
|
||||
public ReaderCancelledException(string message, Exception inner)
|
||||
: base(message, inner) { }
|
||||
}
|
||||
@@ -784,7 +784,7 @@ internal class ArchiveReader
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
throw new InvalidFormatException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -843,7 +843,7 @@ internal class ArchiveReader
|
||||
outStream.ReadExact(data, 0, data.Length);
|
||||
if (outStream.ReadByte() >= 0)
|
||||
{
|
||||
throw new InvalidOperationException("Decoded stream is longer than expected.");
|
||||
throw new InvalidFormatException("Decoded stream is longer than expected.");
|
||||
}
|
||||
dataVector.Add(data);
|
||||
|
||||
@@ -854,7 +854,7 @@ internal class ArchiveReader
|
||||
!= folder._unpackCrc
|
||||
)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
throw new InvalidFormatException(
|
||||
"Decoded stream does not match expected CRC."
|
||||
);
|
||||
}
|
||||
@@ -996,6 +996,11 @@ internal class ArchiveReader
|
||||
numFiles,
|
||||
delegate(int i, uint? attr)
|
||||
{
|
||||
// Keep the original attribute value because it could potentially get
|
||||
// modified in the logic that follows. Some callers of the library may
|
||||
// find the original value useful.
|
||||
db._files[i].ExtendedAttrib = attr;
|
||||
|
||||
// Some third party implementations established an unofficial extension
|
||||
// of the 7z archive format by placing posix file attributes in the high
|
||||
// bits of the windows file attributes. This makes use of the fact that
|
||||
@@ -1220,23 +1225,46 @@ internal class ArchiveReader
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Open(Stream stream)
|
||||
public void Open(Stream stream, bool lookForHeader)
|
||||
{
|
||||
Close();
|
||||
|
||||
_streamOrigin = stream.Position;
|
||||
_streamEnding = stream.Length;
|
||||
|
||||
// TODO: Check Signature!
|
||||
_header = new byte[0x20];
|
||||
for (var offset = 0; offset < 0x20; )
|
||||
var canScan = lookForHeader ? 0x80000 - 20 : 0;
|
||||
while (true)
|
||||
{
|
||||
var delta = stream.Read(_header, offset, 0x20 - offset);
|
||||
if (delta == 0)
|
||||
// TODO: Check Signature!
|
||||
_header = new byte[0x20];
|
||||
for (var offset = 0; offset < 0x20; )
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
var delta = stream.Read(_header, offset, 0x20 - offset);
|
||||
if (delta == 0)
|
||||
{
|
||||
throw new EndOfStreamException();
|
||||
}
|
||||
|
||||
offset += delta;
|
||||
}
|
||||
offset += delta;
|
||||
|
||||
if (
|
||||
!lookForHeader
|
||||
|| _header
|
||||
.AsSpan(0, length: 6)
|
||||
.SequenceEqual<byte>([0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C])
|
||||
)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (canScan == 0)
|
||||
{
|
||||
throw new InvalidFormatException("Unable to find 7z signature");
|
||||
}
|
||||
|
||||
canScan--;
|
||||
stream.Position = ++_streamOrigin;
|
||||
}
|
||||
|
||||
_stream = stream;
|
||||
@@ -1435,7 +1463,7 @@ internal class ArchiveReader
|
||||
#if DEBUG
|
||||
Log.WriteLine(_db._files[index].Name);
|
||||
#endif
|
||||
if (_db._files[index].CrcDefined)
|
||||
if (_db._files[index].Crc.HasValue)
|
||||
{
|
||||
_stream = new CrcCheckStream(_db._files[index].Crc.Value);
|
||||
}
|
||||
|
||||
@@ -8,18 +8,13 @@ internal class CFileItem
|
||||
{
|
||||
public long Size { get; internal set; }
|
||||
public uint? Attrib { get; internal set; }
|
||||
public uint? ExtendedAttrib { get; internal set; }
|
||||
public uint? Crc { get; internal set; }
|
||||
public string Name { get; internal set; }
|
||||
|
||||
public bool HasStream { get; internal set; }
|
||||
public bool IsDir { get; internal set; }
|
||||
|
||||
public bool CrcDefined => Crc != null;
|
||||
|
||||
public bool AttribDefined => Attrib != null;
|
||||
|
||||
public void SetAttrib(uint attrib) => Attrib = attrib;
|
||||
|
||||
public DateTime? CTime { get; internal set; }
|
||||
public DateTime? ATime { get; internal set; }
|
||||
public DateTime? MTime { get; internal set; }
|
||||
|
||||
@@ -38,5 +38,8 @@ public class SevenZipEntry : Entry
|
||||
public override int? Attrib =>
|
||||
FilePart.Header.Attrib.HasValue ? (int?)FilePart.Header.Attrib.Value : null;
|
||||
|
||||
public int? ExtendedAttrib =>
|
||||
FilePart.Header.ExtendedAttrib.HasValue ? (int?)FilePart.Header.ExtendedAttrib.Value : null;
|
||||
|
||||
internal override IEnumerable<FilePart> Parts => FilePart.AsEnumerable<FilePart>();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.IO;
|
||||
@@ -41,7 +40,7 @@ internal class SevenZipFilePart : FilePart
|
||||
{
|
||||
if (!Header.HasStream)
|
||||
{
|
||||
throw new InvalidOperationException("File does not have a stream.");
|
||||
return Stream.Null;
|
||||
}
|
||||
var folderStream = _database.GetFolderStream(_stream, Folder!, _database.PasswordProvider);
|
||||
|
||||
@@ -86,7 +85,7 @@ internal class SevenZipFilePart : FilePart
|
||||
K_LZMA or K_LZMA2 => CompressionType.LZMA,
|
||||
K_PPMD => CompressionType.PPMd,
|
||||
K_B_ZIP2 => CompressionType.BZip2,
|
||||
_ => throw new NotImplementedException()
|
||||
_ => throw new InvalidFormatException(),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
48
src/SharpCompress/Common/SharpCompressException.cs
Normal file
48
src/SharpCompress/Common/SharpCompressException.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
|
||||
namespace SharpCompress.Common;
|
||||
|
||||
public class SharpCompressException : Exception
|
||||
{
|
||||
public SharpCompressException() { }
|
||||
|
||||
public SharpCompressException(string message)
|
||||
: base(message) { }
|
||||
|
||||
public SharpCompressException(string message, Exception inner)
|
||||
: base(message, inner) { }
|
||||
}
|
||||
|
||||
public class ArchiveException(string message) : SharpCompressException(message);
|
||||
|
||||
public class IncompleteArchiveException(string message) : ArchiveException(message);
|
||||
|
||||
public class CryptographicException(string message) : SharpCompressException(message);
|
||||
|
||||
public class ReaderCancelledException(string message) : SharpCompressException(message);
|
||||
|
||||
public class ExtractionException : SharpCompressException
|
||||
{
|
||||
public ExtractionException() { }
|
||||
|
||||
public ExtractionException(string message)
|
||||
: base(message) { }
|
||||
|
||||
public ExtractionException(string message, Exception inner)
|
||||
: base(message, inner) { }
|
||||
}
|
||||
|
||||
public class MultipartStreamRequiredException(string message) : ExtractionException(message);
|
||||
|
||||
public class MultiVolumeExtractionException(string message) : ExtractionException(message);
|
||||
|
||||
public class InvalidFormatException : ExtractionException
|
||||
{
|
||||
public InvalidFormatException() { }
|
||||
|
||||
public InvalidFormatException(string message)
|
||||
: base(message) { }
|
||||
|
||||
public InvalidFormatException(string message, Exception inner)
|
||||
: base(message, inner) { }
|
||||
}
|
||||
@@ -14,5 +14,5 @@ internal enum EntryType : byte
|
||||
LongName = (byte)'L',
|
||||
SparseFile = (byte)'S',
|
||||
VolumeHeader = (byte)'V',
|
||||
GlobalExtendedHeader = (byte)'g'
|
||||
GlobalExtendedHeader = (byte)'g',
|
||||
}
|
||||
|
||||
@@ -101,57 +101,85 @@ internal sealed class TarHeader
|
||||
|
||||
internal bool Read(BinaryReader reader)
|
||||
{
|
||||
var buffer = ReadBlock(reader);
|
||||
if (buffer.Length == 0)
|
||||
string? longName = null;
|
||||
string? longLinkName = null;
|
||||
var hasLongValue = true;
|
||||
byte[] buffer;
|
||||
EntryType entryType;
|
||||
|
||||
do
|
||||
{
|
||||
buffer = ReadBlock(reader);
|
||||
|
||||
if (buffer.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
entryType = ReadEntryType(buffer);
|
||||
|
||||
// LongName and LongLink headers can follow each other and need
|
||||
// to apply to the header that follows them.
|
||||
if (entryType == EntryType.LongName)
|
||||
{
|
||||
longName = ReadLongName(reader, buffer);
|
||||
continue;
|
||||
}
|
||||
else if (entryType == EntryType.LongLink)
|
||||
{
|
||||
longLinkName = ReadLongName(reader, buffer);
|
||||
continue;
|
||||
}
|
||||
|
||||
hasLongValue = false;
|
||||
} while (hasLongValue);
|
||||
|
||||
// Check header checksum
|
||||
if (!checkChecksum(buffer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// for symlinks, additionally read the linkname
|
||||
if (ReadEntryType(buffer) == EntryType.SymLink)
|
||||
{
|
||||
LinkName = ArchiveEncoding.Decode(buffer, 157, 100).TrimNulls();
|
||||
}
|
||||
|
||||
if (ReadEntryType(buffer) == EntryType.LongName)
|
||||
{
|
||||
Name = ReadLongName(reader, buffer);
|
||||
buffer = ReadBlock(reader);
|
||||
}
|
||||
else
|
||||
{
|
||||
Name = ArchiveEncoding.Decode(buffer, 0, 100).TrimNulls();
|
||||
}
|
||||
|
||||
EntryType = ReadEntryType(buffer);
|
||||
Name = longName ?? ArchiveEncoding.Decode(buffer, 0, 100).TrimNulls();
|
||||
EntryType = entryType;
|
||||
Size = ReadSize(buffer);
|
||||
|
||||
// for symlinks, additionally read the linkname
|
||||
if (entryType == EntryType.SymLink || entryType == EntryType.HardLink)
|
||||
{
|
||||
LinkName = longLinkName ?? ArchiveEncoding.Decode(buffer, 157, 100).TrimNulls();
|
||||
}
|
||||
|
||||
Mode = ReadAsciiInt64Base8(buffer, 100, 7);
|
||||
if (EntryType == EntryType.Directory)
|
||||
|
||||
if (entryType == EntryType.Directory)
|
||||
{
|
||||
Mode |= 0b1_000_000_000;
|
||||
}
|
||||
|
||||
UserId = ReadAsciiInt64Base8oldGnu(buffer, 108, 7);
|
||||
GroupId = ReadAsciiInt64Base8oldGnu(buffer, 116, 7);
|
||||
var unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11);
|
||||
LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime();
|
||||
|
||||
var unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11);
|
||||
|
||||
LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime();
|
||||
Magic = ArchiveEncoding.Decode(buffer, 257, 6).TrimNulls();
|
||||
|
||||
if (!string.IsNullOrEmpty(Magic) && "ustar".Equals(Magic))
|
||||
{
|
||||
var namePrefix = ArchiveEncoding.Decode(buffer, 345, 157);
|
||||
namePrefix = namePrefix.TrimNulls();
|
||||
var namePrefix = ArchiveEncoding.Decode(buffer, 345, 157).TrimNulls();
|
||||
|
||||
if (!string.IsNullOrEmpty(namePrefix))
|
||||
{
|
||||
Name = namePrefix + "/" + Name;
|
||||
}
|
||||
}
|
||||
if (EntryType != EntryType.LongName && Name.Length == 0)
|
||||
|
||||
if (entryType != EntryType.LongName && Name.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -188,7 +216,7 @@ internal sealed class TarHeader
|
||||
|
||||
if (buffer.Length != 0 && buffer.Length < BLOCK_SIZE)
|
||||
{
|
||||
throw new InvalidOperationException("Buffer is invalid size");
|
||||
throw new InvalidFormatException("Buffer is invalid size");
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
@@ -286,9 +314,45 @@ internal sealed class TarHeader
|
||||
(byte)' ',
|
||||
(byte)' ',
|
||||
(byte)' ',
|
||||
(byte)' '
|
||||
(byte)' ',
|
||||
};
|
||||
|
||||
internal static bool checkChecksum(byte[] buf)
|
||||
{
|
||||
const int eightSpacesChksum = 256;
|
||||
var buffer = new Span<byte>(buf).Slice(0, 512);
|
||||
int posix_sum = eightSpacesChksum;
|
||||
int sun_sum = eightSpacesChksum;
|
||||
|
||||
foreach (byte b in buffer)
|
||||
{
|
||||
posix_sum += b;
|
||||
sun_sum += unchecked((sbyte)b);
|
||||
}
|
||||
|
||||
// Special case, empty file header
|
||||
if (posix_sum == eightSpacesChksum)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove current checksum from calculation
|
||||
foreach (byte b in buffer.Slice(148, 8))
|
||||
{
|
||||
posix_sum -= b;
|
||||
sun_sum -= unchecked((sbyte)b);
|
||||
}
|
||||
|
||||
// Read and compare checksum for header
|
||||
var crc = ReadAsciiInt64Base8(buf, 148, 7);
|
||||
if (crc != posix_sum && crc != sun_sum)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal static int RecalculateChecksum(byte[] buf)
|
||||
{
|
||||
// Set default value for checksum. That is 8 spaces.
|
||||
|
||||
@@ -13,5 +13,5 @@ internal enum HeaderFlags : ushort
|
||||
EnhancedDeflate = 16,
|
||||
|
||||
//Bit 11: Language encoding flag
|
||||
Efs = 2048
|
||||
Efs = 2048,
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ internal enum ExtraDataType : ushort
|
||||
// -Info-ZIP Unicode Path Extra Field
|
||||
UnicodePathExtraField = 0x7075,
|
||||
Zip64ExtendedInformationExtraField = 0x0001,
|
||||
UnixTimeExtraField = 0x5455
|
||||
UnixTimeExtraField = 0x5455,
|
||||
}
|
||||
|
||||
internal class ExtraData
|
||||
@@ -166,10 +166,10 @@ internal sealed class UnixTimeExtraField : ExtraData
|
||||
return Tuple.Create<DateTime?, DateTime?, DateTime?>(null, null, null);
|
||||
}
|
||||
|
||||
var flags = DataBytes[0];
|
||||
var isModifiedTimeSpecified = (flags & 0x01) == 1;
|
||||
var isLastAccessTimeSpecified = (flags & 0x02) == 1;
|
||||
var isCreationTimeSpecified = (flags & 0x04) == 1;
|
||||
var flags = (RecordedTimeFlag)DataBytes[0];
|
||||
var isModifiedTimeSpecified = flags.HasFlag(RecordedTimeFlag.LastModified);
|
||||
var isLastAccessTimeSpecified = flags.HasFlag(RecordedTimeFlag.LastAccessed);
|
||||
var isCreationTimeSpecified = flags.HasFlag(RecordedTimeFlag.Created);
|
||||
var currentIndex = 1;
|
||||
DateTime? modifiedTime = null;
|
||||
DateTime? lastAccessTime = null;
|
||||
@@ -189,7 +189,7 @@ internal sealed class UnixTimeExtraField : ExtraData
|
||||
{
|
||||
if (currentIndex + 4 > DataBytes.Length)
|
||||
{
|
||||
throw new ArchiveException("Invalid UnicodeExtraTime field");
|
||||
return Tuple.Create<DateTime?, DateTime?, DateTime?>(null, null, null);
|
||||
}
|
||||
|
||||
var lastAccessEpochTime = BinaryPrimitives.ReadInt32LittleEndian(
|
||||
@@ -206,7 +206,7 @@ internal sealed class UnixTimeExtraField : ExtraData
|
||||
{
|
||||
if (currentIndex + 4 > DataBytes.Length)
|
||||
{
|
||||
throw new ArchiveException("Invalid UnicodeExtraTime field");
|
||||
return Tuple.Create<DateTime?, DateTime?, DateTime?>(null, null, null);
|
||||
}
|
||||
|
||||
var creationTimeEpochTime = BinaryPrimitives.ReadInt32LittleEndian(
|
||||
@@ -222,6 +222,15 @@ internal sealed class UnixTimeExtraField : ExtraData
|
||||
return Tuple.Create(modifiedTime, lastAccessTime, creationTime);
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum RecordedTimeFlag
|
||||
{
|
||||
None = 0,
|
||||
LastModified = 1,
|
||||
LastAccessed = 2,
|
||||
Created = 4,
|
||||
}
|
||||
}
|
||||
|
||||
internal static class LocalEntryHeaderExtraFactory
|
||||
@@ -229,11 +238,14 @@ internal static class LocalEntryHeaderExtraFactory
|
||||
internal static ExtraData Create(ExtraDataType type, ushort length, byte[] extraData) =>
|
||||
type switch
|
||||
{
|
||||
ExtraDataType.UnicodePathExtraField
|
||||
=> new ExtraUnicodePathExtraField(type, length, extraData),
|
||||
ExtraDataType.Zip64ExtendedInformationExtraField
|
||||
=> new Zip64ExtendedInformationExtraField(type, length, extraData),
|
||||
ExtraDataType.UnicodePathExtraField => new ExtraUnicodePathExtraField(
|
||||
type,
|
||||
length,
|
||||
extraData
|
||||
),
|
||||
ExtraDataType.Zip64ExtendedInformationExtraField =>
|
||||
new Zip64ExtendedInformationExtraField(type, length, extraData),
|
||||
ExtraDataType.UnixTimeExtraField => new UnixTimeExtraField(type, length, extraData),
|
||||
_ => new ExtraData(type, length, extraData)
|
||||
_ => new ExtraData(type, length, extraData),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@ internal enum ZipHeaderType
|
||||
DirectoryEnd,
|
||||
Split,
|
||||
Zip64DirectoryEnd,
|
||||
Zip64DirectoryEndLocator
|
||||
Zip64DirectoryEndLocator,
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace SharpCompress.Common.Zip;
|
||||
internal enum CryptoMode
|
||||
{
|
||||
Encrypt,
|
||||
Decrypt
|
||||
Decrypt,
|
||||
}
|
||||
|
||||
internal class PkwareTraditionalCryptoStream : Stream
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.IO;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
|
||||
@@ -8,18 +7,13 @@ internal class SeekableZipFilePart : ZipFilePart
|
||||
{
|
||||
private bool _isLocalHeaderLoaded;
|
||||
private readonly SeekableZipHeaderFactory _headerFactory;
|
||||
private readonly DirectoryEntryHeader _directoryEntryHeader;
|
||||
|
||||
internal SeekableZipFilePart(
|
||||
SeekableZipHeaderFactory headerFactory,
|
||||
DirectoryEntryHeader header,
|
||||
Stream stream
|
||||
)
|
||||
: base(header, stream)
|
||||
{
|
||||
_headerFactory = headerFactory;
|
||||
_directoryEntryHeader = header;
|
||||
}
|
||||
: base(header, stream) => _headerFactory = headerFactory;
|
||||
|
||||
internal override Stream GetCompressedStream()
|
||||
{
|
||||
@@ -44,16 +38,6 @@ internal class SeekableZipFilePart : ZipFilePart
|
||||
{
|
||||
BaseStream.Position = Header.DataStartPosition.NotNull();
|
||||
|
||||
if (
|
||||
(Header.CompressedSize == 0)
|
||||
&& FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)
|
||||
&& _directoryEntryHeader.HasData
|
||||
&& (_directoryEntryHeader.CompressedSize != 0)
|
||||
)
|
||||
{
|
||||
return new ReadOnlySubStream(BaseStream, _directoryEntryHeader.CompressedSize);
|
||||
}
|
||||
|
||||
return BaseStream;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,12 @@ internal sealed class SeekableZipHeaderFactory : ZipHeaderFactory
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
if (FlagUtility.HasFlag(localEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor))
|
||||
{
|
||||
localEntryHeader.Crc = directoryEntryHeader.Crc;
|
||||
localEntryHeader.CompressedSize = directoryEntryHeader.CompressedSize;
|
||||
localEntryHeader.UncompressedSize = directoryEntryHeader.UncompressedSize;
|
||||
}
|
||||
return localEntryHeader;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.IO;
|
||||
|
||||
@@ -36,10 +36,7 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
uint headerBytes = 0;
|
||||
if (
|
||||
_lastEntryHeader != null
|
||||
&& (
|
||||
FlagUtility.HasFlag(_lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor)
|
||||
|| _lastEntryHeader.IsZip64
|
||||
)
|
||||
&& FlagUtility.HasFlag(_lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor)
|
||||
)
|
||||
{
|
||||
if (_lastEntryHeader.Part is null)
|
||||
@@ -49,7 +46,9 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation(
|
||||
ref rewindableStream
|
||||
);
|
||||
|
||||
var pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null;
|
||||
|
||||
var crc = reader.ReadUInt32();
|
||||
if (crc == POST_DATA_DESCRIPTOR)
|
||||
{
|
||||
@@ -82,6 +81,60 @@ internal class StreamingZipHeaderFactory : ZipHeaderFactory
|
||||
_lastEntryHeader.DataStartPosition = pos - _lastEntryHeader.CompressedSize;
|
||||
}
|
||||
}
|
||||
else if (_lastEntryHeader != null && _lastEntryHeader.IsZip64)
|
||||
{
|
||||
if (_lastEntryHeader.Part is null)
|
||||
continue;
|
||||
|
||||
reader = ((StreamingZipFilePart)_lastEntryHeader.Part).FixStreamedFileLocation(
|
||||
ref rewindableStream
|
||||
);
|
||||
|
||||
var pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null;
|
||||
|
||||
headerBytes = reader.ReadUInt32();
|
||||
|
||||
var version = reader.ReadUInt16();
|
||||
var flags = (HeaderFlags)reader.ReadUInt16();
|
||||
var compressionMethod = (ZipCompressionMethod)reader.ReadUInt16();
|
||||
var lastModifiedDate = reader.ReadUInt16();
|
||||
var lastModifiedTime = reader.ReadUInt16();
|
||||
|
||||
var crc = reader.ReadUInt32();
|
||||
|
||||
if (crc == POST_DATA_DESCRIPTOR)
|
||||
{
|
||||
crc = reader.ReadUInt32();
|
||||
}
|
||||
_lastEntryHeader.Crc = crc;
|
||||
|
||||
// The DataDescriptor can be either 64bit or 32bit
|
||||
var compressed_size = reader.ReadUInt32();
|
||||
var uncompressed_size = reader.ReadUInt32();
|
||||
|
||||
// Check if we have header or 64bit DataDescriptor
|
||||
var test_header = !(headerBytes == 0x04034b50 || headerBytes == 0x02014b50);
|
||||
|
||||
var test_64bit = ((long)uncompressed_size << 32) | compressed_size;
|
||||
if (test_64bit == _lastEntryHeader.CompressedSize && test_header)
|
||||
{
|
||||
_lastEntryHeader.UncompressedSize =
|
||||
((long)reader.ReadUInt32() << 32) | headerBytes;
|
||||
headerBytes = reader.ReadUInt32();
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastEntryHeader.UncompressedSize = uncompressed_size;
|
||||
}
|
||||
|
||||
if (pos.HasValue)
|
||||
{
|
||||
_lastEntryHeader.DataStartPosition = pos - _lastEntryHeader.CompressedSize;
|
||||
|
||||
// 4 = First 4 bytes of the entry header (i.e. 50 4B 03 04)
|
||||
rewindableStream.Position = pos.Value + 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
headerBytes = reader.ReadUInt32();
|
||||
|
||||
@@ -4,5 +4,5 @@ internal enum WinzipAesKeySize
|
||||
{
|
||||
KeySize128 = 1,
|
||||
KeySize192 = 2,
|
||||
KeySize256 = 3
|
||||
KeySize256 = 3,
|
||||
}
|
||||
|
||||
@@ -4,6 +4,11 @@ internal enum ZipCompressionMethod
|
||||
{
|
||||
None = 0,
|
||||
Shrink = 1,
|
||||
Reduce1 = 2,
|
||||
Reduce2 = 3,
|
||||
Reduce3 = 4,
|
||||
Reduce4 = 5,
|
||||
Explode = 6,
|
||||
Deflate = 8,
|
||||
Deflate64 = 9,
|
||||
BZip2 = 12,
|
||||
@@ -11,5 +16,5 @@ internal enum ZipCompressionMethod
|
||||
ZStd = 93,
|
||||
Xz = 95,
|
||||
PPMd = 98,
|
||||
WinzipAes = 0x63 //http://www.winzip.com/aes_info.htm
|
||||
WinzipAes = 0x63, //http://www.winzip.com/aes_info.htm
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
|
||||
namespace SharpCompress.Common.Zip;
|
||||
@@ -15,10 +16,19 @@ public class ZipEntry : Entry
|
||||
return;
|
||||
}
|
||||
_filePart = filePart;
|
||||
|
||||
LastModifiedTime = Utility.DosDateToDateTime(
|
||||
filePart.Header.LastModifiedDate,
|
||||
filePart.Header.LastModifiedTime
|
||||
);
|
||||
|
||||
var times =
|
||||
filePart.Header.Extra.FirstOrDefault(header =>
|
||||
header.GetType() == typeof(UnixTimeExtraField)
|
||||
) as UnixTimeExtraField;
|
||||
|
||||
LastAccessedTime = times?.UnicodeTimes.Item2;
|
||||
CreatedTime = times?.UnicodeTimes.Item3;
|
||||
}
|
||||
|
||||
public override CompressionType CompressionType =>
|
||||
@@ -31,7 +41,12 @@ public class ZipEntry : Entry
|
||||
ZipCompressionMethod.PPMd => CompressionType.PPMd,
|
||||
ZipCompressionMethod.None => CompressionType.None,
|
||||
ZipCompressionMethod.Shrink => CompressionType.Shrink,
|
||||
_ => CompressionType.Unknown
|
||||
ZipCompressionMethod.Reduce1 => CompressionType.Reduce1,
|
||||
ZipCompressionMethod.Reduce2 => CompressionType.Reduce2,
|
||||
ZipCompressionMethod.Reduce3 => CompressionType.Reduce3,
|
||||
ZipCompressionMethod.Reduce4 => CompressionType.Reduce4,
|
||||
ZipCompressionMethod.Explode => CompressionType.Explode,
|
||||
_ => CompressionType.Unknown,
|
||||
};
|
||||
|
||||
public override long Crc => _filePart?.Header.Crc ?? 0;
|
||||
@@ -46,9 +61,17 @@ public class ZipEntry : Entry
|
||||
|
||||
public override DateTime? LastModifiedTime { get; }
|
||||
|
||||
public override DateTime? CreatedTime => null;
|
||||
/// <inheritdoc/>
|
||||
/// <remarks>
|
||||
/// The returned time is UTC, not local.
|
||||
/// </remarks>
|
||||
public override DateTime? CreatedTime { get; }
|
||||
|
||||
public override DateTime? LastAccessedTime => null;
|
||||
/// <inheritdoc/>
|
||||
/// <remarks>
|
||||
/// The returned time is UTC, not local.
|
||||
/// </remarks>
|
||||
public override DateTime? LastAccessedTime { get; }
|
||||
|
||||
public override DateTime? ArchivedTime => null;
|
||||
|
||||
|
||||
@@ -7,8 +7,10 @@ using SharpCompress.Compressors;
|
||||
using SharpCompress.Compressors.BZip2;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
using SharpCompress.Compressors.Deflate64;
|
||||
using SharpCompress.Compressors.Explode;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
using SharpCompress.Compressors.PPMd;
|
||||
using SharpCompress.Compressors.Reduce;
|
||||
using SharpCompress.Compressors.Shrink;
|
||||
using SharpCompress.Compressors.Xz;
|
||||
using SharpCompress.IO;
|
||||
@@ -89,6 +91,32 @@ internal abstract class ZipFilePart : FilePart
|
||||
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);
|
||||
@@ -203,6 +231,10 @@ internal abstract class ZipFilePart : FilePart
|
||||
{
|
||||
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:
|
||||
|
||||
@@ -56,11 +56,11 @@ internal class ZipHeaderFactory
|
||||
case POST_DATA_DESCRIPTOR:
|
||||
{
|
||||
if (
|
||||
FlagUtility.HasFlag(
|
||||
_lastEntryHeader != null
|
||||
&& FlagUtility.HasFlag(
|
||||
_lastEntryHeader.NotNull().Flags,
|
||||
HeaderFlags.UsePostDataDescriptor
|
||||
)
|
||||
&& _lastEntryHeader != null
|
||||
)
|
||||
{
|
||||
_lastEntryHeader.Crc = reader.ReadUInt32();
|
||||
|
||||
199
src/SharpCompress/Compressors/ArcLzw/ArcLzwStream.cs
Normal file
199
src/SharpCompress/Compressors/ArcLzw/ArcLzwStream.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SharpCompress.Compressors.RLE90;
|
||||
using SharpCompress.Compressors.Squeezed;
|
||||
|
||||
public partial class ArcLzwStream : Stream
|
||||
{
|
||||
private Stream _stream;
|
||||
private bool _processed;
|
||||
private bool _useCrunched;
|
||||
private int _compressedSize;
|
||||
|
||||
private const int BITS = 12;
|
||||
private const int CRUNCH_BITS = 12;
|
||||
private const int SQUASH_BITS = 13;
|
||||
private const int INIT_BITS = 9;
|
||||
private const ushort FIRST = 257;
|
||||
private const ushort CLEAR = 256;
|
||||
|
||||
private ushort oldcode;
|
||||
private byte finchar;
|
||||
private int n_bits;
|
||||
private ushort maxcode;
|
||||
private ushort[] prefix = new ushort[8191];
|
||||
private byte[] suffix = new byte[8191];
|
||||
private bool clearFlag;
|
||||
private Stack<byte> stack = new Stack<byte>();
|
||||
private ushort freeEnt;
|
||||
private ushort maxcodemax;
|
||||
|
||||
public ArcLzwStream(Stream stream, int compressedSize, bool useCrunched = true)
|
||||
{
|
||||
_stream = stream;
|
||||
_useCrunched = useCrunched;
|
||||
_compressedSize = compressedSize;
|
||||
|
||||
oldcode = 0;
|
||||
finchar = 0;
|
||||
n_bits = 0;
|
||||
maxcode = 0;
|
||||
clearFlag = false;
|
||||
freeEnt = FIRST;
|
||||
maxcodemax = 0;
|
||||
}
|
||||
|
||||
private ushort? GetCode(BitReader reader)
|
||||
{
|
||||
if (clearFlag || freeEnt > maxcode)
|
||||
{
|
||||
if (freeEnt > maxcode)
|
||||
{
|
||||
n_bits++;
|
||||
maxcode = (n_bits == BITS) ? maxcodemax : (ushort)((1 << n_bits) - 1);
|
||||
}
|
||||
if (clearFlag)
|
||||
{
|
||||
clearFlag = false;
|
||||
n_bits = INIT_BITS;
|
||||
maxcode = (ushort)((1 << n_bits) - 1);
|
||||
}
|
||||
}
|
||||
return (ushort?)reader.ReadBits(n_bits);
|
||||
}
|
||||
|
||||
public List<byte> Decompress(byte[] input, bool useCrunched)
|
||||
{
|
||||
var result = new List<byte>();
|
||||
int bits = useCrunched ? CRUNCH_BITS : SQUASH_BITS;
|
||||
|
||||
if (useCrunched)
|
||||
{
|
||||
if (input[0] != BITS)
|
||||
{
|
||||
throw new InvalidDataException($"File packed with {input[0]}, expected {BITS}.");
|
||||
}
|
||||
|
||||
input = input.Skip(1).ToArray();
|
||||
}
|
||||
|
||||
maxcodemax = (ushort)(1 << bits);
|
||||
clearFlag = false;
|
||||
n_bits = INIT_BITS;
|
||||
maxcode = (ushort)((1 << n_bits) - 1);
|
||||
|
||||
for (int i = 0; i < 256; i++)
|
||||
{
|
||||
suffix[i] = (byte)i;
|
||||
}
|
||||
|
||||
var reader = new BitReader(input);
|
||||
freeEnt = FIRST;
|
||||
|
||||
if (GetCode(reader) is ushort old)
|
||||
{
|
||||
oldcode = old;
|
||||
finchar = (byte)oldcode;
|
||||
result.Add(finchar);
|
||||
}
|
||||
|
||||
while (GetCode(reader) is ushort code)
|
||||
{
|
||||
if (code == CLEAR)
|
||||
{
|
||||
Array.Clear(prefix, 0, prefix.Length);
|
||||
clearFlag = true;
|
||||
freeEnt = (ushort)(FIRST - 1);
|
||||
|
||||
if (GetCode(reader) is ushort c)
|
||||
{
|
||||
code = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ushort incode = code;
|
||||
|
||||
if (code >= freeEnt)
|
||||
{
|
||||
stack.Push(finchar);
|
||||
code = oldcode;
|
||||
}
|
||||
|
||||
while (code >= 256)
|
||||
{
|
||||
stack.Push(suffix[code]);
|
||||
code = prefix[code];
|
||||
}
|
||||
|
||||
finchar = suffix[code];
|
||||
stack.Push(finchar);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
result.Add(stack.Pop());
|
||||
}
|
||||
code = freeEnt;
|
||||
if (code < maxcodemax)
|
||||
{
|
||||
prefix[code] = oldcode;
|
||||
suffix[code] = finchar;
|
||||
freeEnt = (ushort)(code + 1);
|
||||
}
|
||||
|
||||
oldcode = incode;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Stream base class implementation
|
||||
public override bool CanRead => true;
|
||||
public override bool CanSeek => false;
|
||||
public override bool CanWrite => false;
|
||||
public override long Length => throw new NotImplementedException();
|
||||
public override long Position
|
||||
{
|
||||
get => _stream.Position;
|
||||
set => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Flush() => throw new NotImplementedException();
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (_processed)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
_processed = true;
|
||||
var data = new byte[_compressedSize];
|
||||
_stream.Read(data, 0, _compressedSize);
|
||||
var decoded = Decompress(data, _useCrunched);
|
||||
var result = decoded.Count();
|
||||
if (_useCrunched)
|
||||
{
|
||||
var unpacked = RLE.UnpackRLE(decoded.ToArray());
|
||||
unpacked.CopyTo(buffer, 0);
|
||||
result = unpacked.Count;
|
||||
}
|
||||
else
|
||||
{
|
||||
decoded.CopyTo(buffer, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public override void SetLength(long value) => throw new NotImplementedException();
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
56
src/SharpCompress/Compressors/ArcLzw/BitReader.cs
Normal file
56
src/SharpCompress/Compressors/ArcLzw/BitReader.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
|
||||
public partial class ArcLzwStream
|
||||
{
|
||||
public class BitReader
|
||||
{
|
||||
private readonly byte[] data;
|
||||
private int bitPosition;
|
||||
private int bytePosition;
|
||||
|
||||
public BitReader(byte[] inputData)
|
||||
{
|
||||
data = inputData;
|
||||
bitPosition = 0;
|
||||
bytePosition = 0;
|
||||
}
|
||||
|
||||
public int? ReadBits(int bitCount)
|
||||
{
|
||||
if (bitCount <= 0 || bitCount > 16)
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(bitCount),
|
||||
"Bit count must be between 1 and 16"
|
||||
);
|
||||
|
||||
if (bytePosition >= data.Length)
|
||||
return null;
|
||||
|
||||
int result = 0;
|
||||
int bitsRead = 0;
|
||||
|
||||
while (bitsRead < bitCount)
|
||||
{
|
||||
if (bytePosition >= data.Length)
|
||||
return null;
|
||||
|
||||
int bitsAvailable = 8 - bitPosition;
|
||||
int bitsToRead = Math.Min(bitCount - bitsRead, bitsAvailable);
|
||||
|
||||
int mask = (1 << bitsToRead) - 1;
|
||||
result |= ((data[bytePosition] >> bitPosition) & mask) << bitsRead;
|
||||
|
||||
bitPosition += bitsToRead;
|
||||
bitsRead += bitsToRead;
|
||||
|
||||
if (bitPosition >= 8)
|
||||
{
|
||||
bitPosition = 0;
|
||||
bytePosition++;
|
||||
}
|
||||
}
|
||||
|
||||
return (ushort)result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -555,6 +555,6 @@ internal class BZip2Constants
|
||||
858,
|
||||
364,
|
||||
936,
|
||||
638
|
||||
638,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable disable
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
@@ -42,14 +42,17 @@ internal class CBZip2InputStream : Stream
|
||||
private static void Cadvise()
|
||||
{
|
||||
//System.out.Println("CRC Error");
|
||||
//throw new CCoruptionError();
|
||||
throw new InvalidOperationException("BZip2 error");
|
||||
}
|
||||
|
||||
private static void BadBGLengths() => Cadvise();
|
||||
|
||||
private static void BitStreamEOF() => Cadvise();
|
||||
|
||||
private static void CompressedStreamEOF() => Cadvise();
|
||||
private static void CompressedStreamEOF()
|
||||
{
|
||||
throw new InvalidOperationException("BZip2 compressed file ends unexpectedly");
|
||||
}
|
||||
|
||||
private void MakeMaps()
|
||||
{
|
||||
@@ -539,9 +542,14 @@ internal class CBZip2InputStream : Stream
|
||||
{
|
||||
j++;
|
||||
}
|
||||
selectorMtf[i] = (char)j;
|
||||
if (i < BZip2Constants.MAX_SELECTORS)
|
||||
{
|
||||
selectorMtf[i] = (char)j;
|
||||
}
|
||||
}
|
||||
|
||||
nSelectors = Math.Min(nSelectors, BZip2Constants.MAX_SELECTORS);
|
||||
|
||||
/* Undo the MTF values for the selectors. */
|
||||
{
|
||||
var pos = new char[BZip2Constants.N_GROUPS];
|
||||
|
||||
@@ -1829,7 +1829,7 @@ internal sealed class CBZip2OutputStream : Stream
|
||||
88573,
|
||||
265720,
|
||||
797161,
|
||||
2391484
|
||||
2391484,
|
||||
};
|
||||
|
||||
private void AllocateCompressStructures()
|
||||
|
||||
@@ -288,7 +288,7 @@ internal class CRC
|
||||
unchecked((int)0xbcb4666d),
|
||||
unchecked((int)0xb8757bda),
|
||||
unchecked((int)0xb5365d03),
|
||||
unchecked((int)0xb1f740b4)
|
||||
unchecked((int)0xb1f740b4),
|
||||
};
|
||||
|
||||
public CRC() => InitialiseCRC();
|
||||
|
||||
@@ -3,5 +3,5 @@ namespace SharpCompress.Compressors;
|
||||
public enum CompressionMode
|
||||
{
|
||||
Compress = 0,
|
||||
Decompress = 1
|
||||
Decompress = 1,
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ internal sealed partial class DeflateManager
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
0
|
||||
0,
|
||||
};
|
||||
|
||||
// extra bits for each distance code
|
||||
@@ -141,7 +141,7 @@ internal sealed partial class DeflateManager
|
||||
12,
|
||||
12,
|
||||
13,
|
||||
13
|
||||
13,
|
||||
};
|
||||
|
||||
internal enum BlockState
|
||||
@@ -149,14 +149,14 @@ internal sealed partial class DeflateManager
|
||||
NeedMore = 0, // block not completed, need more input or more output
|
||||
BlockDone, // block flush performed
|
||||
FinishStarted, // finish started, need only more output at next deflate
|
||||
FinishDone // finish done, accept no more input or output
|
||||
FinishDone, // finish done, accept no more input or output
|
||||
}
|
||||
|
||||
internal enum DeflateFlavor
|
||||
{
|
||||
Store,
|
||||
Fast,
|
||||
Slow
|
||||
Slow,
|
||||
}
|
||||
|
||||
private const int MEM_LEVEL_MAX = 9;
|
||||
@@ -214,7 +214,7 @@ internal sealed partial class DeflateManager
|
||||
new Config(8, 16, 128, 128, DeflateFlavor.Slow),
|
||||
new Config(8, 32, 128, 256, DeflateFlavor.Slow),
|
||||
new Config(32, 128, 258, 1024, DeflateFlavor.Slow),
|
||||
new Config(32, 258, 258, 4096, DeflateFlavor.Slow)
|
||||
new Config(32, 258, 258, 4096, DeflateFlavor.Slow),
|
||||
};
|
||||
|
||||
private static readonly Config[] Table;
|
||||
@@ -233,7 +233,7 @@ internal sealed partial class DeflateManager
|
||||
"insufficient memory",
|
||||
"buffer error",
|
||||
"incompatible version",
|
||||
""
|
||||
"",
|
||||
};
|
||||
|
||||
// preset dictionary flag in zlib header
|
||||
@@ -1793,7 +1793,7 @@ internal sealed partial class DeflateManager
|
||||
DeflateFlavor.Store => DeflateNone,
|
||||
DeflateFlavor.Fast => DeflateFast,
|
||||
DeflateFlavor.Slow => DeflateSlow,
|
||||
_ => DeflateFunction
|
||||
_ => DeflateFunction,
|
||||
};
|
||||
|
||||
internal int SetParams(CompressionLevel level, CompressionStrategy strategy)
|
||||
|
||||
@@ -39,5 +39,5 @@ public enum FlushType
|
||||
Full,
|
||||
|
||||
/// <summary>Signals the end of the compression/decompression stream.</summary>
|
||||
Finish
|
||||
Finish,
|
||||
}
|
||||
|
||||
@@ -1615,7 +1615,7 @@ internal sealed class InfTree
|
||||
79,
|
||||
0,
|
||||
9,
|
||||
255
|
||||
255,
|
||||
};
|
||||
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'fixed_td'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
@@ -1716,7 +1716,7 @@ internal sealed class InfTree
|
||||
193,
|
||||
192,
|
||||
5,
|
||||
24577
|
||||
24577,
|
||||
};
|
||||
|
||||
// Tables for deflate from PKZIP's appnote.txt.
|
||||
@@ -1753,7 +1753,7 @@ internal sealed class InfTree
|
||||
227,
|
||||
258,
|
||||
0,
|
||||
0
|
||||
0,
|
||||
};
|
||||
|
||||
// see note #13 above about 258
|
||||
@@ -1790,7 +1790,7 @@ internal sealed class InfTree
|
||||
5,
|
||||
0,
|
||||
112,
|
||||
112
|
||||
112,
|
||||
};
|
||||
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'cpdist'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
@@ -1825,7 +1825,7 @@ internal sealed class InfTree
|
||||
8193,
|
||||
12289,
|
||||
16385,
|
||||
24577
|
||||
24577,
|
||||
};
|
||||
|
||||
//UPGRADE_NOTE: Final was removed from the declaration of 'cpdext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
|
||||
@@ -1860,7 +1860,7 @@ internal sealed class InfTree
|
||||
12,
|
||||
12,
|
||||
13,
|
||||
13
|
||||
13,
|
||||
};
|
||||
|
||||
// If BMAX needs to be larger than 16, then h and x[] should be uLong.
|
||||
|
||||
@@ -93,7 +93,7 @@ internal sealed class InflateBlocks
|
||||
2,
|
||||
14,
|
||||
1,
|
||||
15
|
||||
15,
|
||||
};
|
||||
|
||||
internal ZlibCodec _codec; // pointer back to this zlib stream
|
||||
@@ -815,7 +815,7 @@ internal sealed class InflateBlocks
|
||||
CODES = 6, // processing fixed or dynamic block
|
||||
DRY = 7, // output remaining window bytes
|
||||
DONE = 8, // finished last block, done
|
||||
BAD = 9 // ot a data error--stuck here
|
||||
BAD = 9, // ot a data error--stuck here
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -842,7 +842,7 @@ internal static class InternalInflateConstants
|
||||
0x00001fff,
|
||||
0x00003fff,
|
||||
0x00007fff,
|
||||
0x0000ffff
|
||||
0x0000ffff,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2054,7 +2054,7 @@ internal sealed class InflateManager
|
||||
CHECK2 = 10, // two check bytes to go
|
||||
CHECK1 = 11, // one check byte to go
|
||||
DONE = 12, // finished check, done
|
||||
BAD = 13 // got an error--stay here
|
||||
BAD = 13, // got an error--stay here
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -95,7 +95,7 @@ internal sealed partial class DeflateManager
|
||||
2,
|
||||
14,
|
||||
1,
|
||||
15
|
||||
15,
|
||||
};
|
||||
|
||||
// The lengths of the bit length codes are sent in order of decreasing
|
||||
@@ -618,7 +618,7 @@ internal sealed partial class DeflateManager
|
||||
29,
|
||||
29,
|
||||
29,
|
||||
29
|
||||
29,
|
||||
};
|
||||
|
||||
internal static readonly sbyte[] LengthCode =
|
||||
@@ -878,7 +878,7 @@ internal sealed partial class DeflateManager
|
||||
27,
|
||||
27,
|
||||
27,
|
||||
28
|
||||
28,
|
||||
};
|
||||
|
||||
internal static readonly int[] LengthBase =
|
||||
@@ -911,7 +911,7 @@ internal sealed partial class DeflateManager
|
||||
160,
|
||||
192,
|
||||
224,
|
||||
0
|
||||
0,
|
||||
};
|
||||
|
||||
internal static readonly int[] DistanceBase =
|
||||
@@ -945,7 +945,7 @@ internal sealed partial class DeflateManager
|
||||
8192,
|
||||
12288,
|
||||
16384,
|
||||
24576
|
||||
24576,
|
||||
};
|
||||
|
||||
internal short[] dyn_tree; // the dynamic tree
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate;
|
||||
|
||||
@@ -143,7 +143,7 @@ public enum CompressionLevel
|
||||
/// <summary>
|
||||
/// A synonym for BestCompression.
|
||||
/// </summary>
|
||||
Level9 = BestCompression
|
||||
Level9 = BestCompression,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -171,13 +171,13 @@ public enum CompressionStrategy
|
||||
/// Using <c>HuffmanOnly</c> will force the compressor to do Huffman encoding only, with no
|
||||
/// string matching.
|
||||
/// </summary>
|
||||
HuffmanOnly = 2
|
||||
HuffmanOnly = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A general purpose exception class for exceptions in the Zlib library.
|
||||
/// </summary>
|
||||
public class ZlibException : Exception
|
||||
public class ZlibException : SharpCompressException
|
||||
{
|
||||
/// <summary>
|
||||
/// The ZlibException class captures exception information generated
|
||||
@@ -859,7 +859,7 @@ internal sealed class StaticTree
|
||||
99,
|
||||
8,
|
||||
227,
|
||||
8
|
||||
8,
|
||||
};
|
||||
|
||||
internal static readonly short[] distTreeCodes =
|
||||
@@ -923,7 +923,7 @@ internal sealed class StaticTree
|
||||
7,
|
||||
5,
|
||||
23,
|
||||
5
|
||||
5,
|
||||
};
|
||||
|
||||
// extra bits for each bit length code
|
||||
@@ -947,7 +947,7 @@ internal sealed class StaticTree
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
7
|
||||
7,
|
||||
};
|
||||
|
||||
internal static readonly StaticTree Literals;
|
||||
|
||||
@@ -39,7 +39,7 @@ internal enum ZlibStreamFlavor
|
||||
{
|
||||
ZLIB = 1950,
|
||||
DEFLATE = 1951,
|
||||
GZIP = 1952
|
||||
GZIP = 1952,
|
||||
}
|
||||
|
||||
internal class ZlibBaseStream : Stream
|
||||
@@ -655,6 +655,6 @@ internal class ZlibBaseStream : Stream
|
||||
{
|
||||
Writer,
|
||||
Reader,
|
||||
Undefined
|
||||
Undefined,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
//
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -8,5 +8,5 @@ internal enum BlockType
|
||||
{
|
||||
Uncompressed = 0,
|
||||
Static = 1,
|
||||
Dynamic = 2
|
||||
Dynamic = 2,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Zip;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64;
|
||||
@@ -151,7 +152,7 @@ public sealed class Deflate64Stream : Stream
|
||||
{
|
||||
// The stream is either malicious or poorly implemented and returned a number of
|
||||
// bytes larger than the buffer supplied to it.
|
||||
throw new InvalidDataException("Deflate64: invalid data");
|
||||
throw new InvalidFormatException("Deflate64: invalid data");
|
||||
}
|
||||
|
||||
_inflater.SetInput(_buffer, 0, bytes);
|
||||
|
||||
@@ -111,7 +111,7 @@ internal static class FastEncoderStatics
|
||||
0x7e,
|
||||
0x7c,
|
||||
0x1f,
|
||||
0x3f
|
||||
0x3f,
|
||||
};
|
||||
|
||||
internal static ReadOnlySpan<byte> B_FINAL_FAST_ENCODER_TREE_STRUCTURE_DATA =>
|
||||
@@ -214,7 +214,7 @@ internal static class FastEncoderStatics
|
||||
0x7e,
|
||||
0x7c,
|
||||
0x1f,
|
||||
0x3f
|
||||
0x3f,
|
||||
};
|
||||
|
||||
// Output a currentMatch with length matchLen (>= MIN_MATCH) and displacement matchPos
|
||||
@@ -243,7 +243,6 @@ internal static class FastEncoderStatics
|
||||
// cache locality, fewer memory operations.
|
||||
//
|
||||
|
||||
|
||||
// Encoding information for literal and Length.
|
||||
// The least 5 significant bits are the length
|
||||
// and the rest is the code bits.
|
||||
@@ -762,7 +761,7 @@ internal static class FastEncoderStatics
|
||||
0x0039e7f1,
|
||||
0x003be7f1,
|
||||
0x003de7f1,
|
||||
0x000047eb
|
||||
0x000047eb,
|
||||
};
|
||||
|
||||
internal static readonly uint[] FAST_ENCODER_DISTANCE_CODE_INFO =
|
||||
@@ -798,7 +797,7 @@ internal static class FastEncoderStatics
|
||||
0x000007d5,
|
||||
0x000017d5,
|
||||
0x00000000,
|
||||
0x00000100
|
||||
0x00000100,
|
||||
};
|
||||
|
||||
internal static readonly uint[] BIT_MASK =
|
||||
@@ -818,7 +817,7 @@ internal static class FastEncoderStatics
|
||||
4095,
|
||||
8191,
|
||||
16383,
|
||||
32767
|
||||
32767,
|
||||
};
|
||||
internal static readonly byte[] EXTRA_LENGTH_BITS =
|
||||
{
|
||||
@@ -850,7 +849,7 @@ internal static class FastEncoderStatics
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
0
|
||||
0,
|
||||
};
|
||||
internal static readonly byte[] EXTRA_DISTANCE_BITS =
|
||||
{
|
||||
@@ -885,7 +884,7 @@ internal static class FastEncoderStatics
|
||||
13,
|
||||
13,
|
||||
0,
|
||||
0
|
||||
0,
|
||||
};
|
||||
internal const int NUM_CHARS = 256;
|
||||
internal const int NUM_LENGTH_BASE_CODES = 29;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64;
|
||||
|
||||
@@ -192,7 +192,7 @@ internal sealed class HuffmanTree
|
||||
var increment = 1 << len;
|
||||
if (start >= increment)
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: invalid Huffman data");
|
||||
throw new InvalidFormatException("Deflate64: invalid Huffman data");
|
||||
}
|
||||
|
||||
// Note the bits in the table are reverted.
|
||||
@@ -234,7 +234,7 @@ internal sealed class HuffmanTree
|
||||
if (value > 0)
|
||||
{
|
||||
// prevent an IndexOutOfRangeException from array[index]
|
||||
throw new InvalidDataException("Deflate64: invalid Huffman data");
|
||||
throw new InvalidFormatException("Deflate64: invalid Huffman data");
|
||||
}
|
||||
|
||||
Debug.Assert(
|
||||
@@ -307,7 +307,7 @@ internal sealed class HuffmanTree
|
||||
// huffman code lengths must be at least 1 bit long
|
||||
if (codeLength <= 0)
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: invalid Huffman data");
|
||||
throw new InvalidFormatException("Deflate64: invalid Huffman data");
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using SharpCompress.Compressors.Deflate;
|
||||
|
||||
namespace SharpCompress.Compressors.Deflate64;
|
||||
|
||||
@@ -70,7 +70,7 @@ internal sealed class InflaterManaged
|
||||
5,
|
||||
5,
|
||||
5,
|
||||
16
|
||||
16,
|
||||
};
|
||||
|
||||
// The base length for length code 257 - 285.
|
||||
@@ -105,7 +105,7 @@ internal sealed class InflaterManaged
|
||||
163,
|
||||
195,
|
||||
227,
|
||||
3
|
||||
3,
|
||||
};
|
||||
|
||||
// The base distance for distance code 0 - 31
|
||||
@@ -143,7 +143,7 @@ internal sealed class InflaterManaged
|
||||
16385,
|
||||
24577,
|
||||
32769,
|
||||
49153
|
||||
49153,
|
||||
};
|
||||
|
||||
// code lengths for code length alphabet is stored in following order
|
||||
@@ -184,7 +184,7 @@ internal sealed class InflaterManaged
|
||||
0x07,
|
||||
0x17,
|
||||
0x0f,
|
||||
0x1f
|
||||
0x1f,
|
||||
};
|
||||
|
||||
private readonly OutputWindow _output;
|
||||
@@ -385,7 +385,7 @@ internal sealed class InflaterManaged
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: unknown block type");
|
||||
throw new ZlibException("Deflate64: unknown block type");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,7 +411,7 @@ internal sealed class InflaterManaged
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: unknown block type");
|
||||
throw new ZlibException("Deflate64: unknown block type");
|
||||
}
|
||||
|
||||
//
|
||||
@@ -473,7 +473,7 @@ internal sealed class InflaterManaged
|
||||
// make sure complement matches
|
||||
if ((ushort)_blockLength != (ushort)(~blockLengthComplement))
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: invalid block length");
|
||||
throw new ZlibException("Deflate64: invalid block length");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,7 +507,7 @@ internal sealed class InflaterManaged
|
||||
default:
|
||||
Debug. /*Fail*/
|
||||
Assert(false, "check why we are here!");
|
||||
throw new InvalidDataException("Deflate64: unknown state");
|
||||
throw new ZlibException("Deflate64: unknown state");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -569,7 +569,7 @@ internal sealed class InflaterManaged
|
||||
{
|
||||
if (symbol < 0 || symbol >= S_EXTRA_LENGTH_BITS.Length)
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: invalid data");
|
||||
throw new ZlibException("Deflate64: invalid data");
|
||||
}
|
||||
_extraBits = S_EXTRA_LENGTH_BITS[symbol];
|
||||
Debug.Assert(_extraBits != 0, "We handle other cases separately!");
|
||||
@@ -591,7 +591,7 @@ internal sealed class InflaterManaged
|
||||
|
||||
if (_length < 0 || _length >= S_LENGTH_BASE.Length)
|
||||
{
|
||||
throw new InvalidDataException("Deflate64: invalid data");
|
||||
throw new ZlibException("Deflate64: invalid data");
|
||||
}
|
||||
_length = S_LENGTH_BASE[_length] + bits;
|
||||
}
|
||||
@@ -649,7 +649,7 @@ internal sealed class InflaterManaged
|
||||
default:
|
||||
Debug. /*Fail*/
|
||||
Assert(false, "check why we are here!");
|
||||
throw new InvalidDataException("Deflate64: unknown state");
|
||||
throw new ZlibException("Deflate64: unknown state");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -781,7 +781,7 @@ internal sealed class InflaterManaged
|
||||
if (_loopCounter == 0)
|
||||
{
|
||||
// can't have "prev code" on first code
|
||||
throw new InvalidDataException();
|
||||
throw new ZlibException();
|
||||
}
|
||||
|
||||
var previousCode = _codeList[_loopCounter - 1];
|
||||
@@ -789,7 +789,7 @@ internal sealed class InflaterManaged
|
||||
|
||||
if (_loopCounter + repeatCount > _codeArraySize)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
throw new ZlibException();
|
||||
}
|
||||
|
||||
for (var j = 0; j < repeatCount; j++)
|
||||
@@ -809,7 +809,7 @@ internal sealed class InflaterManaged
|
||||
|
||||
if (_loopCounter + repeatCount > _codeArraySize)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
throw new ZlibException();
|
||||
}
|
||||
|
||||
for (var j = 0; j < repeatCount; j++)
|
||||
@@ -830,7 +830,7 @@ internal sealed class InflaterManaged
|
||||
|
||||
if (_loopCounter + repeatCount > _codeArraySize)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
throw new ZlibException();
|
||||
}
|
||||
|
||||
for (var j = 0; j < repeatCount; j++)
|
||||
@@ -846,7 +846,7 @@ internal sealed class InflaterManaged
|
||||
default:
|
||||
Debug. /*Fail*/
|
||||
Assert(false, "check why we are here!");
|
||||
throw new InvalidDataException("Deflate64: unknown state");
|
||||
throw new ZlibException("Deflate64: unknown state");
|
||||
}
|
||||
|
||||
var literalTreeCodeLength = new byte[HuffmanTree.MAX_LITERAL_TREE_ELEMENTS];
|
||||
@@ -865,7 +865,7 @@ internal sealed class InflaterManaged
|
||||
// Make sure there is an end-of-block code, otherwise how could we ever end?
|
||||
if (literalTreeCodeLength[HuffmanTree.END_OF_BLOCK_CODE] == 0)
|
||||
{
|
||||
throw new InvalidDataException();
|
||||
throw new ZlibException();
|
||||
}
|
||||
|
||||
_literalLengthTree = new HuffmanTree(literalTreeCodeLength);
|
||||
|
||||
@@ -37,5 +37,5 @@ internal enum InflaterState
|
||||
ReadingFooter = 22,
|
||||
VerifyingFooter = 23,
|
||||
|
||||
Done = 24 // Finished
|
||||
Done = 24, // Finished
|
||||
}
|
||||
|
||||
@@ -8,5 +8,5 @@ internal enum MatchState
|
||||
{
|
||||
HasSymbol = 1,
|
||||
HasMatch = 2,
|
||||
HasSymbolAndMatch = 3
|
||||
HasSymbolAndMatch = 3,
|
||||
}
|
||||
|
||||
746
src/SharpCompress/Compressors/Explode/ExplodeStream.cs
Normal file
746
src/SharpCompress/Compressors/Explode/ExplodeStream.cs
Normal file
@@ -0,0 +1,746 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
|
||||
namespace SharpCompress.Compressors.Explode;
|
||||
|
||||
public class ExplodeStream : Stream
|
||||
{
|
||||
private const int INVALID_CODE = 99;
|
||||
private const int WSIZE = 64 * 1024;
|
||||
|
||||
private readonly long unCompressedSize;
|
||||
private readonly int compressedSize;
|
||||
private readonly HeaderFlags generalPurposeBitFlag;
|
||||
private readonly Stream inStream;
|
||||
|
||||
private huftNode[]? hufLiteralCodeTable; /* literal code table */
|
||||
private huftNode[] hufLengthCodeTable = []; /* length code table */
|
||||
private huftNode[] hufDistanceCodeTable = []; /* distance code table */
|
||||
|
||||
private int bitsForLiteralCodeTable;
|
||||
private int bitsForLengthCodeTable;
|
||||
private int bitsForDistanceCodeTable;
|
||||
private int numOfUncodedLowerDistanceBits; /* number of uncoded lower distance bits */
|
||||
|
||||
private ulong bitBuffer;
|
||||
private int bitBufferCount;
|
||||
|
||||
private readonly byte[] windowsBuffer;
|
||||
private uint maskForLiteralCodeTable;
|
||||
private uint maskForLengthCodeTable;
|
||||
private uint maskForDistanceCodeTable;
|
||||
private uint maskForDistanceLowBits;
|
||||
private long outBytesCount;
|
||||
|
||||
private int windowIndex;
|
||||
private int distance;
|
||||
private int length;
|
||||
|
||||
internal ExplodeStream(
|
||||
Stream inStr,
|
||||
long compressedSize,
|
||||
long uncompressedSize,
|
||||
HeaderFlags generalPurposeBitFlag
|
||||
)
|
||||
{
|
||||
inStream = inStr;
|
||||
this.compressedSize = (int)compressedSize;
|
||||
unCompressedSize = (long)uncompressedSize;
|
||||
this.generalPurposeBitFlag = generalPurposeBitFlag;
|
||||
explode_SetTables();
|
||||
|
||||
windowsBuffer = new byte[WSIZE];
|
||||
explode_var_init();
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override bool CanRead => true;
|
||||
public override bool CanSeek => false;
|
||||
public override bool CanWrite => false;
|
||||
public override long Length => unCompressedSize;
|
||||
public override long Position
|
||||
{
|
||||
get => outBytesCount;
|
||||
set { }
|
||||
}
|
||||
|
||||
static uint[] mask_bits = new uint[]
|
||||
{
|
||||
0x0000,
|
||||
0x0001,
|
||||
0x0003,
|
||||
0x0007,
|
||||
0x000f,
|
||||
0x001f,
|
||||
0x003f,
|
||||
0x007f,
|
||||
0x00ff,
|
||||
0x01ff,
|
||||
0x03ff,
|
||||
0x07ff,
|
||||
0x0fff,
|
||||
0x1fff,
|
||||
0x3fff,
|
||||
0x7fff,
|
||||
0xffff,
|
||||
};
|
||||
|
||||
/* Tables for length and distance */
|
||||
static int[] cplen2 = new int[]
|
||||
{
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
31,
|
||||
32,
|
||||
33,
|
||||
34,
|
||||
35,
|
||||
36,
|
||||
37,
|
||||
38,
|
||||
39,
|
||||
40,
|
||||
41,
|
||||
42,
|
||||
43,
|
||||
44,
|
||||
45,
|
||||
46,
|
||||
47,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
51,
|
||||
52,
|
||||
53,
|
||||
54,
|
||||
55,
|
||||
56,
|
||||
57,
|
||||
58,
|
||||
59,
|
||||
60,
|
||||
61,
|
||||
62,
|
||||
63,
|
||||
64,
|
||||
65,
|
||||
};
|
||||
|
||||
static int[] cplen3 = new int[]
|
||||
{
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
11,
|
||||
12,
|
||||
13,
|
||||
14,
|
||||
15,
|
||||
16,
|
||||
17,
|
||||
18,
|
||||
19,
|
||||
20,
|
||||
21,
|
||||
22,
|
||||
23,
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
27,
|
||||
28,
|
||||
29,
|
||||
30,
|
||||
31,
|
||||
32,
|
||||
33,
|
||||
34,
|
||||
35,
|
||||
36,
|
||||
37,
|
||||
38,
|
||||
39,
|
||||
40,
|
||||
41,
|
||||
42,
|
||||
43,
|
||||
44,
|
||||
45,
|
||||
46,
|
||||
47,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
51,
|
||||
52,
|
||||
53,
|
||||
54,
|
||||
55,
|
||||
56,
|
||||
57,
|
||||
58,
|
||||
59,
|
||||
60,
|
||||
61,
|
||||
62,
|
||||
63,
|
||||
64,
|
||||
65,
|
||||
66,
|
||||
};
|
||||
|
||||
static int[] extra = new int[]
|
||||
{
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
8,
|
||||
};
|
||||
|
||||
static int[] cpdist4 = new int[]
|
||||
{
|
||||
1,
|
||||
65,
|
||||
129,
|
||||
193,
|
||||
257,
|
||||
321,
|
||||
385,
|
||||
449,
|
||||
513,
|
||||
577,
|
||||
641,
|
||||
705,
|
||||
769,
|
||||
833,
|
||||
897,
|
||||
961,
|
||||
1025,
|
||||
1089,
|
||||
1153,
|
||||
1217,
|
||||
1281,
|
||||
1345,
|
||||
1409,
|
||||
1473,
|
||||
1537,
|
||||
1601,
|
||||
1665,
|
||||
1729,
|
||||
1793,
|
||||
1857,
|
||||
1921,
|
||||
1985,
|
||||
2049,
|
||||
2113,
|
||||
2177,
|
||||
2241,
|
||||
2305,
|
||||
2369,
|
||||
2433,
|
||||
2497,
|
||||
2561,
|
||||
2625,
|
||||
2689,
|
||||
2753,
|
||||
2817,
|
||||
2881,
|
||||
2945,
|
||||
3009,
|
||||
3073,
|
||||
3137,
|
||||
3201,
|
||||
3265,
|
||||
3329,
|
||||
3393,
|
||||
3457,
|
||||
3521,
|
||||
3585,
|
||||
3649,
|
||||
3713,
|
||||
3777,
|
||||
3841,
|
||||
3905,
|
||||
3969,
|
||||
4033,
|
||||
};
|
||||
|
||||
static int[] cpdist8 = new int[]
|
||||
{
|
||||
1,
|
||||
129,
|
||||
257,
|
||||
385,
|
||||
513,
|
||||
641,
|
||||
769,
|
||||
897,
|
||||
1025,
|
||||
1153,
|
||||
1281,
|
||||
1409,
|
||||
1537,
|
||||
1665,
|
||||
1793,
|
||||
1921,
|
||||
2049,
|
||||
2177,
|
||||
2305,
|
||||
2433,
|
||||
2561,
|
||||
2689,
|
||||
2817,
|
||||
2945,
|
||||
3073,
|
||||
3201,
|
||||
3329,
|
||||
3457,
|
||||
3585,
|
||||
3713,
|
||||
3841,
|
||||
3969,
|
||||
4097,
|
||||
4225,
|
||||
4353,
|
||||
4481,
|
||||
4609,
|
||||
4737,
|
||||
4865,
|
||||
4993,
|
||||
5121,
|
||||
5249,
|
||||
5377,
|
||||
5505,
|
||||
5633,
|
||||
5761,
|
||||
5889,
|
||||
6017,
|
||||
6145,
|
||||
6273,
|
||||
6401,
|
||||
6529,
|
||||
6657,
|
||||
6785,
|
||||
6913,
|
||||
7041,
|
||||
7169,
|
||||
7297,
|
||||
7425,
|
||||
7553,
|
||||
7681,
|
||||
7809,
|
||||
7937,
|
||||
8065,
|
||||
};
|
||||
|
||||
private int get_tree(int[] arrBitLengths, int numberExpected)
|
||||
/* Get the bit lengths for a code representation from the compressed
|
||||
stream. If get_tree() returns 4, then there is an error in the data.
|
||||
Otherwise zero is returned. */
|
||||
{
|
||||
/* get bit lengths */
|
||||
int inIndex = inStream.ReadByte() + 1; /* length/count pairs to read */
|
||||
int outIndex = 0; /* next code */
|
||||
do
|
||||
{
|
||||
int nextByte = inStream.ReadByte();
|
||||
int bitLengthOfCodes = (nextByte & 0xf) + 1; /* bits in code (1..16) */
|
||||
int numOfCodes = ((nextByte & 0xf0) >> 4) + 1; /* codes with those bits (1..16) */
|
||||
if (outIndex + numOfCodes > numberExpected)
|
||||
return 4; /* don't overflow arrBitLengths[] */
|
||||
do
|
||||
{
|
||||
arrBitLengths[outIndex++] = bitLengthOfCodes;
|
||||
} while ((--numOfCodes) != 0);
|
||||
} while ((--inIndex) != 0);
|
||||
|
||||
return outIndex != numberExpected ? 4 : 0; /* should have read numberExpected of them */
|
||||
}
|
||||
|
||||
private int explode_SetTables()
|
||||
{
|
||||
int returnCode; /* return codes */
|
||||
int[] arrBitLengthsForCodes = new int[256]; /* bit lengths for codes */
|
||||
|
||||
bitsForLiteralCodeTable = 0; /* bits for tb */
|
||||
bitsForLengthCodeTable = 7;
|
||||
bitsForDistanceCodeTable = (compressedSize) > 200000 ? 8 : 7;
|
||||
|
||||
if ((generalPurposeBitFlag & HeaderFlags.Bit2) != 0)
|
||||
/* With literal tree--minimum match length is 3 */
|
||||
{
|
||||
bitsForLiteralCodeTable = 9; /* base table size for literals */
|
||||
if ((returnCode = get_tree(arrBitLengthsForCodes, 256)) != 0)
|
||||
return returnCode;
|
||||
|
||||
if (
|
||||
(
|
||||
returnCode = HuftTree.huftbuid(
|
||||
arrBitLengthsForCodes,
|
||||
256,
|
||||
256,
|
||||
[],
|
||||
[],
|
||||
out hufLiteralCodeTable,
|
||||
ref bitsForLiteralCodeTable
|
||||
)
|
||||
) != 0
|
||||
)
|
||||
return returnCode;
|
||||
|
||||
if ((returnCode = get_tree(arrBitLengthsForCodes, 64)) != 0)
|
||||
return returnCode;
|
||||
|
||||
if (
|
||||
(
|
||||
returnCode = HuftTree.huftbuid(
|
||||
arrBitLengthsForCodes,
|
||||
64,
|
||||
0,
|
||||
cplen3,
|
||||
extra,
|
||||
out hufLengthCodeTable,
|
||||
ref bitsForLengthCodeTable
|
||||
)
|
||||
) != 0
|
||||
)
|
||||
return returnCode;
|
||||
}
|
||||
else
|
||||
/* No literal tree--minimum match length is 2 */
|
||||
{
|
||||
if ((returnCode = get_tree(arrBitLengthsForCodes, 64)) != 0)
|
||||
return returnCode;
|
||||
|
||||
hufLiteralCodeTable = null;
|
||||
|
||||
if (
|
||||
(
|
||||
returnCode = HuftTree.huftbuid(
|
||||
arrBitLengthsForCodes,
|
||||
64,
|
||||
0,
|
||||
cplen2,
|
||||
extra,
|
||||
out hufLengthCodeTable,
|
||||
ref bitsForLengthCodeTable
|
||||
)
|
||||
) != 0
|
||||
)
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
if ((returnCode = get_tree(arrBitLengthsForCodes, 64)) != 0)
|
||||
return (int)returnCode;
|
||||
|
||||
if ((generalPurposeBitFlag & HeaderFlags.Bit1) != 0) /* true if 8K */
|
||||
{
|
||||
numOfUncodedLowerDistanceBits = 7;
|
||||
returnCode = HuftTree.huftbuid(
|
||||
arrBitLengthsForCodes,
|
||||
64,
|
||||
0,
|
||||
cpdist8,
|
||||
extra,
|
||||
out hufDistanceCodeTable,
|
||||
ref bitsForDistanceCodeTable
|
||||
);
|
||||
}
|
||||
else /* else 4K */
|
||||
{
|
||||
numOfUncodedLowerDistanceBits = 6;
|
||||
returnCode = HuftTree.huftbuid(
|
||||
arrBitLengthsForCodes,
|
||||
64,
|
||||
0,
|
||||
cpdist4,
|
||||
extra,
|
||||
out hufDistanceCodeTable,
|
||||
ref bitsForDistanceCodeTable
|
||||
);
|
||||
}
|
||||
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
private void NeedBits(int numberOfBits)
|
||||
{
|
||||
while (bitBufferCount < (numberOfBits))
|
||||
{
|
||||
bitBuffer |= (uint)inStream.ReadByte() << bitBufferCount;
|
||||
bitBufferCount += 8;
|
||||
}
|
||||
}
|
||||
|
||||
private void DumpBits(int numberOfBits)
|
||||
{
|
||||
bitBuffer >>= numberOfBits;
|
||||
bitBufferCount -= numberOfBits;
|
||||
}
|
||||
|
||||
int DecodeHuft(huftNode[] htab, int bits, uint mask, out huftNode huftPointer, out int e)
|
||||
{
|
||||
NeedBits(bits);
|
||||
|
||||
int tabOffset = (int)(~bitBuffer & mask);
|
||||
huftPointer = htab[tabOffset];
|
||||
|
||||
while (true)
|
||||
{
|
||||
DumpBits(huftPointer.NumberOfBitsUsed);
|
||||
e = huftPointer.NumberOfExtraBits;
|
||||
if (e <= 32)
|
||||
break;
|
||||
if (e == INVALID_CODE)
|
||||
return 1;
|
||||
|
||||
e &= 31;
|
||||
NeedBits(e);
|
||||
|
||||
tabOffset = (int)(~bitBuffer & mask_bits[e]);
|
||||
huftPointer = huftPointer.ChildNodes[tabOffset];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void explode_var_init()
|
||||
{
|
||||
/* explode the coded data */
|
||||
bitBuffer = 0;
|
||||
bitBufferCount = 0;
|
||||
maskForLiteralCodeTable = mask_bits[bitsForLiteralCodeTable]; //only used in explode_lit
|
||||
maskForLengthCodeTable = mask_bits[bitsForLengthCodeTable];
|
||||
maskForDistanceCodeTable = mask_bits[bitsForDistanceCodeTable];
|
||||
maskForDistanceLowBits = mask_bits[numOfUncodedLowerDistanceBits];
|
||||
outBytesCount = 0;
|
||||
|
||||
windowIndex = 0; /* initialize bit buffer, window */
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
int countIndex = 0;
|
||||
while (countIndex < count && outBytesCount < unCompressedSize) /* do until unCompressedSize bytes uncompressed */
|
||||
{
|
||||
if (length == 0)
|
||||
{
|
||||
NeedBits(1);
|
||||
bool literal = (bitBuffer & 1) == 1;
|
||||
DumpBits(1);
|
||||
|
||||
huftNode huftPointer;
|
||||
if (literal) /* then literal--decode it */
|
||||
{
|
||||
byte nextByte;
|
||||
if (hufLiteralCodeTable != null)
|
||||
{
|
||||
/* get coded literal */
|
||||
if (
|
||||
DecodeHuft(
|
||||
hufLiteralCodeTable,
|
||||
bitsForLiteralCodeTable,
|
||||
maskForLiteralCodeTable,
|
||||
out huftPointer,
|
||||
out _
|
||||
) != 0
|
||||
)
|
||||
throw new Exception("Error decoding literal value");
|
||||
|
||||
nextByte = (byte)huftPointer.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
NeedBits(8);
|
||||
nextByte = (byte)bitBuffer;
|
||||
DumpBits(8);
|
||||
}
|
||||
|
||||
buffer[offset + (countIndex++)] = nextByte;
|
||||
windowsBuffer[windowIndex++] = nextByte;
|
||||
outBytesCount++;
|
||||
|
||||
if (windowIndex == WSIZE)
|
||||
windowIndex = 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
NeedBits(numOfUncodedLowerDistanceBits); /* get distance low bits */
|
||||
distance = (int)(bitBuffer & maskForDistanceLowBits);
|
||||
DumpBits(numOfUncodedLowerDistanceBits);
|
||||
|
||||
/* get coded distance high bits */
|
||||
if (
|
||||
DecodeHuft(
|
||||
hufDistanceCodeTable,
|
||||
bitsForDistanceCodeTable,
|
||||
maskForDistanceCodeTable,
|
||||
out huftPointer,
|
||||
out _
|
||||
) != 0
|
||||
)
|
||||
throw new Exception("Error decoding distance high bits");
|
||||
|
||||
distance = windowIndex - (distance + huftPointer.Value); /* construct offset */
|
||||
|
||||
/* get coded length */
|
||||
if (
|
||||
DecodeHuft(
|
||||
hufLengthCodeTable,
|
||||
bitsForLengthCodeTable,
|
||||
maskForLengthCodeTable,
|
||||
out huftPointer,
|
||||
out int extraBitLength
|
||||
) != 0
|
||||
)
|
||||
throw new Exception("Error decoding coded length");
|
||||
|
||||
length = huftPointer.Value;
|
||||
|
||||
if (extraBitLength != 0) /* get length extra bits */
|
||||
{
|
||||
NeedBits(8);
|
||||
length += (int)(bitBuffer & 0xff);
|
||||
DumpBits(8);
|
||||
}
|
||||
|
||||
if (length > (unCompressedSize - outBytesCount))
|
||||
length = (int)(unCompressedSize - outBytesCount);
|
||||
|
||||
distance &= WSIZE - 1;
|
||||
}
|
||||
|
||||
while (length != 0 && countIndex < count)
|
||||
{
|
||||
byte nextByte = windowsBuffer[distance++];
|
||||
buffer[offset + (countIndex++)] = nextByte;
|
||||
windowsBuffer[windowIndex++] = nextByte;
|
||||
outBytesCount++;
|
||||
|
||||
if (distance == WSIZE)
|
||||
distance = 0;
|
||||
|
||||
if (windowIndex == WSIZE)
|
||||
windowIndex = 0;
|
||||
|
||||
length--;
|
||||
}
|
||||
}
|
||||
|
||||
return countIndex;
|
||||
}
|
||||
}
|
||||
269
src/SharpCompress/Compressors/Explode/HuftTree.cs
Normal file
269
src/SharpCompress/Compressors/Explode/HuftTree.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* This code has been converted to C# based on the original huft_tree code found in
|
||||
* inflate.c -- by Mark Adler version c17e, 30 Mar 2007
|
||||
*/
|
||||
|
||||
namespace SharpCompress.Compressors.Explode;
|
||||
|
||||
public class huftNode
|
||||
{
|
||||
public int NumberOfExtraBits; /* number of extra bits or operation */
|
||||
public int NumberOfBitsUsed; /* number of bits in this code or subcode */
|
||||
public int Value; /* literal, length base, or distance base */
|
||||
public huftNode[] ChildNodes = []; /* next level of table */
|
||||
}
|
||||
|
||||
public static class HuftTree
|
||||
{
|
||||
private const int INVALID_CODE = 99;
|
||||
|
||||
/* If BMAX needs to be larger than 16, then h and x[] should be ulg. */
|
||||
private const int BMAX = 16; /* maximum bit length of any code (16 for explode) */
|
||||
private const int N_MAX = 288; /* maximum number of codes in any set */
|
||||
|
||||
public static int huftbuid(
|
||||
int[] arrBitLengthForCodes,
|
||||
int numberOfCodes,
|
||||
int numberOfSimpleValueCodes,
|
||||
int[] arrBaseValuesForNonSimpleCodes,
|
||||
int[] arrExtraBitsForNonSimpleCodes,
|
||||
out huftNode[] outHufTable,
|
||||
ref int outBitsForTable
|
||||
)
|
||||
/* Given a list of code lengths and a maximum table size, make a set of
|
||||
tables to decode that set of codes. Return zero on success, one if
|
||||
the given code set is incomplete (the tables are still built in this
|
||||
case), two if the input is invalid (all zero length codes or an
|
||||
oversubscribed set of lengths), and three if not enough memory.
|
||||
The code with value 256 is special, and the tables are constructed
|
||||
so that no bits beyond that code are fetched when that code is
|
||||
decoded. */
|
||||
{
|
||||
outHufTable = [];
|
||||
|
||||
/* Generate counts for each bit length */
|
||||
int lengthOfEOBcode = numberOfCodes > 256 ? arrBitLengthForCodes[256] : BMAX; /* set length of EOB code, if any */
|
||||
|
||||
int[] arrBitLengthCount = new int[BMAX + 1];
|
||||
for (int i = 0; i < BMAX + 1; i++)
|
||||
arrBitLengthCount[i] = 0;
|
||||
|
||||
int pIndex = 0;
|
||||
int counterCurrentCode = numberOfCodes;
|
||||
do
|
||||
{
|
||||
arrBitLengthCount[arrBitLengthForCodes[pIndex]]++;
|
||||
pIndex++; /* assume all entries <= BMAX */
|
||||
} while ((--counterCurrentCode) != 0);
|
||||
|
||||
if (arrBitLengthCount[0] == numberOfCodes) /* null input--all zero length codes */
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find minimum and maximum length, bound *outBitsForTable by those */
|
||||
int counter;
|
||||
for (counter = 1; counter <= BMAX; counter++)
|
||||
if (arrBitLengthCount[counter] != 0)
|
||||
break;
|
||||
|
||||
int numberOfBitsInCurrentCode = counter; /* minimum code length */
|
||||
if (outBitsForTable < counter)
|
||||
outBitsForTable = counter;
|
||||
|
||||
for (counterCurrentCode = BMAX; counterCurrentCode != 0; counterCurrentCode--)
|
||||
if (arrBitLengthCount[counterCurrentCode] != 0)
|
||||
break;
|
||||
|
||||
int maximumCodeLength = counterCurrentCode; /* maximum code length */
|
||||
if (outBitsForTable > counterCurrentCode)
|
||||
outBitsForTable = counterCurrentCode;
|
||||
|
||||
/* Adjust last length count to fill out codes, if needed */
|
||||
int numberOfDummyCodesAdded;
|
||||
for (
|
||||
numberOfDummyCodesAdded = 1 << counter;
|
||||
counter < counterCurrentCode;
|
||||
counter++, numberOfDummyCodesAdded <<= 1
|
||||
)
|
||||
if ((numberOfDummyCodesAdded -= arrBitLengthCount[counter]) < 0)
|
||||
return 2; /* bad input: more codes than bits */
|
||||
|
||||
if ((numberOfDummyCodesAdded -= arrBitLengthCount[counterCurrentCode]) < 0)
|
||||
return 2;
|
||||
|
||||
arrBitLengthCount[counterCurrentCode] += numberOfDummyCodesAdded;
|
||||
|
||||
/* Generate starting offsets into the value table for each length */
|
||||
int[] bitOffset = new int[BMAX + 1];
|
||||
bitOffset[1] = 0;
|
||||
counter = 0;
|
||||
pIndex = 1;
|
||||
int xIndex = 2;
|
||||
while ((--counterCurrentCode) != 0)
|
||||
{ /* note that i == g from above */
|
||||
bitOffset[xIndex++] = (counter += arrBitLengthCount[pIndex++]);
|
||||
}
|
||||
|
||||
/* Make a table of values in order of bit lengths */
|
||||
int[] arrValuesInOrderOfBitLength = new int[N_MAX];
|
||||
for (int i = 0; i < N_MAX; i++)
|
||||
arrValuesInOrderOfBitLength[i] = 0;
|
||||
|
||||
pIndex = 0;
|
||||
counterCurrentCode = 0;
|
||||
do
|
||||
{
|
||||
if ((counter = arrBitLengthForCodes[pIndex++]) != 0)
|
||||
arrValuesInOrderOfBitLength[bitOffset[counter]++] = counterCurrentCode;
|
||||
} while (++counterCurrentCode < numberOfCodes);
|
||||
|
||||
numberOfCodes = bitOffset[maximumCodeLength]; /* set numberOfCodes to length of v */
|
||||
|
||||
/* Generate the Huffman codes and for each, make the table entries */
|
||||
bitOffset[0] = counterCurrentCode = 0; /* first Huffman code is zero */
|
||||
pIndex = 0; /* grab values in bit order */
|
||||
int tableLevel = -1; /* no tables yet--level -1 */
|
||||
int bitsBeforeThisTable = 0;
|
||||
int[] arrLX = new int[BMAX + 1];
|
||||
int stackOfBitsPerTable = 1; /* stack of bits per table */
|
||||
arrLX[stackOfBitsPerTable - 1] = 0; /* no bits decoded yet */
|
||||
|
||||
huftNode[][] arrHufTableStack = new huftNode[BMAX][];
|
||||
huftNode[] pointerToCurrentTable = [];
|
||||
int numberOfEntriesInCurrentTable = 0;
|
||||
|
||||
bool first = true;
|
||||
|
||||
/* go through the bit lengths (k already is bits in shortest code) */
|
||||
for (; numberOfBitsInCurrentCode <= maximumCodeLength; numberOfBitsInCurrentCode++)
|
||||
{
|
||||
int counterForCodes = arrBitLengthCount[numberOfBitsInCurrentCode];
|
||||
while ((counterForCodes--) != 0)
|
||||
{
|
||||
/* here i is the Huffman code of length k bits for value *p */
|
||||
/* make tables up to required level */
|
||||
while (
|
||||
numberOfBitsInCurrentCode
|
||||
> bitsBeforeThisTable + arrLX[stackOfBitsPerTable + tableLevel]
|
||||
)
|
||||
{
|
||||
bitsBeforeThisTable += arrLX[stackOfBitsPerTable + (tableLevel++)]; /* add bits already decoded */
|
||||
|
||||
/* compute minimum size table less than or equal to *outBitsForTable bits */
|
||||
numberOfEntriesInCurrentTable =
|
||||
(numberOfEntriesInCurrentTable = maximumCodeLength - bitsBeforeThisTable)
|
||||
> outBitsForTable
|
||||
? outBitsForTable
|
||||
: numberOfEntriesInCurrentTable; /* upper limit */
|
||||
int fBitCounter1 =
|
||||
1 << (counter = numberOfBitsInCurrentCode - bitsBeforeThisTable);
|
||||
if (fBitCounter1 > counterForCodes + 1) /* try a k-w bit table */
|
||||
{ /* too few codes for k-w bit table */
|
||||
fBitCounter1 -= counterForCodes + 1; /* deduct codes from patterns left */
|
||||
xIndex = numberOfBitsInCurrentCode;
|
||||
while (++counter < numberOfEntriesInCurrentTable) /* try smaller tables up to z bits */
|
||||
{
|
||||
if ((fBitCounter1 <<= 1) <= arrBitLengthCount[++xIndex])
|
||||
break; /* enough codes to use up j bits */
|
||||
fBitCounter1 -= arrBitLengthCount[xIndex]; /* else deduct codes from patterns */
|
||||
}
|
||||
}
|
||||
if (
|
||||
bitsBeforeThisTable + counter > lengthOfEOBcode
|
||||
&& bitsBeforeThisTable < lengthOfEOBcode
|
||||
)
|
||||
counter = lengthOfEOBcode - bitsBeforeThisTable; /* make EOB code end at table */
|
||||
|
||||
numberOfEntriesInCurrentTable = 1 << counter; /* table entries for j-bit table */
|
||||
arrLX[stackOfBitsPerTable + tableLevel] = counter; /* set table size in stack */
|
||||
|
||||
/* allocate and link in new table */
|
||||
pointerToCurrentTable = new huftNode[numberOfEntriesInCurrentTable];
|
||||
|
||||
// set the pointer, pointed to by *outHufTable to the second huft in pointertoCurrentTable
|
||||
if (first)
|
||||
{
|
||||
outHufTable = pointerToCurrentTable; /* link to list for huft_free() */
|
||||
first = false;
|
||||
}
|
||||
|
||||
arrHufTableStack[tableLevel] = pointerToCurrentTable; /* table starts after link */
|
||||
|
||||
/* connect to last table, if there is one */
|
||||
if (tableLevel != 0)
|
||||
{
|
||||
bitOffset[tableLevel] = counterCurrentCode; /* save pattern for backing up */
|
||||
|
||||
huftNode vHuft = new huftNode
|
||||
{
|
||||
NumberOfBitsUsed = arrLX[stackOfBitsPerTable + tableLevel - 1], /* bits to dump before this table */
|
||||
NumberOfExtraBits = 32 + counter, /* bits in this table */
|
||||
ChildNodes = pointerToCurrentTable, /* pointer to this table */
|
||||
};
|
||||
|
||||
counter =
|
||||
(counterCurrentCode & ((1 << bitsBeforeThisTable) - 1))
|
||||
>> (bitsBeforeThisTable - arrLX[stackOfBitsPerTable + tableLevel - 1]);
|
||||
arrHufTableStack[tableLevel - 1][counter] = vHuft; /* connect to last table */
|
||||
}
|
||||
}
|
||||
|
||||
/* set up table entry in r */
|
||||
huftNode vHuft1 = new huftNode
|
||||
{
|
||||
NumberOfBitsUsed = numberOfBitsInCurrentCode - bitsBeforeThisTable,
|
||||
};
|
||||
|
||||
if (pIndex >= numberOfCodes)
|
||||
vHuft1.NumberOfExtraBits = INVALID_CODE; /* out of values--invalid code */
|
||||
else if (arrValuesInOrderOfBitLength[pIndex] < numberOfSimpleValueCodes)
|
||||
{
|
||||
vHuft1.NumberOfExtraBits = (
|
||||
arrValuesInOrderOfBitLength[pIndex] < 256 ? 32 : 31
|
||||
); /* 256 is end-of-block code */
|
||||
vHuft1.Value = arrValuesInOrderOfBitLength[pIndex++]; /* simple code is just the value */
|
||||
}
|
||||
else
|
||||
{
|
||||
vHuft1.NumberOfExtraBits = arrExtraBitsForNonSimpleCodes[
|
||||
arrValuesInOrderOfBitLength[pIndex] - numberOfSimpleValueCodes
|
||||
]; /* non-simple--look up in lists */
|
||||
vHuft1.Value = arrBaseValuesForNonSimpleCodes[
|
||||
arrValuesInOrderOfBitLength[pIndex++] - numberOfSimpleValueCodes
|
||||
];
|
||||
}
|
||||
|
||||
/* fill code-like entries with r */
|
||||
int fBitCounter2 = 1 << (numberOfBitsInCurrentCode - bitsBeforeThisTable);
|
||||
for (
|
||||
counter = counterCurrentCode >> bitsBeforeThisTable;
|
||||
counter < numberOfEntriesInCurrentTable;
|
||||
counter += fBitCounter2
|
||||
)
|
||||
pointerToCurrentTable[counter] = vHuft1;
|
||||
|
||||
/* backwards increment the k-bit code i */
|
||||
for (
|
||||
counter = 1 << (numberOfBitsInCurrentCode - 1);
|
||||
(counterCurrentCode & counter) != 0;
|
||||
counter >>= 1
|
||||
)
|
||||
counterCurrentCode ^= counter;
|
||||
counterCurrentCode ^= counter;
|
||||
|
||||
/* backup over finished tables */
|
||||
while (
|
||||
(counterCurrentCode & ((1 << bitsBeforeThisTable) - 1)) != bitOffset[tableLevel]
|
||||
)
|
||||
bitsBeforeThisTable -= arrLX[stackOfBitsPerTable + (--tableLevel)];
|
||||
}
|
||||
}
|
||||
|
||||
/* return actual size of base table */
|
||||
outBitsForTable = arrLX[stackOfBitsPerTable];
|
||||
|
||||
/* Return true (1) if we were given an incomplete table */
|
||||
return (numberOfDummyCodesAdded != 0 && maximumCodeLength != 1) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ internal class BCJFilter : Filter
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
};
|
||||
|
||||
private static readonly int[] MASK_TO_BIT_NUMBER = { 0, 1, 2, 2, 3, 3, 3, 3 };
|
||||
|
||||
@@ -39,7 +39,7 @@ internal class BCJFilterIA64 : Filter
|
||||
4,
|
||||
4,
|
||||
0,
|
||||
0
|
||||
0,
|
||||
};
|
||||
|
||||
public BCJFilterIA64(bool isEncoder, Stream baseStream)
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace SharpCompress.Compressors.Filters;
|
||||
|
||||
@@ -244,7 +244,7 @@ public sealed class BranchExecFilter
|
||||
long size = data.Length;
|
||||
if (size < 16)
|
||||
{
|
||||
throw new InvalidDataException("Unexpected data size");
|
||||
throw new InvalidFormatException("Unexpected data size");
|
||||
}
|
||||
|
||||
size -= 16;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace SharpCompress.Compressors.Filters
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.SevenZip;
|
||||
using SharpCompress.Compressors.LZMA.Utilites;
|
||||
using SharpCompress.IO;
|
||||
@@ -46,7 +47,7 @@ internal static class DecoderStreamHelper
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Could not link output stream to coder.");
|
||||
throw new InvalidFormatException("Could not link output stream to coder.");
|
||||
}
|
||||
|
||||
private static void FindPrimaryOutStreamIndex(
|
||||
@@ -75,7 +76,7 @@ internal static class DecoderStreamHelper
|
||||
{
|
||||
if (foundPrimaryOutStream)
|
||||
{
|
||||
throw new NotSupportedException("Multiple output streams.");
|
||||
throw new InvalidFormatException("Multiple output streams.");
|
||||
}
|
||||
|
||||
foundPrimaryOutStream = true;
|
||||
@@ -87,7 +88,7 @@ internal static class DecoderStreamHelper
|
||||
|
||||
if (!foundPrimaryOutStream)
|
||||
{
|
||||
throw new NotSupportedException("No output stream.");
|
||||
throw new InvalidFormatException("No output stream.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ internal enum CoderPropId
|
||||
/// <summary>
|
||||
/// Specifies mode with end marker.
|
||||
/// </summary>
|
||||
EndMarker
|
||||
EndMarker,
|
||||
}
|
||||
|
||||
internal interface ISetCoderProperties
|
||||
|
||||
@@ -85,28 +85,56 @@ internal class OutWindow
|
||||
_streamPos = _pos;
|
||||
}
|
||||
|
||||
public void CopyBlock(int distance, int len)
|
||||
public void CopyPending()
|
||||
{
|
||||
var size = len;
|
||||
var pos = _pos - distance - 1;
|
||||
if (pos < 0)
|
||||
if (_pendingLen < 1)
|
||||
{
|
||||
pos += _windowSize;
|
||||
return;
|
||||
}
|
||||
for (; size > 0 && _pos < _windowSize && _total < _limit; size--)
|
||||
var rem = _pendingLen;
|
||||
var pos = (_pendingDist < _pos ? _pos : _pos + _windowSize) - _pendingDist - 1;
|
||||
while (rem > 0 && HasSpace)
|
||||
{
|
||||
if (pos >= _windowSize)
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
_buffer[_pos++] = _buffer[pos++];
|
||||
_total++;
|
||||
PutByte(_buffer[pos++]);
|
||||
rem--;
|
||||
}
|
||||
_pendingLen = rem;
|
||||
}
|
||||
|
||||
public void CopyBlock(int distance, int len)
|
||||
{
|
||||
var rem = len;
|
||||
var pos = (distance < _pos ? _pos : _pos + _windowSize) - distance - 1;
|
||||
var targetSize = HasSpace ? (int)Math.Min(rem, _limit - _total) : 0;
|
||||
var sizeUntilWindowEnd = Math.Min(_windowSize - _pos, _windowSize - pos);
|
||||
var sizeUntilOverlap = Math.Abs(pos - _pos);
|
||||
var fastSize = Math.Min(Math.Min(sizeUntilWindowEnd, sizeUntilOverlap), targetSize);
|
||||
if (fastSize >= 2)
|
||||
{
|
||||
_buffer.AsSpan(pos, fastSize).CopyTo(_buffer.AsSpan(_pos, fastSize));
|
||||
_pos += fastSize;
|
||||
pos += fastSize;
|
||||
_total += fastSize;
|
||||
if (_pos >= _windowSize)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
rem -= fastSize;
|
||||
}
|
||||
_pendingLen = size;
|
||||
while (rem > 0 && HasSpace)
|
||||
{
|
||||
if (pos >= _windowSize)
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
PutByte(_buffer[pos++]);
|
||||
rem--;
|
||||
}
|
||||
_pendingLen = rem;
|
||||
_pendingDist = distance;
|
||||
}
|
||||
|
||||
@@ -188,12 +216,23 @@ internal class OutWindow
|
||||
return size;
|
||||
}
|
||||
|
||||
public void CopyPending()
|
||||
public int ReadByte()
|
||||
{
|
||||
if (_pendingLen > 0)
|
||||
if (_streamPos >= _pos)
|
||||
{
|
||||
CopyBlock(_pendingDist, _pendingLen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int value = _buffer[_streamPos];
|
||||
|
||||
_streamPos++;
|
||||
if (_streamPos >= _windowSize)
|
||||
{
|
||||
_pos = 0;
|
||||
_streamPos = 0;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public int AvailableBytes => _pos - _streamPos;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.IO;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Crypto;
|
||||
using SharpCompress.IO;
|
||||
|
||||
@@ -32,7 +33,7 @@ public sealed class LZipStream : Stream
|
||||
var dSize = ValidateAndReadSize(stream);
|
||||
if (dSize == 0)
|
||||
{
|
||||
throw new IOException("Not an LZip stream");
|
||||
throw new InvalidFormatException("Not an LZip stream");
|
||||
}
|
||||
var properties = GetProperties(dSize);
|
||||
_stream = new LzmaStream(properties, stream);
|
||||
@@ -167,11 +168,6 @@ public sealed class LZipStream : Stream
|
||||
/// </summary>
|
||||
public static int ValidateAndReadSize(Stream stream)
|
||||
{
|
||||
if (stream is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
|
||||
// Read the header
|
||||
Span<byte> header = stackalloc byte[6];
|
||||
var n = stream.Read(header);
|
||||
@@ -198,33 +194,25 @@ public sealed class LZipStream : Stream
|
||||
return (1 << basePower) - (subtractionNumerator * (1 << (basePower - 4)));
|
||||
}
|
||||
|
||||
private static readonly byte[] headerBytes = new byte[6]
|
||||
{
|
||||
private static readonly byte[] headerBytes =
|
||||
[
|
||||
(byte)'L',
|
||||
(byte)'Z',
|
||||
(byte)'I',
|
||||
(byte)'P',
|
||||
1,
|
||||
113
|
||||
};
|
||||
|
||||
public static void WriteHeaderSize(Stream stream)
|
||||
{
|
||||
if (stream is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(stream));
|
||||
}
|
||||
113,
|
||||
];
|
||||
|
||||
public static void WriteHeaderSize(Stream stream) =>
|
||||
// hard coding the dictionary size encoding
|
||||
stream.Write(headerBytes, 0, 6);
|
||||
}
|
||||
|
||||
/// <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
|
||||
@@ -235,6 +223,6 @@ public sealed class LZipStream : Stream
|
||||
(byte)(dictionarySize & 0xff),
|
||||
(byte)((dictionarySize >> 8) & 0xff),
|
||||
(byte)((dictionarySize >> 16) & 0xff),
|
||||
(byte)((dictionarySize >> 24) & 0xff)
|
||||
};
|
||||
(byte)((dictionarySize >> 24) & 0xff),
|
||||
];
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user