From 42d9dfd117ee72f113ccf2438badeb487aed1cbe Mon Sep 17 00:00:00 2001 From: twirpx Date: Wed, 16 Aug 2017 12:19:23 +0500 Subject: [PATCH 001/124] Fixed bug: Passing default ReaderOptions when creating ZipReader for solid extraction (#287) --- src/SharpCompress/Archives/Zip/ZipArchive.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.cs b/src/SharpCompress/Archives/Zip/ZipArchive.cs index 49cb69ec..f0889668 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchive.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchive.cs @@ -208,7 +208,7 @@ namespace SharpCompress.Archives.Zip { var stream = Volumes.Single().Stream; stream.Position = 0; - return ZipReader.Open(stream); + return ZipReader.Open(stream, ReaderOptions); } } } \ No newline at end of file From 0f511c4b2a82eec147ff049d17151b0325dd4d64 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Thu, 17 Aug 2017 11:43:34 +0100 Subject: [PATCH 002/124] Mark for 0.18.1 --- src/SharpCompress/SharpCompress.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index ebcf43a8..7ed7ffa3 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -2,9 +2,9 @@ SharpCompress - Pure C# Decompression/Compression en-US - 0.18 - 0.18.0.0 - 0.18.0.0 + 0.18.1 + 0.18.1.0 + 0.18.1.0 Adam Hathcock net45;net35;netstandard1.0;netstandard1.3 $(LibraryFrameworks) From 08fee76b4eff2bc82985dd7cd0abe0d10102c39c Mon Sep 17 00:00:00 2001 From: Vladimir Kozlov Date: Fri, 8 Sep 2017 19:25:53 +0700 Subject: [PATCH 003/124] Fixes Double Dispose() of ZipWritingStream #294 https://github.com/adamhathcock/sharpcompress/issues/294 (#295) --- src/SharpCompress/Writers/Zip/ZipWriter.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index 1f806d53..3a29ac63 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -276,6 +276,7 @@ namespace SharpCompress.Writers.Zip // Flag to prevent throwing exceptions on Dispose private bool limitsExceeded; + private bool isDisposed; internal ZipWritingStream(ZipWriter writer, Stream originalStream, ZipCentralDirectoryEntry entry, ZipCompressionMethod zipCompressionMethod, CompressionLevel compressionLevel) @@ -344,6 +345,13 @@ namespace SharpCompress.Writers.Zip protected override void Dispose(bool disposing) { + if (isDisposed) + { + return; + } + + isDisposed = true; + base.Dispose(disposing); if (disposing) { From f20c03180e3270697c091f366899e0eb7a280795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Sun, 17 Sep 2017 23:05:20 +0200 Subject: [PATCH 004/124] XZ: fix variable-length integers decoding (#299) --- src/SharpCompress/Compressors/Xz/MultiByteIntegers.cs | 2 +- tests/SharpCompress.Test/Xz/XZIndexTests.cs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/SharpCompress/Compressors/Xz/MultiByteIntegers.cs b/src/SharpCompress/Compressors/Xz/MultiByteIntegers.cs index 27c9a6fd..24795128 100644 --- a/src/SharpCompress/Compressors/Xz/MultiByteIntegers.cs +++ b/src/SharpCompress/Compressors/Xz/MultiByteIntegers.cs @@ -18,7 +18,7 @@ namespace SharpCompress.Compressors.Xz int i = 0; while ((LastByte & 0x80) != 0) { - if (i >= MaxBytes) + if (++i >= MaxBytes) throw new InvalidDataException(); LastByte = reader.ReadByte(); if (LastByte == 0) diff --git a/tests/SharpCompress.Test/Xz/XZIndexTests.cs b/tests/SharpCompress.Test/Xz/XZIndexTests.cs index c9976e17..b26c8da0 100644 --- a/tests/SharpCompress.Test/Xz/XZIndexTests.cs +++ b/tests/SharpCompress.Test/Xz/XZIndexTests.cs @@ -41,5 +41,14 @@ namespace SharpCompress.Test.Xz index.Process(); Assert.Equal(index.NumberOfRecords, (ulong)1); } + + [Fact] + public void ReadsFirstRecord() + { + BinaryReader br = new BinaryReader(CompressedStream); + var index = new XZIndex(br, false); + index.Process(); + Assert.Equal((ulong)OriginalBytes.Length, index.Records[0].UncompressedSize); + } } } From 3d91b4eb5e78536df2eb2ef886bc3e900b210113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Sun, 17 Sep 2017 23:14:23 +0200 Subject: [PATCH 005/124] XZ: fix padding issues (#300) * XZ: fix variable-length integers decoding * XZ: fix block and index padding issues * cleanup in XZStreamTests --- src/SharpCompress/Compressors/Xz/XZBlock.cs | 8 +-- src/SharpCompress/Compressors/Xz/XZIndex.cs | 6 +- tests/SharpCompress.Test/Xz/XZBlockTests.cs | 25 +++++++ tests/SharpCompress.Test/Xz/XZIndexTests.cs | 54 ++++++++++++++- tests/SharpCompress.Test/Xz/XZStreamTests.cs | 26 ++++++- tests/SharpCompress.Test/Xz/XZTestsBase.cs | 72 ++++++++++++++++++++ 6 files changed, 180 insertions(+), 11 deletions(-) diff --git a/src/SharpCompress/Compressors/Xz/XZBlock.cs b/src/SharpCompress/Compressors/Xz/XZBlock.cs index 118bdc54..0848618b 100644 --- a/src/SharpCompress/Compressors/Xz/XZBlock.cs +++ b/src/SharpCompress/Compressors/Xz/XZBlock.cs @@ -50,11 +50,11 @@ namespace SharpCompress.Compressors.Xz private void SkipPadding() { - int padding = (int)(_bytesRead % 4); - if (padding > 0) + int bytes = (int)(BaseStream.Position % 4); + if (bytes > 0) { - byte[] paddingBytes = new byte[padding]; - BaseStream.Read(paddingBytes, 0, padding); + byte[] paddingBytes = new byte[4 - bytes]; + BaseStream.Read(paddingBytes, 0, paddingBytes.Length); if (paddingBytes.Any(b => b != 0)) throw new InvalidDataException("Padding bytes were non-null"); } diff --git a/src/SharpCompress/Compressors/Xz/XZIndex.cs b/src/SharpCompress/Compressors/Xz/XZIndex.cs index 5e223284..f1a66776 100644 --- a/src/SharpCompress/Compressors/Xz/XZIndex.cs +++ b/src/SharpCompress/Compressors/Xz/XZIndex.cs @@ -55,10 +55,10 @@ namespace SharpCompress.Compressors.Xz private void SkipPadding() { - int padding = (int)(_reader.BaseStream.Position - StreamStartPosition) % 4; - if (padding > 0) + int bytes = (int)(_reader.BaseStream.Position - StreamStartPosition) % 4; + if (bytes > 0) { - byte[] paddingBytes = _reader.ReadBytes(padding); + byte[] paddingBytes = _reader.ReadBytes(4 - bytes); if (paddingBytes.Any(b => b != 0)) throw new InvalidDataException("Padding bytes were non-null"); } diff --git a/tests/SharpCompress.Test/Xz/XZBlockTests.cs b/tests/SharpCompress.Test/Xz/XZBlockTests.cs index 00f185c6..7a528b44 100644 --- a/tests/SharpCompress.Test/Xz/XZBlockTests.cs +++ b/tests/SharpCompress.Test/Xz/XZBlockTests.cs @@ -12,6 +12,11 @@ namespace SharpCompress.Test.Xz stream.Position = 12; } + protected override void RewindIndexed(Stream stream) + { + stream.Position = 12; + } + private byte[] ReadBytes(XZBlock block, int bytesToRead) { byte[] buffer = new byte[bytesToRead]; @@ -69,5 +74,25 @@ namespace SharpCompress.Test.Xz var sr = new StreamReader(XZBlock); Assert.Equal(sr.ReadToEnd(), Original); } + + [Fact] + public void NoopWhenNoPadding() + { + // CompressedStream's only block has no padding. + var XZBlock = new XZBlock(CompressedStream, CheckType.CRC64, 8); + var sr = new StreamReader(XZBlock); + sr.ReadToEnd(); + Assert.Equal(0L, CompressedStream.Position % 4L); + } + + [Fact] + public void SkipsPaddingWhenPresent() + { + // CompressedIndexedStream's first block has 1-byte padding. + var XZBlock = new XZBlock(CompressedIndexedStream, CheckType.CRC64, 8); + var sr = new StreamReader(XZBlock); + sr.ReadToEnd(); + Assert.Equal(0L, CompressedIndexedStream.Position % 4L); + } } } diff --git a/tests/SharpCompress.Test/Xz/XZIndexTests.cs b/tests/SharpCompress.Test/Xz/XZIndexTests.cs index b26c8da0..ff8b3b0b 100644 --- a/tests/SharpCompress.Test/Xz/XZIndexTests.cs +++ b/tests/SharpCompress.Test/Xz/XZIndexTests.cs @@ -6,11 +6,21 @@ namespace SharpCompress.Test.Xz { public class XZIndexTests : XZTestsBase { + protected override void RewindEmpty(Stream stream) + { + stream.Position = 12; + } + protected override void Rewind(Stream stream) { stream.Position = 356; } + protected override void RewindIndexed(Stream stream) + { + stream.Position = 612; + } + [Fact] public void RecordsStreamStartOnInit() { @@ -34,12 +44,52 @@ namespace SharpCompress.Test.Xz } [Fact] - public void ReadsNumberOfRecords() + public void ReadsNoRecord() + { + BinaryReader br = new BinaryReader(CompressedEmptyStream); + var index = new XZIndex(br, false); + index.Process(); + Assert.Equal((ulong)0, index.NumberOfRecords); + } + + [Fact] + public void ReadsOneRecord() { BinaryReader br = new BinaryReader(CompressedStream); var index = new XZIndex(br, false); index.Process(); - Assert.Equal(index.NumberOfRecords, (ulong)1); + Assert.Equal((ulong)1, index.NumberOfRecords); + } + + [Fact] + public void ReadsMultipleRecords() + { + BinaryReader br = new BinaryReader(CompressedIndexedStream); + var index = new XZIndex(br, false); + index.Process(); + Assert.Equal((ulong)2, index.NumberOfRecords); + } + + [Fact] + public void ReadsFirstRecord() + { + BinaryReader br = new BinaryReader(CompressedStream); + var index = new XZIndex(br, false); + index.Process(); + Assert.Equal((ulong)OriginalBytes.Length, index.Records[0].UncompressedSize); + } + + [Fact] + public void SkipsPadding() + { + // Index with 3-byte padding. + using (Stream badStream = new MemoryStream(new byte[] { 0x00, 0x01, 0x10, 0x80, 0x01, 0x00, 0x00, 0x00, 0xB1, 0x01, 0xD9, 0xC9, 0xFF })) + { + BinaryReader br = new BinaryReader(badStream); + var index = new XZIndex(br, false); + index.Process(); + Assert.Equal(0L, badStream.Position % 4L); + } } [Fact] diff --git a/tests/SharpCompress.Test/Xz/XZStreamTests.cs b/tests/SharpCompress.Test/Xz/XZStreamTests.cs index 14b99dd0..4b6bbaf6 100644 --- a/tests/SharpCompress.Test/Xz/XZStreamTests.cs +++ b/tests/SharpCompress.Test/Xz/XZStreamTests.cs @@ -4,8 +4,19 @@ using Xunit; namespace SharpCompress.Test.Xz { - public class XZStreamReaderTests : XZTestsBase + public class XZStreamTests : XZTestsBase { + [Fact] + public void CanReadEmptyStream() + { + XZStream xz = new XZStream(CompressedEmptyStream); + using (var sr = new StreamReader(xz)) + { + string uncompressed = sr.ReadToEnd(); + Assert.Equal(OriginalEmpty, uncompressed); + } + } + [Fact] public void CanReadStream() { @@ -13,7 +24,18 @@ namespace SharpCompress.Test.Xz using (var sr = new StreamReader(xz)) { string uncompressed = sr.ReadToEnd(); - Assert.Equal(uncompressed, Original); + Assert.Equal(Original, uncompressed); + } + } + + [Fact] + public void CanReadIndexedStream() + { + XZStream xz = new XZStream(CompressedIndexedStream); + using (var sr = new StreamReader(xz)) + { + string uncompressed = sr.ReadToEnd(); + Assert.Equal(OriginalIndexed, uncompressed); } } } diff --git a/tests/SharpCompress.Test/Xz/XZTestsBase.cs b/tests/SharpCompress.Test/Xz/XZTestsBase.cs index 8edd15a5..a01657c5 100644 --- a/tests/SharpCompress.Test/Xz/XZTestsBase.cs +++ b/tests/SharpCompress.Test/Xz/XZTestsBase.cs @@ -7,7 +7,14 @@ namespace SharpCompress.Test.Xz { public XZTestsBase() { + RewindEmpty(CompressedEmptyStream); Rewind(CompressedStream); + RewindIndexed(CompressedIndexedStream); + } + + protected virtual void RewindEmpty(Stream stream) + { + stream.Position = 0; } protected virtual void Rewind(Stream stream) @@ -15,6 +22,22 @@ namespace SharpCompress.Test.Xz stream.Position = 0; } + protected virtual void RewindIndexed(Stream stream) + { + stream.Position = 0; + } + + protected Stream CompressedEmptyStream { get; } = new MemoryStream(CompressedEmpty); + + protected static byte[] CompressedEmpty { get; } = new byte[] { + 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x01, 0x69, 0x22, 0xde, 0x36, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0xdf, 0x44, 0x21, 0x90, 0x42, 0x99, 0x0d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x59, 0x5a + }; + + protected static byte[] OriginalEmptyBytes => Encoding.ASCII.GetBytes(OriginalEmpty); + + protected static string OriginalEmpty { get; } = string.Empty; + protected Stream CompressedStream { get; } = new MemoryStream(Compressed); protected static byte[] Compressed { get; } = new byte[] { @@ -65,5 +88,54 @@ namespace SharpCompress.Test.Xz "The eager children cry.\r\n" + "\"Why, Mary loves the lamb, you know.\"\r\n" + "The teacher did reply."; + + protected Stream CompressedIndexedStream { get; } = new MemoryStream(CompressedIndexed); + + protected static byte[] CompressedIndexed { get; } = new byte[] { + 0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00, 0x00, 0x01, 0x69, 0x22, 0xde, 0x36, 0x03, 0xc0, 0xe3, 0x02, + 0x80, 0x20, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xe5, 0xd7, 0x32, 0xe0, 0x0f, 0xff, 0x01, + 0x5b, 0x5d, 0x00, 0x26, 0x98, 0x4a, 0x47, 0xc6, 0x6a, 0x27, 0xd7, 0x36, 0x7a, 0x05, 0xb9, 0x4f, + 0xd7, 0xde, 0x3a, 0x0e, 0xee, 0x1b, 0xd7, 0x81, 0xe9, 0xf5, 0x90, 0x1e, 0xd5, 0x9e, 0x88, 0x32, + 0x1c, 0x7b, 0x43, 0x84, 0x02, 0x58, 0x92, 0xcf, 0x97, 0xfc, 0xae, 0x01, 0x83, 0x23, 0x48, 0x93, + 0xc6, 0x56, 0xcc, 0x6d, 0xb1, 0x23, 0x10, 0x24, 0x3b, 0x9e, 0x06, 0xaa, 0xc0, 0xce, 0x86, 0x0a, + 0xb7, 0x9f, 0x99, 0x61, 0xbe, 0x3b, 0x6d, 0xfe, 0x60, 0xef, 0x14, 0x35, 0x7f, 0x21, 0xe8, 0x96, + 0x0e, 0xbd, 0x41, 0x7c, 0x65, 0x89, 0x96, 0x28, 0x5e, 0x85, 0xa6, 0x4b, 0xf3, 0xf9, 0xf8, 0x25, + 0x31, 0x4a, 0xbb, 0x72, 0xce, 0xcf, 0x53, 0xdf, 0x13, 0x42, 0x2d, 0xbc, 0x95, 0xa5, 0x6d, 0xc4, + 0x8c, 0x72, 0x99, 0xe8, 0x9a, 0xcf, 0x80, 0xd4, 0xc4, 0x3f, 0x55, 0xc3, 0x9b, 0x00, 0xce, 0x65, + 0x27, 0x6e, 0xbf, 0xb2, 0x88, 0xab, 0xc0, 0x5f, 0xf9, 0xd0, 0xc8, 0xbb, 0xd7, 0x48, 0xd7, 0x2e, + 0x5e, 0xbb, 0x23, 0x35, 0x6e, 0x62, 0xb6, 0x13, 0xd4, 0x06, 0xd1, 0x5b, 0x97, 0xee, 0x5b, 0x89, + 0x78, 0x07, 0x24, 0x74, 0x59, 0x06, 0x1e, 0x7f, 0x8c, 0xb0, 0x48, 0xff, 0x0a, 0x76, 0xb2, 0x07, + 0xa0, 0x99, 0xf5, 0x4b, 0x68, 0xd4, 0x55, 0x32, 0xb3, 0x17, 0x7b, 0xb6, 0x26, 0xdb, 0x1c, 0xc3, + 0x0b, 0xda, 0x3e, 0x46, 0xba, 0x1a, 0x67, 0x23, 0xb7, 0x2a, 0x40, 0xdc, 0xc9, 0xa2, 0xe4, 0xb5, + 0x68, 0x5c, 0x81, 0x60, 0xa7, 0xad, 0xe6, 0xba, 0xbb, 0x0d, 0x82, 0x8a, 0xe2, 0x03, 0xa9, 0x22, + 0x09, 0x5e, 0xd8, 0x69, 0xfa, 0x29, 0xd1, 0x32, 0xa1, 0xf0, 0x9b, 0x3c, 0xc3, 0x0b, 0x9a, 0x53, + 0xf0, 0x3e, 0xf3, 0x1b, 0x77, 0xee, 0x8f, 0xa6, 0x15, 0x02, 0x77, 0x14, 0x54, 0x60, 0xae, 0xbe, + 0x91, 0x9e, 0xe6, 0x8b, 0x87, 0x6e, 0x46, 0x44, 0x64, 0xc7, 0x58, 0x90, 0x62, 0x25, 0x32, 0xf9, + 0xcd, 0xd2, 0x73, 0x2e, 0x3f, 0xd7, 0x5d, 0x3c, 0x86, 0x1c, 0xa8, 0x35, 0xa9, 0xc2, 0xcb, 0x59, + 0xcb, 0xac, 0xb3, 0x03, 0x12, 0xd4, 0x8a, 0xde, 0xd5, 0xc1, 0xd8, 0x0c, 0x32, 0x49, 0x87, 0x97, + 0x62, 0x4f, 0x32, 0x39, 0x63, 0x5b, 0x8b, 0xd1, 0x6c, 0x5c, 0x90, 0xd9, 0x93, 0x13, 0xae, 0x70, + 0xf5, 0x2f, 0x40, 0xaf, 0x01, 0x95, 0x01, 0x0c, 0xc5, 0xfa, 0x82, 0xf8, 0x71, 0x9d, 0x53, 0xe6, + 0x47, 0x6e, 0x99, 0x54, 0x57, 0x41, 0x72, 0xea, 0xf5, 0x78, 0xdd, 0x86, 0xbd, 0x00, 0x00, 0x00, + 0x72, 0x6a, 0xf2, 0x47, 0x03, 0xc0, 0xcb, 0x01, 0x8d, 0x02, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xfb, 0xa7, 0xf7, 0x94, 0xe0, 0x01, 0x0c, 0x00, 0xc3, 0x5d, 0x00, 0x06, 0x82, 0xca, 0x9b, 0x77, + 0x93, 0x57, 0xb3, 0x76, 0xbd, 0x8b, 0xcb, 0xee, 0xf4, 0x2c, 0xff, 0x7f, 0x95, 0x33, 0x15, 0x10, + 0xa5, 0xf9, 0xfd, 0xa6, 0xbb, 0x9e, 0xf9, 0x75, 0x67, 0xee, 0xec, 0x8b, 0x40, 0xea, 0x32, 0x47, + 0x3d, 0x26, 0xbe, 0x11, 0x9c, 0xa6, 0x40, 0xbe, 0x84, 0x1f, 0x1b, 0x35, 0x1a, 0x66, 0x10, 0x9c, + 0xf4, 0x12, 0x1a, 0x95, 0x81, 0xb5, 0x55, 0x6b, 0xc5, 0x42, 0xfd, 0x37, 0x70, 0xc5, 0x08, 0xa4, + 0x27, 0x67, 0x11, 0x0b, 0x1f, 0xcc, 0xdb, 0x54, 0x9b, 0x5a, 0x5f, 0xee, 0x21, 0x63, 0xdd, 0x4b, + 0xbc, 0x49, 0x95, 0x6d, 0xf4, 0xcb, 0x9a, 0x9a, 0x5e, 0xe4, 0x7d, 0x0f, 0x02, 0x22, 0xa9, 0x42, + 0x46, 0x1a, 0x04, 0x87, 0x43, 0x72, 0x59, 0xa4, 0xd6, 0xeb, 0x69, 0x36, 0xde, 0xea, 0x53, 0x8c, + 0x89, 0xd7, 0x22, 0xa6, 0xf7, 0xa8, 0x4c, 0x72, 0x6c, 0x80, 0x69, 0x01, 0xb2, 0xa7, 0xe8, 0x8b, + 0x94, 0xaf, 0x0e, 0x47, 0x58, 0x1d, 0x0e, 0x5c, 0x7c, 0x33, 0x9f, 0x21, 0x17, 0x2c, 0x4f, 0x3d, + 0x72, 0xff, 0xcf, 0x7a, 0x4f, 0x82, 0x5b, 0x85, 0x28, 0x70, 0xf4, 0x8c, 0x81, 0x41, 0xb8, 0x20, + 0x5c, 0x3e, 0x02, 0x5e, 0x5a, 0x61, 0xbb, 0x2f, 0x64, 0xc5, 0x4e, 0x53, 0xe4, 0xca, 0xe4, 0xd9, + 0x75, 0xaf, 0x15, 0x4d, 0xff, 0x01, 0xec, 0x13, 0x4a, 0x70, 0x00, 0x04, 0xf9, 0xfa, 0x00, 0x00, + 0x99, 0x57, 0xc4, 0x96, 0x00, 0x02, 0xf7, 0x02, 0x80, 0x20, 0xdf, 0x01, 0x8d, 0x02, 0x00, 0x00, + 0x4c, 0x41, 0xe6, 0xa1, 0x9b, 0xe3, 0x51, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x59, 0x5a + }; + + protected static byte[] OriginalIndexedBytes => Encoding.ASCII.GetBytes(OriginalIndexed); + + protected static string OriginalIndexed { get; } = Original + Original + Original + Original + Original + Original + Original + Original + Original; } } From 525c1873e83d905a56c945501e24ac91c1086dde Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 17 Sep 2017 22:16:57 +0100 Subject: [PATCH 006/124] Fix merge --- tests/SharpCompress.Test/Xz/XZIndexTests.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/tests/SharpCompress.Test/Xz/XZIndexTests.cs b/tests/SharpCompress.Test/Xz/XZIndexTests.cs index ff8b3b0b..6f41945f 100644 --- a/tests/SharpCompress.Test/Xz/XZIndexTests.cs +++ b/tests/SharpCompress.Test/Xz/XZIndexTests.cs @@ -91,14 +91,5 @@ namespace SharpCompress.Test.Xz Assert.Equal(0L, badStream.Position % 4L); } } - - [Fact] - public void ReadsFirstRecord() - { - BinaryReader br = new BinaryReader(CompressedStream); - var index = new XZIndex(br, false); - index.Process(); - Assert.Equal((ulong)OriginalBytes.Length, index.Records[0].UncompressedSize); - } } } From 7064cda6dedb6d840e5c3727d420d6f4f8a698ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Sun, 17 Sep 2017 23:21:09 +0200 Subject: [PATCH 007/124] Zlib: fix Adler32 implementation (#301) --- src/SharpCompress/Compressors/Deflate/Zlib.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SharpCompress/Compressors/Deflate/Zlib.cs b/src/SharpCompress/Compressors/Deflate/Zlib.cs index 8cfaeac8..1badf10b 100644 --- a/src/SharpCompress/Compressors/Deflate/Zlib.cs +++ b/src/SharpCompress/Compressors/Deflate/Zlib.cs @@ -418,7 +418,7 @@ namespace SharpCompress.Compressors.Deflate internal sealed class Adler { // largest prime smaller than 65536 - private static readonly int BASE = 65521; + private static readonly uint BASE = 65521U; // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 private static readonly int NMAX = 5552; @@ -430,8 +430,8 @@ namespace SharpCompress.Compressors.Deflate return 1; } - int s1 = (int)(adler & 0xffff); - int s2 = (int)((adler >> 16) & 0xffff); + uint s1 = adler & 0xffffU; + uint s2 = (adler >> 16) & 0xffffU; while (len > 0) { @@ -486,7 +486,7 @@ namespace SharpCompress.Compressors.Deflate s1 %= BASE; s2 %= BASE; } - return (uint)((s2 << 16) | s1); + return (s2 << 16) | s1; } } } \ No newline at end of file From bb59f28b22a7ba2af467d944ec7d590defebe395 Mon Sep 17 00:00:00 2001 From: Thritton Date: Tue, 19 Sep 2017 16:25:10 +0200 Subject: [PATCH 008/124] Update ArchiveReader.cs (#303) #227 Added check if argument is in range in method TranslateTime(long? time) --- src/SharpCompress/Common/SevenZip/ArchiveReader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs index 64a18b8b..6f83395f 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs @@ -182,7 +182,7 @@ namespace SharpCompress.Common.SevenZip private DateTime? TranslateTime(long? time) { - if (time.HasValue) + if (time.HasValue && time.Value >= 0 && time.Value <= 2650467743999999999) //maximum Windows file time 31.12.9999 { return TranslateTime(time.Value); } @@ -1588,4 +1588,4 @@ namespace SharpCompress.Common.SevenZip #endregion } -} \ No newline at end of file +} From 50b01428b4a92097fcd7f4d4264ebb169e08c7d6 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Fri, 22 Sep 2017 09:16:42 +0100 Subject: [PATCH 009/124] Mark for 0.18.2 --- src/SharpCompress/SharpCompress.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 7ed7ffa3..2ec54654 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -2,9 +2,9 @@ SharpCompress - Pure C# Decompression/Compression en-US - 0.18.1 - 0.18.1.0 - 0.18.1.0 + 0.18.2 + 0.18.2.0 + 0.18.2.0 Adam Hathcock net45;net35;netstandard1.0;netstandard1.3 $(LibraryFrameworks) From 550fecd4d3a8d31e9daa9af2c6425e05abf63e73 Mon Sep 17 00:00:00 2001 From: coderb Date: Mon, 23 Oct 2017 06:58:02 -0400 Subject: [PATCH 010/124] bugfix: eliminate spurious rar crc exception when Read() is called with count = 0 (#313) --- src/SharpCompress/Compressors/Rar/RarCrcStream.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs index 3760d252..3a46cd77 100644 --- a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs @@ -31,7 +31,7 @@ namespace SharpCompress.Compressors.Rar { { currentCrc = RarCRC.CheckCrc(currentCrc, buffer, offset, result); } - else if (GetCrc() != readStream.CurrentCrc) + else if (GetCrc() != readStream.CurrentCrc && count != 0) { // NOTE: we use the last FileHeader in a multipart volume to check CRC throw new InvalidFormatException("file crc mismatch"); From c2528cf93e2cf4cbef9f23205ba3f74dc1fd84b1 Mon Sep 17 00:00:00 2001 From: Martijn Kant Date: Mon, 4 Dec 2017 11:55:30 +0100 Subject: [PATCH 011/124] Mk/add support for extracting password protected LZMA(2) 7z archives (#324) * Added possibility to decompress a password protected 7z LZMA archive * Fix tests --- .gitignore | 1 + .../Archives/SevenZip/SevenZipArchive.cs | 27 ++++++++++--- .../Common/SevenZip/ArchiveDatabase.cs | 7 ++++ .../Common/SevenZip/ArchiveReader.cs | 18 ++++----- .../Common/SevenZip/SevenZipFilePart.cs | 2 +- src/SharpCompress/IO/BufferedSubStream.cs | 2 +- tests/SharpCompress.Test/ArchiveTests.cs | 22 +++++----- .../SevenZip/SevenZipArchiveTests.cs | 38 ++++++++++++++++-- tests/TestArchives/Archives/7Zip.LZMA.Aes.7z | Bin 0 -> 59655 bytes tests/TestArchives/Archives/7Zip.LZMA2.Aes.7z | Bin 0 -> 59652 bytes 10 files changed, 86 insertions(+), 31 deletions(-) create mode 100644 tests/TestArchives/Archives/7Zip.LZMA.Aes.7z create mode 100644 tests/TestArchives/Archives/7Zip.LZMA2.Aes.7z diff --git a/.gitignore b/.gitignore index 71becff1..2e0b14fa 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ tests/TestArchives/Scratch .vs tools .vscode +.idea/ diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index f6ec3d50..48182b62 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using SharpCompress.Common; using SharpCompress.Common.SevenZip; +using SharpCompress.Compressors.LZMA.Utilites; using SharpCompress.IO; using SharpCompress.Readers; @@ -117,7 +118,7 @@ namespace SharpCompress.Archives.SevenZip stream.Position = 0; var reader = new ArchiveReader(); reader.Open(stream); - database = reader.ReadDatabase(null); + database = reader.ReadDatabase(new PasswordProvider(ReaderOptions.Password)); } } @@ -144,7 +145,7 @@ namespace SharpCompress.Archives.SevenZip protected override IReader CreateReaderForSolidExtraction() { - return new SevenZipReader(this); + return new SevenZipReader(ReaderOptions, this); } public override bool IsSolid { get { return Entries.Where(x => !x.IsDirectory).GroupBy(x => x.FilePart.Folder).Count() > 1; } } @@ -165,8 +166,8 @@ namespace SharpCompress.Archives.SevenZip private Stream currentStream; private CFileItem currentItem; - internal SevenZipReader(SevenZipArchive archive) - : base(new ReaderOptions(), ArchiveType.SevenZip) + internal SevenZipReader(ReaderOptions readerOptions, SevenZipArchive archive) + : base(readerOptions, ArchiveType.SevenZip) { this.archive = archive; } @@ -190,7 +191,7 @@ namespace SharpCompress.Archives.SevenZip } else { - currentStream = archive.database.GetFolderStream(stream, currentFolder, null); + currentStream = archive.database.GetFolderStream(stream, currentFolder, new PasswordProvider(Options.Password)); } foreach (var entry in group) { @@ -205,5 +206,21 @@ namespace SharpCompress.Archives.SevenZip return CreateEntryStream(new ReadOnlySubStream(currentStream, currentItem.Size)); } } + + private class PasswordProvider : IPasswordProvider + { + private readonly string _password; + + public PasswordProvider(string password) + { + _password = password; + + } + + public string CryptoGetTextPassword() + { + return _password; + } + } } } diff --git a/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs index fedd7782..c6da3cda 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs @@ -22,6 +22,13 @@ namespace SharpCompress.Common.SevenZip internal List PackStreamStartPositions = new List(); internal List FolderStartFileIndex = new List(); internal List FileIndexToFolderIndexMap = new List(); + + internal IPasswordProvider PasswordProvider { get; } + + public ArchiveDatabase(IPasswordProvider passwordProvider) + { + PasswordProvider = passwordProvider; + } internal void Clear() { diff --git a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs index 6f83395f..3e0155f0 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs @@ -1211,7 +1211,7 @@ namespace SharpCompress.Common.SevenZip public ArchiveDatabase ReadDatabase(IPasswordProvider pass) { - var db = new ArchiveDatabase(); + var db = new ArchiveDatabase(pass); db.Clear(); db.MajorVersion = _header[6]; @@ -1279,7 +1279,7 @@ namespace SharpCompress.Common.SevenZip throw new InvalidOperationException(); } - var dataVector = ReadAndDecodePackedStreams(db.StartPositionAfterHeader, pass); + var dataVector = ReadAndDecodePackedStreams(db.StartPositionAfterHeader, db.PasswordProvider); // compressed header without content is odd but ok if (dataVector.Count == 0) @@ -1301,7 +1301,7 @@ namespace SharpCompress.Common.SevenZip } } - ReadHeader(db, pass); + ReadHeader(db, db.PasswordProvider); } db.Fill(); return db; @@ -1441,7 +1441,7 @@ namespace SharpCompress.Common.SevenZip #endregion } - private Stream GetCachedDecoderStream(ArchiveDatabase _db, int folderIndex, IPasswordProvider pw) + private Stream GetCachedDecoderStream(ArchiveDatabase _db, int folderIndex) { Stream s; if (!_cachedStreams.TryGetValue(folderIndex, out s)) @@ -1456,13 +1456,13 @@ namespace SharpCompress.Common.SevenZip } s = DecoderStreamHelper.CreateDecoderStream(_stream, folderStartPackPos, packSizes.ToArray(), folderInfo, - pw); + _db.PasswordProvider); _cachedStreams.Add(folderIndex, s); } return s; } - public Stream OpenStream(ArchiveDatabase _db, int fileIndex, IPasswordProvider pw) + public Stream OpenStream(ArchiveDatabase _db, int fileIndex) { int folderIndex = _db.FileIndexToFolderIndexMap[fileIndex]; int numFilesInFolder = _db.NumUnpackStreamsVector[folderIndex]; @@ -1479,12 +1479,12 @@ namespace SharpCompress.Common.SevenZip skipSize += _db.Files[firstFileIndex + i].Size; } - Stream s = GetCachedDecoderStream(_db, folderIndex, pw); + Stream s = GetCachedDecoderStream(_db, folderIndex); s.Position = skipSize; return new ReadOnlySubStream(s, _db.Files[fileIndex].Size); } - public void Extract(ArchiveDatabase _db, int[] indices, IPasswordProvider pw) + public void Extract(ArchiveDatabase _db, int[] indices) { int numItems; bool allFilesMode = (indices == null); @@ -1562,7 +1562,7 @@ namespace SharpCompress.Common.SevenZip // TODO: If the decoding fails the last file may be extracted incompletely. Delete it? Stream s = DecoderStreamHelper.CreateDecoderStream(_stream, folderStartPackPos, packSizes.ToArray(), - folderInfo, pw); + folderInfo, _db.PasswordProvider); byte[] buffer = new byte[4 << 10]; for (;;) { diff --git a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs index 9dec4aa0..ea42d174 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs @@ -42,7 +42,7 @@ namespace SharpCompress.Common.SevenZip { return null; } - var folderStream = _database.GetFolderStream(_stream, Folder, null); + var folderStream = _database.GetFolderStream(_stream, Folder, _database.PasswordProvider); int firstFileIndex = _database.FolderStartFileIndex[_database.Folders.IndexOf(Folder)]; int skipCount = Index - firstFileIndex; diff --git a/src/SharpCompress/IO/BufferedSubStream.cs b/src/SharpCompress/IO/BufferedSubStream.cs index dd913d49..f08be705 100644 --- a/src/SharpCompress/IO/BufferedSubStream.cs +++ b/src/SharpCompress/IO/BufferedSubStream.cs @@ -41,7 +41,7 @@ namespace SharpCompress.IO throw new NotSupportedException(); } - public override long Length => throw new NotSupportedException(); + public override long Length => BytesLeftToRead; public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index b6d63c9f..757b10a1 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -40,7 +40,7 @@ namespace SharpCompress.Test foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { entry.WriteToDirectory(SCRATCH_FILES_PATH, - new ExtractionOptions() + new ExtractionOptions { ExtractFullPath = true, Overwrite = true @@ -51,24 +51,24 @@ namespace SharpCompress.Test } } - protected void ArchiveStreamRead(string testArchive) + protected void ArchiveStreamRead(string testArchive, ReaderOptions readerOptions = null) { testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); - ArchiveStreamRead(testArchive.AsEnumerable()); + ArchiveStreamRead(readerOptions, testArchive.AsEnumerable()); } - protected void ArchiveStreamRead(params string[] testArchives) + protected void ArchiveStreamRead(ReaderOptions readerOptions = null, params string[] testArchives) { - ArchiveStreamRead(testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x))); + ArchiveStreamRead(readerOptions, testArchives.Select(x => Path.Combine(TEST_ARCHIVES_PATH, x))); } - protected void ArchiveStreamRead(IEnumerable testArchives) + protected void ArchiveStreamRead(ReaderOptions readerOptions, IEnumerable testArchives) { foreach (var path in testArchives) { ResetScratch(); using (Stream stream = File.OpenRead(path)) - using (var archive = ArchiveFactory.Open(stream)) + using (var archive = ArchiveFactory.Open(stream, readerOptions)) { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { @@ -83,17 +83,17 @@ namespace SharpCompress.Test } } - protected void ArchiveFileRead(string testArchive) + protected void ArchiveFileRead(string testArchive, ReaderOptions readerOptions = null) { testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); - ArchiveFileRead(testArchive.AsEnumerable()); + ArchiveFileRead(testArchive.AsEnumerable(), readerOptions); } - protected void ArchiveFileRead(IEnumerable testArchives) + protected void ArchiveFileRead(IEnumerable testArchives, ReaderOptions readerOptions = null) { foreach (var path in testArchives) { ResetScratch(); - using (var archive = ArchiveFactory.Open(path)) + using (var archive = ArchiveFactory.Open(path, readerOptions)) { //archive.EntryExtractionBegin += archive_EntryExtractionBegin; //archive.FilePartExtractionBegin += archive_FilePartExtractionBegin; diff --git a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs index 4faf74af..db6c07f0 100644 --- a/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs +++ b/tests/SharpCompress.Test/SevenZip/SevenZipArchiveTests.cs @@ -1,5 +1,6 @@ using System; using SharpCompress.Common; +using SharpCompress.Readers; using Xunit; namespace SharpCompress.Test.SevenZip @@ -18,6 +19,17 @@ namespace SharpCompress.Test.SevenZip ArchiveFileRead("7Zip.LZMA.7z"); } + [Fact] + public void SevenZipArchive_LZMAAES_StreamRead() + { + ArchiveStreamRead("7Zip.LZMA.Aes.7z", new ReaderOptions() {Password = "testpassword"}); + } + + [Fact] + public void SevenZipArchive_LZMAAES_PathRead() + { + ArchiveFileRead("7Zip.LZMA.Aes.7z", new ReaderOptions() {Password = "testpassword"}); + } [Fact] public void SevenZipArchive_PPMd_StreamRead() { @@ -35,6 +47,7 @@ namespace SharpCompress.Test.SevenZip { ArchiveFileRead("7Zip.PPMd.7z"); } + [Fact] public void SevenZipArchive_LZMA2_StreamRead() { @@ -46,6 +59,19 @@ namespace SharpCompress.Test.SevenZip { ArchiveFileRead("7Zip.LZMA2.7z"); } + + [Fact] + public void SevenZipArchive_LZMA2AES_StreamRead() + { + ArchiveStreamRead("7Zip.LZMA2.Aes.7z", new ReaderOptions {Password = "testpassword"}); + } + + [Fact] + public void SevenZipArchive_LZMA2AES_PathRead() + { + ArchiveFileRead("7Zip.LZMA2.Aes.7z", new ReaderOptions {Password = "testpassword"}); + } + [Fact] public void SevenZipArchive_BZip2_StreamRead() { @@ -67,9 +93,13 @@ namespace SharpCompress.Test.SevenZip [Fact] public void SevenZipArchive_BZip2_Split() { - Assert.Throws(() => ArchiveStreamRead("Original.7z.001", "Original.7z.002", - "Original.7z.003", "Original.7z.004", "Original.7z.005", - "Original.7z.006", "Original.7z.007")); + Assert.Throws(() => ArchiveStreamRead(null, "Original.7z.001", + "Original.7z.002", + "Original.7z.003", + "Original.7z.004", + "Original.7z.005", + "Original.7z.006", + "Original.7z.007")); } } -} +} \ No newline at end of file diff --git a/tests/TestArchives/Archives/7Zip.LZMA.Aes.7z b/tests/TestArchives/Archives/7Zip.LZMA.Aes.7z new file mode 100644 index 0000000000000000000000000000000000000000..21a07017c89cefbf10286013b755f9d977373f2c GIT binary patch literal 59655 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%0DT-R|ZHJz`)7TrisZXm)pnjL{ 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 literal 0 HcmV?d00001 From 3da3b212fa55521ed603e0a06c7f79ec81d29dd3 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 4 Dec 2017 18:48:38 +0000 Subject: [PATCH 012/124] create new memorystream to allow proper resizing as memorystream could be a user provided buffer. Update xunit (#307) --- src/SharpCompress/IO/RewindableStream.cs | 8 +++++++- tests/SharpCompress.Test/SharpCompress.Test.csproj | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/SharpCompress/IO/RewindableStream.cs b/src/SharpCompress/IO/RewindableStream.cs index c4b77659..3cfc035c 100644 --- a/src/SharpCompress/IO/RewindableStream.cs +++ b/src/SharpCompress/IO/RewindableStream.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using SharpCompress.Compressors.Filters; namespace SharpCompress.IO { @@ -46,8 +47,13 @@ namespace SharpCompress.IO } else { + bufferStream.TransferTo(buffer); - bufferStream = buffer; + //create new memorystream to allow proper resizing as memorystream could be a user provided buffer + //https://github.com/adamhathcock/sharpcompress/issues/306 + bufferStream = new MemoryStream(); + buffer.Position = 0; + buffer.TransferTo(bufferStream); bufferStream.Position = 0; } isRewound = true; diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index d0f8cdd1..23b005e8 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -14,10 +14,10 @@ - + - - + + From a4ebd5fb3d1e81d92db63296b2d010882ab79455 Mon Sep 17 00:00:00 2001 From: Sors Date: Mon, 4 Dec 2017 20:59:49 +0200 Subject: [PATCH 013/124] Rar 5 format (#310) Fix rar 5 format comment --- src/SharpCompress/Common/Rar/Headers/MarkHeader.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs index 4c4d0b6e..f5c61274 100644 --- a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs @@ -18,9 +18,9 @@ namespace SharpCompress.Common.Rar.Headers Flags == 0x1A21 && HeaderSize == 0x07; - // Rar5 signature: 52 61 72 21 1A 07 10 00 (not supported yet) + // Rar5 signature: 52 61 72 21 1A 07 01 00 (not supported yet) } internal bool OldFormat { get; private set; } } -} \ No newline at end of file +} From a8b6def76a9536587691c9dec0556347ec0137ad Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Fri, 8 Dec 2017 12:00:29 +0000 Subject: [PATCH 014/124] Netcore2 (#302) * Add netstandard 2.0 target and netcoreapp2.0 tests * Update xunit * set tests explicitly to netcore2 * update travis * Don't say build as netcoreapp1.0 * try adding dotnet 1 too * Remove .NET Core 1 support * switch to circle * update cake * fix circle build * try fix file ending test again * Fix casing on files * Another casing fix * Add back netstandard1.0 * Finish adding netstandard 1.0 back * Add netstandard1.3 back --- .circleci/config.yml | 11 ++++++ .gitattributes | 2 +- .travis.yml | 13 ------- SharpCompress.sln.DotSettings | 1 + build.cake | 31 ++++++++++------- build.sh | 2 +- src/SharpCompress/SharpCompress.csproj | 3 +- .../SharpCompress.Test/Rar/RarArchiveTests.cs | 34 +++++++++---------- .../Rar/RarHeaderFactoryTest.cs | 10 +++--- .../SharpCompress.Test/Rar/RarReaderTests.cs | 32 ++++++++--------- .../SharpCompress.Test.csproj | 19 +++++------ .../SharpCompress.Test/Tar/TarArchiveTests.cs | 28 +++++++-------- tests/SharpCompress.Test/TestBase.cs | 19 ++++++++--- .../SharpCompress.Test/Zip/ZipArchiveTests.cs | 10 +++--- .../SharpCompress.Test/Zip/ZipReaderTests.cs | 24 ++++++------- 15 files changed, 124 insertions(+), 115 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 .travis.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..8cd5464c --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,11 @@ +version: 2 +jobs: + build: + docker: + - image: adamhathcock/cake-build:latest + steps: + - checkout + - run: + name: Build + command: ./build.sh + \ No newline at end of file diff --git a/.gitattributes b/.gitattributes index 2b6346c9..0a96b77c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,4 +2,4 @@ * text=auto # need original files to be windows -test/TestArchives/Original/*.txt eol=crlf \ No newline at end of file +*.txt text eol=crlf \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9737ad20..00000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -dist: trusty -language: csharp -cache: - directories: - - $HOME/.dotnet -solution: SharpCompress.sln -matrix: - include: - - dotnet: 1.0.4 - mono: none - env: DOTNETCORE=1 -script: - - ./build.sh \ No newline at end of file diff --git a/SharpCompress.sln.DotSettings b/SharpCompress.sln.DotSettings index d6a99be9..a3a163da 100644 --- a/SharpCompress.sln.DotSettings +++ b/SharpCompress.sln.DotSettings @@ -114,6 +114,7 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True True True True diff --git a/build.cake b/build.cake index c1d19980..c6193be1 100644 --- a/build.cake +++ b/build.cake @@ -30,8 +30,11 @@ Task("Build") DotNetCoreBuild("./src/SharpCompress/SharpCompress.csproj", settings); - settings.Framework = "netcoreapp1.1"; - DotNetCoreBuild("./tests/SharpCompress.Test/SharpCompress.Test.csproj", settings); + settings.Framework = "netstandard1.3"; + DotNetCoreBuild("./src/SharpCompress/SharpCompress.csproj", settings); + + settings.Framework = "netstandard2.0"; + DotNetCoreBuild("./src/SharpCompress/SharpCompress.csproj", settings); } }); @@ -39,23 +42,25 @@ Task("Test") .IsDependentOn("Build") .Does(() => { - if (!bool.Parse(EnvironmentVariable("APPVEYOR") ?? "false") - && !bool.Parse(EnvironmentVariable("TRAVIS") ?? "false")) + var files = GetFiles("tests/**/*.csproj"); + foreach(var file in files) { - var files = GetFiles("tests/**/*.csproj"); - foreach(var file in files) + var settings = new DotNetCoreTestSettings { - var settings = new DotNetCoreTestSettings + Configuration = "Release", + Framework = "netcoreapp2.0" + }; + + DotNetCoreTest(file.ToString(), settings); + + + settings = new DotNetCoreTestSettings { - Configuration = "Release" + Configuration = "Release", + Framework = "netcoreapp2.0" }; DotNetCoreTest(file.ToString(), settings); - } - } - else - { - Information("Skipping tests as this is AppVeyor or Travis CI"); } }); diff --git a/build.sh b/build.sh index 9ed17711..90111e63 100755 --- a/build.sh +++ b/build.sh @@ -8,7 +8,7 @@ # Define directories. SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) TOOLS_DIR=$SCRIPT_DIR/tools -CAKE_VERSION=0.19.1 +CAKE_VERSION=0.23.0 CAKE_DLL=$TOOLS_DIR/Cake.CoreCLR.$CAKE_VERSION/Cake.dll # Make sure the tools folder exist. diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 2ec54654..888ffd94 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -6,8 +6,7 @@ 0.18.2.0 0.18.2.0 Adam Hathcock - net45;net35;netstandard1.0;netstandard1.3 - $(LibraryFrameworks) + net45;net35;netstandard1.0;netstandard1.3;netstandard2.0 true true SharpCompress diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index 87ac25b1..e227a74e 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -35,10 +35,10 @@ namespace SharpCompress.Test.Rar ResetScratch(); using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, testArchive))) using (var archive = RarArchive.Open(stream, new ReaderOptions() - { - Password = password, - LeaveStreamOpen = true - })) + { + Password = password, + LeaveStreamOpen = true + })) { foreach (var entry in archive.Entries) { @@ -66,10 +66,10 @@ namespace SharpCompress.Test.Rar { ResetScratch(); using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, archiveName), new ReaderOptions() - { - Password = password, - LeaveStreamOpen = true - })) + { + Password = password, + LeaveStreamOpen = true + })) { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { @@ -120,12 +120,12 @@ namespace SharpCompress.Test.Rar public void Rar_Jpg_ArchiveStreamRead() { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "RarJpeg.jpg"))) + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rarjpeg.jpg"))) { using (var archive = RarArchive.Open(stream, new ReaderOptions() - { - LookForHeader = true - })) + { + LookForHeader = true + })) { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { @@ -224,7 +224,7 @@ namespace SharpCompress.Test.Rar using (var archive = RarArchive.Open(stream)) { Assert.False(archive.IsSolid); - Assert.True(archive.Entries.Any(entry => entry.IsDirectory)); + Assert.Contains(true, archive.Entries.Select(entry => entry.IsDirectory)); } } } @@ -233,10 +233,10 @@ namespace SharpCompress.Test.Rar public void Rar_Jpg_ArchiveFileRead() { ResetScratch(); - using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, "RarJpeg.jpg"), new ReaderOptions() - { - LookForHeader = true - })) + using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, "Rarjpeg.jpg"), new ReaderOptions() + { + LookForHeader = true + })) { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { diff --git a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs index 0a5908d3..29309cff 100644 --- a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs +++ b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs @@ -12,14 +12,14 @@ namespace SharpCompress.Test.Rar public class RarHeaderFactoryTest : TestBase { private readonly RarHeaderFactory rarHeaderFactory; - + public RarHeaderFactoryTest() { ResetScratch(); rarHeaderFactory = new RarHeaderFactory(StreamingMode.Seekable, new ReaderOptions() - { - LeaveStreamOpen = true - }); + { + LeaveStreamOpen = true + }); } @@ -27,7 +27,7 @@ namespace SharpCompress.Test.Rar public void ReadHeaders_RecognizeEncryptedFlag() { - ReadEncryptedFlag("Rar.Encrypted_filesAndHeader.rar", true); + ReadEncryptedFlag("Rar.encrypted_filesAndHeader.rar", true); diff --git a/tests/SharpCompress.Test/Rar/RarReaderTests.cs b/tests/SharpCompress.Test/Rar/RarReaderTests.cs index 9dc0f24a..aa6d5aa0 100644 --- a/tests/SharpCompress.Test/Rar/RarReaderTests.cs +++ b/tests/SharpCompress.Test/Rar/RarReaderTests.cs @@ -149,9 +149,9 @@ namespace SharpCompress.Test.Rar ResetScratch(); using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, testArchive))) using (var reader = RarReader.Open(stream, new ReaderOptions() - { - Password = password - })) + { + Password = password + })) { while (reader.MoveToNextEntry()) { @@ -209,9 +209,9 @@ namespace SharpCompress.Test.Rar ResetScratch(); using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Audio_program.rar"))) using (var reader = RarReader.Open(stream, new ReaderOptions() - { - LookForHeader = true - })) + { + LookForHeader = true + })) { while (reader.MoveToNextEntry()) { @@ -231,11 +231,11 @@ namespace SharpCompress.Test.Rar public void Rar_Jpg_Reader() { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "RarJpeg.jpg"))) + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rarjpeg.jpg"))) using (var reader = RarReader.Open(stream, new ReaderOptions() - { - LookForHeader = true - })) + { + LookForHeader = true + })) { while (reader.MoveToNextEntry()) { @@ -262,9 +262,9 @@ namespace SharpCompress.Test.Rar ResetScratch(); using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.solid.rar"))) using (var reader = RarReader.Open(stream, new ReaderOptions() - { - LookForHeader = true - })) + { + LookForHeader = true + })) { while (reader.MoveToNextEntry()) { @@ -287,9 +287,9 @@ namespace SharpCompress.Test.Rar ResetScratch(); using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.rar"))) using (var reader = RarReader.Open(stream, new ReaderOptions() - { - LookForHeader = true - })) + { + LookForHeader = true + })) { while (reader.MoveToNextEntry()) { diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index 23b005e8..a8651e7c 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -1,28 +1,25 @@  - netcoreapp1.1 + netcoreapp2.0 SharpCompress.Test ../../SharpCompress.snk true true SharpCompress.Test true - 1.1.2 - - + + + + + - - - - - - - + + \ No newline at end of file diff --git a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs index e0cf73b4..4e677abb 100644 --- a/tests/SharpCompress.Test/Tar/TarArchiveTests.cs +++ b/tests/SharpCompress.Test/Tar/TarArchiveTests.cs @@ -35,11 +35,11 @@ namespace SharpCompress.Test.Tar using (var archive = TarArchive.Open(unmodified)) { Assert.Equal(5, archive.Entries.Count); - Assert.True(archive.Entries.Any(entry => entry.Key == "very long filename/")); - Assert.True(archive.Entries.Any(entry => entry.Key == "very long filename/very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename.jpg")); - Assert.True(archive.Entries.Any(entry => entry.Key == "z_file 1.txt")); - Assert.True(archive.Entries.Any(entry => entry.Key == "z_file 2.txt")); - Assert.True(archive.Entries.Any(entry => entry.Key == "z_file 3.txt")); + Assert.Contains("very long filename/", archive.Entries.Select(entry => entry.Key)); + Assert.Contains("very long filename/very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename very long filename.jpg", archive.Entries.Select(entry => entry.Key)); + Assert.Contains("z_file 1.txt", archive.Entries.Select(entry => entry.Key)); + Assert.Contains("z_file 2.txt", archive.Entries.Select(entry => entry.Key)); + Assert.Contains("z_file 3.txt", archive.Entries.Select(entry => entry.Key)); } } @@ -74,7 +74,7 @@ namespace SharpCompress.Test.Tar using (var archive2 = TarArchive.Open(unmodified)) { Assert.Equal(1, archive2.Entries.Count); - Assert.True(archive2.Entries.Any(entry => entry.Key == longFilename)); + Assert.Contains(longFilename, archive2.Entries.Select(entry => entry.Key)); foreach (var entry in archive2.Entries) Assert.Equal("dummy filecontent", new StreamReader(entry.OpenEntryStream()).ReadLine()); @@ -85,15 +85,15 @@ namespace SharpCompress.Test.Tar public void Tar_UstarArchivePathReadLongName() { string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "ustar with long names.tar"); - using(var archive = TarArchive.Open(unmodified)) + using (var archive = TarArchive.Open(unmodified)) { Assert.Equal(6, archive.Entries.Count); - Assert.True(archive.Entries.Any(entry => entry.Key == "Directory/")); - Assert.True(archive.Entries.Any(entry => entry.Key == "Directory/Some file with veeeeeeeeeery loooooooooong name")); - Assert.True(archive.Entries.Any(entry => entry.Key == "Directory/Directory with veeeeeeeeeery loooooooooong name/")); - Assert.True(archive.Entries.Any(entry => entry.Key == "Directory/Directory with veeeeeeeeeery loooooooooong name/Some file with veeeeeeeeeery loooooooooong name")); - Assert.True(archive.Entries.Any(entry => entry.Key == "Directory/Directory with veeeeeeeeeery loooooooooong name/Directory with veeeeeeeeeery loooooooooong name/")); - Assert.True(archive.Entries.Any(entry => entry.Key == "Directory/Directory with veeeeeeeeeery loooooooooong name/Directory with veeeeeeeeeery loooooooooong name/Some file with veeeeeeeeeery loooooooooong name")); + Assert.Contains("Directory/", archive.Entries.Select(entry => entry.Key)); + Assert.Contains("Directory/Some file with veeeeeeeeeery loooooooooong name", archive.Entries.Select(entry => entry.Key)); + Assert.Contains("Directory/Directory with veeeeeeeeeery loooooooooong name/", archive.Entries.Select(entry => entry.Key)); + Assert.Contains("Directory/Directory with veeeeeeeeeery loooooooooong name/Some file with veeeeeeeeeery loooooooooong name", archive.Entries.Select(entry => entry.Key)); + Assert.Contains("Directory/Directory with veeeeeeeeeery loooooooooong name/Directory with veeeeeeeeeery loooooooooong name/", archive.Entries.Select(entry => entry.Key)); + Assert.Contains("Directory/Directory with veeeeeeeeeery loooooooooong name/Directory with veeeeeeeeeery loooooooooong name/Some file with veeeeeeeeeery loooooooooong name", archive.Entries.Select(entry => entry.Key)); } } @@ -114,7 +114,7 @@ namespace SharpCompress.Test.Tar [Fact] public void Tar_Random_Write_Add() { - string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg","test.jpg"); + string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"); string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Tar.mod.tar"); string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.mod.tar"); string modified = Path.Combine(TEST_ARCHIVES_PATH, "Tar.noEmptyDirs.tar"); diff --git a/tests/SharpCompress.Test/TestBase.cs b/tests/SharpCompress.Test/TestBase.cs index 496cff41..4547ad76 100644 --- a/tests/SharpCompress.Test/TestBase.cs +++ b/tests/SharpCompress.Test/TestBase.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; -using Microsoft.Extensions.PlatformAbstractions; -using SharpCompress.Common; using SharpCompress.Readers; using Xunit; @@ -12,7 +10,7 @@ namespace SharpCompress.Test { public class TestBase : IDisposable { - protected string SOLUTION_BASE_PATH=null; + protected string SOLUTION_BASE_PATH = null; protected string TEST_ARCHIVES_PATH; protected string ORIGINAL_FILES_PATH; protected string MISC_TEST_FILES_PATH; @@ -181,6 +179,11 @@ namespace SharpCompress.Test protected void CompareFilesByPath(string file1, string file2) { + //TODO: fix line ending issues with the text file + if (file1.EndsWith("txt")) + { + return; + } using (var file1Stream = File.OpenRead(file1)) using (var file2Stream = File.OpenRead(file2)) { @@ -237,8 +240,14 @@ namespace SharpCompress.Test public TestBase() { Monitor.Enter(lockObject); - var index = PlatformServices.Default.Application.ApplicationBasePath.IndexOf("SharpCompress.Test", StringComparison.OrdinalIgnoreCase); - SOLUTION_BASE_PATH = Path.GetDirectoryName(PlatformServices.Default.Application.ApplicationBasePath.Substring(0, index)); + +#if NETSTANDARD20 + var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf("SharpCompress.Test", StringComparison.OrdinalIgnoreCase); + SOLUTION_BASE_PATH = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory.Substring(0, index)); +#else + var index = Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationBasePath.IndexOf("SharpCompress.Test", StringComparison.OrdinalIgnoreCase); + SOLUTION_BASE_PATH = Path.GetDirectoryName(Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationBasePath.Substring(0, index)); +#endif TEST_ARCHIVES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Archives"); ORIGINAL_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Original"); MISC_TEST_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "MiscTest"); diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index b0b26555..031698d8 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -160,7 +160,7 @@ namespace SharpCompress.Test.Zip [Fact] public void Zip_Random_Write_Add() { - string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg","test.jpg"); + string jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"); string scratchPath = Path.Combine(SCRATCH_FILES_PATH, "Zip.deflate.mod.zip"); string unmodified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod.zip"); string modified = Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.mod2.zip"); @@ -297,7 +297,7 @@ namespace SharpCompress.Test.Zip { archive.AddAllFromDirectory(SCRATCH_FILES_PATH); archive.RemoveEntry(archive.Entries.Single(x => x.Key.EndsWith("jpg", StringComparison.OrdinalIgnoreCase))); - Assert.False(archive.Entries.Any(x => x.Key.EndsWith("jpg"))); + Assert.Null(archive.Entries.FirstOrDefault(x => x.Key.EndsWith("jpg"))); } Directory.Delete(SCRATCH_FILES_PATH, true); } @@ -307,9 +307,9 @@ namespace SharpCompress.Test.Zip { ResetScratch(); using (var reader = ZipArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES.zip"), new ReaderOptions() - { - Password = "test" - })) + { + Password = "test" + })) { foreach (var entry in reader.Entries.Where(x => !x.IsDirectory)) { diff --git a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs index a86cb2d4..4e264dcc 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -14,7 +14,7 @@ namespace SharpCompress.Test.Zip { UseExtensionInsteadOfNameToVerify = true; } - + [Fact] public void Issue_269_Double_Skip() { @@ -41,13 +41,13 @@ namespace SharpCompress.Test.Zip [Fact] public void Zip_Zip64_Streamed_Read() { - Read("Zip.Zip64.zip", CompressionType.Deflate); + Read("Zip.zip64.zip", CompressionType.Deflate); } [Fact] public void Zip_ZipX_Streamed_Read() { - Read("Zip.Zipx", CompressionType.LZMA); + Read("Zip.zipx", CompressionType.LZMA); } [Fact] @@ -141,8 +141,8 @@ namespace SharpCompress.Test.Zip ResetScratch(); using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.bzip2.pkware.zip"))) using (var reader = ZipReader.Open(stream, new ReaderOptions() - { - Password = "test" + { + Password = "test" })) { while (reader.MoveToNextEntry()) @@ -218,11 +218,11 @@ namespace SharpCompress.Test.Zip using ( Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, - "Zip.lzma.winzipaes.zip"))) + "Zip.lzma.WinzipAES.zip"))) using (var reader = ZipReader.Open(stream, new ReaderOptions() - { - Password = "test" - })) + { + Password = "test" + })) { while (reader.MoveToNextEntry()) { @@ -248,9 +248,9 @@ namespace SharpCompress.Test.Zip ResetScratch(); using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.WinzipAES.zip"))) using (var reader = ZipReader.Open(stream, new ReaderOptions() - { - Password = "test" - })) + { + Password = "test" + })) { while (reader.MoveToNextEntry()) { From 45845f8963c8f61f2d9469996421662d2c306d25 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Fri, 8 Dec 2017 12:03:28 +0000 Subject: [PATCH 015/124] Add Circle CI build --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 104efdcc..ae23f57a 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ The major feature is support for non-seekable streams so large files can be proc AppVeyor Build - [![Build status](https://ci.appveyor.com/api/projects/status/voxg971oemmvxh1e/branch/master?svg=true)](https://ci.appveyor.com/project/adamhathcock/sharpcompress/branch/master) -Travis CI Build - -[![Build Status](https://travis-ci.org/adamhathcock/sharpcompress.svg?branch=master)](https://travis-ci.org/adamhathcock/sharpcompress) +Circle CI Build - +[![CircleCI](https://circleci.com/gh/adamhathcock/sharpcompress.svg?style=svg)](https://circleci.com/gh/adamhathcock/sharpcompress) ## Need Help? Post Issues on Github! From 694e8691624066353e6a094cd50250e9a51e19d6 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Fri, 8 Dec 2017 13:58:38 +0000 Subject: [PATCH 016/124] Use arraypool for transfer/skip (#326) * Use arraypool for transfer/skip * Merge fixes * Remove redundant constant --- build.cake | 12 +- src/SharpCompress/SharpCompress.csproj | 15 +++ src/SharpCompress/Utility.cs | 113 ++++++++++++------ .../SharpCompress.Test.csproj | 2 +- 4 files changed, 100 insertions(+), 42 deletions(-) diff --git a/build.cake b/build.cake index c6193be1..c92639cc 100644 --- a/build.cake +++ b/build.cake @@ -54,13 +54,13 @@ Task("Test") DotNetCoreTest(file.ToString(), settings); - settings = new DotNetCoreTestSettings - { - Configuration = "Release", - Framework = "netcoreapp2.0" - }; + settings = new DotNetCoreTestSettings + { + Configuration = "Release", + Framework = "netcoreapp1.1" + }; - DotNetCoreTest(file.ToString(), settings); + DotNetCoreTest(file.ToString(), settings); } }); diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 888ffd94..18d0878b 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -24,4 +24,19 @@ $(DefineConstants);NO_FILE;NO_CRYPTO;SILVERLIGHT + + $(DefineConstants);NETCORE + + + $(DefineConstants);NETCORE + + + + + + + + + + \ No newline at end of file diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 9da09c64..b13429b8 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -1,4 +1,7 @@ using System; +#if NETCORE || NET45 +using System.Buffers; +#endif using System.Collections.Generic; using System.IO; using System.Linq; @@ -7,7 +10,7 @@ using SharpCompress.Readers; namespace SharpCompress { internal static class Utility - { + { public static ReadOnlyCollection ToReadOnly(this IEnumerable items) { return new ReadOnlyCollection(items.ToList()); @@ -139,36 +142,54 @@ namespace SharpCompress public static void Skip(this Stream source, long advanceAmount) { byte[] buffer = GetTransferByteArray(); - int read = 0; - int readCount = 0; - do + try { - readCount = buffer.Length; - if (readCount > advanceAmount) + int read = 0; + int readCount = 0; + do { - readCount = (int)advanceAmount; - } - read = source.Read(buffer, 0, readCount); - if (read <= 0) - { - break; - } - advanceAmount -= read; - if (advanceAmount == 0) - { - break; + readCount = buffer.Length; + if (readCount > advanceAmount) + { + readCount = (int)advanceAmount; + } + read = source.Read(buffer, 0, readCount); + if (read <= 0) + { + break; + } + advanceAmount -= read; + if (advanceAmount == 0) + { + break; + } } + while (true); + } + finally + { +#if NETCORE || NET45 + ArrayPool.Shared.Return(buffer); +#endif } - while (true); } public static void Skip(this Stream source) { byte[] buffer = GetTransferByteArray(); - do + try { + do + { + } + while (source.Read(buffer, 0, buffer.Length) == buffer.Length); + } + finally + { +#if NETCORE || NET45 + ArrayPool.Shared.Return(buffer); +#endif } - while (source.Read(buffer, 0, buffer.Length) == buffer.Length); } public static DateTime DosDateToDateTime(UInt16 iDate, UInt16 iTime) @@ -233,30 +254,48 @@ namespace SharpCompress public static long TransferTo(this Stream source, Stream destination) { byte[] array = GetTransferByteArray(); - int count; - long total = 0; - while (ReadTransferBlock(source, array, out count)) + try { - total += count; - destination.Write(array, 0, count); + int count; + long total = 0; + while (ReadTransferBlock(source, array, out count)) + { + total += count; + destination.Write(array, 0, count); + } + return total; + } + finally + { +#if NETCORE || NET45 + ArrayPool.Shared.Return(array); +#endif } - return total; } public static long TransferTo(this Stream source, Stream destination, Common.Entry entry, IReaderExtractionListener readerExtractionListener) { byte[] array = GetTransferByteArray(); - int count; - var iterations = 0; - long total = 0; - while (ReadTransferBlock(source, array, out count)) + try { - total += count; - destination.Write(array, 0, count); - iterations++; - readerExtractionListener.FireEntryExtractionProgress(entry, total, iterations); + int count; + var iterations = 0; + long total = 0; + while (ReadTransferBlock(source, array, out count)) + { + total += count; + destination.Write(array, 0, count); + iterations++; + readerExtractionListener.FireEntryExtractionProgress(entry, total, iterations); + } + return total; + } + finally + { +#if NETCORE || NET45 + ArrayPool.Shared.Return(array); +#endif } - return total; } private static bool ReadTransferBlock(Stream source, byte[] array, out int count) @@ -266,7 +305,11 @@ namespace SharpCompress private static byte[] GetTransferByteArray() { +#if NETCORE || NET45 + return ArrayPool.Shared.Rent(81920); +#else return new byte[81920]; +#endif } public static bool ReadFully(this Stream stream, byte[] buffer) diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index a8651e7c..8dbcecb1 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -1,6 +1,6 @@  - netcoreapp2.0 + netcoreapp1.1;netcoreapp2.0 SharpCompress.Test ../../SharpCompress.snk true From bd9417e74c454d400bd5f723b1db8cdcafe76c05 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Tue, 12 Dec 2017 11:17:57 +0000 Subject: [PATCH 017/124] Mark for 0.19 --- src/SharpCompress/SharpCompress.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 18d0878b..91b0ff1a 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -2,9 +2,9 @@ SharpCompress - Pure C# Decompression/Compression en-US - 0.18.2 - 0.18.2.0 - 0.18.2.0 + 0.19.0 + 0.19.0.0 + 0.19.0.0 Adam Hathcock net45;net35;netstandard1.0;netstandard1.3;netstandard2.0 true From 5bdf01ee59273975378cc246a9314ffc7e6e7793 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Fri, 15 Dec 2017 14:45:02 +0000 Subject: [PATCH 018/124] Absorb arraypool from CoreFX (#331) --- src/SharpCompress/Buffers/ArrayPool.cs | 119 +++++++++++++++ src/SharpCompress/Buffers/DefaultArrayPool.cs | 142 ++++++++++++++++++ .../Buffers/DefaultArrayPoolBucket.cs | 110 ++++++++++++++ src/SharpCompress/Buffers/Utilities.cs | 38 +++++ src/SharpCompress/SharpCompress.csproj | 9 -- src/SharpCompress/Utility.cs | 12 +- 6 files changed, 415 insertions(+), 15 deletions(-) create mode 100644 src/SharpCompress/Buffers/ArrayPool.cs create mode 100644 src/SharpCompress/Buffers/DefaultArrayPool.cs create mode 100644 src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs create mode 100644 src/SharpCompress/Buffers/Utilities.cs diff --git a/src/SharpCompress/Buffers/ArrayPool.cs b/src/SharpCompress/Buffers/ArrayPool.cs new file mode 100644 index 00000000..0ef99b2e --- /dev/null +++ b/src/SharpCompress/Buffers/ArrayPool.cs @@ -0,0 +1,119 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#if NETCORE +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Buffers +{ + ///

+ /// Provides a resource pool that enables reusing instances of type . + /// + /// + /// + /// Renting and returning buffers with an can increase performance + /// in situations where arrays are created and destroyed frequently, resulting in significant + /// memory pressure on the garbage collector. + /// + /// + /// This class is thread-safe. All members may be used by multiple threads concurrently. + /// + /// + public abstract class ArrayPool + { + /// The lazily-initialized shared pool instance. + private static ArrayPool s_sharedInstance = null; + + /// + /// Retrieves a shared instance. + /// + /// + /// The shared pool provides a default implementation of + /// that's intended for general applicability. It maintains arrays of multiple sizes, and + /// may hand back a larger array than was actually requested, but will never hand back a smaller + /// array than was requested. Renting a buffer from it with will result in an + /// existing buffer being taken from the pool if an appropriate buffer is available or in a new + /// buffer being allocated if one is not available. + /// + public static ArrayPool Shared + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Volatile.Read(ref s_sharedInstance) ?? EnsureSharedCreated(); } + } + + /// Ensures that has been initialized to a pool and returns it. + [MethodImpl(MethodImplOptions.NoInlining)] + private static ArrayPool EnsureSharedCreated() + { + Interlocked.CompareExchange(ref s_sharedInstance, Create(), null); + return s_sharedInstance; + } + + /// + /// Creates a new instance using default configuration options. + /// + /// A new instance. + public static ArrayPool Create() + { + return new DefaultArrayPool(); + } + + /// + /// Creates a new instance using custom configuration options. + /// + /// The maximum length of array instances that may be stored in the pool. + /// + /// The maximum number of array instances that may be stored in each bucket in the pool. The pool + /// groups arrays of similar lengths into buckets for faster access. + /// + /// A new instance with the specified configuration options. + /// + /// The created pool will group arrays into buckets, with no more than + /// in each bucket and with those arrays not exceeding in length. + /// + public static ArrayPool Create(int maxArrayLength, int maxArraysPerBucket) + { + return new DefaultArrayPool(maxArrayLength, maxArraysPerBucket); + } + + /// + /// Retrieves a buffer that is at least the requested length. + /// + /// The minimum length of the array needed. + /// + /// An that is at least in length. + /// + /// + /// This buffer is loaned to the caller and should be returned to the same pool via + /// so that it may be reused in subsequent usage of . + /// It is not a fatal error to not return a rented buffer, but failure to do so may lead to + /// decreased application performance, as the pool may need to create a new buffer to replace + /// the one lost. + /// + public abstract T[] Rent(int minimumLength); + + /// + /// Returns to the pool an array that was previously obtained via on the same + /// instance. + /// + /// + /// The buffer previously obtained from to return to the pool. + /// + /// + /// If true and if the pool will store the buffer to enable subsequent reuse, + /// will clear of its contents so that a subsequent consumer via + /// will not see the previous consumer's content. If false or if the pool will release the buffer, + /// the array's contents are left unchanged. + /// + /// + /// Once a buffer has been returned to the pool, the caller gives up all ownership of the buffer + /// and must not use it. The reference returned from a given call to must only be + /// returned via once. The default + /// may hold onto the returned buffer in order to rent it again, or it may release the returned buffer + /// if it's determined that the pool already has enough buffers stored. + /// + public abstract void Return(T[] array, bool clearArray = false); + } +} +#endif \ No newline at end of file diff --git a/src/SharpCompress/Buffers/DefaultArrayPool.cs b/src/SharpCompress/Buffers/DefaultArrayPool.cs new file mode 100644 index 00000000..cb35088e --- /dev/null +++ b/src/SharpCompress/Buffers/DefaultArrayPool.cs @@ -0,0 +1,142 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#if NETCORE +namespace System.Buffers +{ + internal sealed partial class DefaultArrayPool : ArrayPool + { + /// The default maximum length of each array in the pool (2^20). + private const int DefaultMaxArrayLength = 1024 * 1024; + /// The default maximum number of arrays per bucket that are available for rent. + private const int DefaultMaxNumberOfArraysPerBucket = 50; + /// Lazily-allocated empty array used when arrays of length 0 are requested. + private static T[] s_emptyArray; // we support contracts earlier than those with Array.Empty() + + private readonly Bucket[] _buckets; + + internal DefaultArrayPool() : this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket) + { + } + + internal DefaultArrayPool(int maxArrayLength, int maxArraysPerBucket) + { + if (maxArrayLength <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxArrayLength)); + } + if (maxArraysPerBucket <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxArraysPerBucket)); + } + + // Our bucketing algorithm has a min length of 2^4 and a max length of 2^30. + // Constrain the actual max used to those values. + const int MinimumArrayLength = 0x10, MaximumArrayLength = 0x40000000; + if (maxArrayLength > MaximumArrayLength) + { + maxArrayLength = MaximumArrayLength; + } + else if (maxArrayLength < MinimumArrayLength) + { + maxArrayLength = MinimumArrayLength; + } + + // Create the buckets. + int poolId = Id; + int maxBuckets = Utilities.SelectBucketIndex(maxArrayLength); + var buckets = new Bucket[maxBuckets + 1]; + for (int i = 0; i < buckets.Length; i++) + { + buckets[i] = new Bucket(Utilities.GetMaxSizeForBucket(i), maxArraysPerBucket, poolId); + } + _buckets = buckets; + } + + /// Gets an ID for the pool to use with events. + private int Id => GetHashCode(); + + public override T[] Rent(int minimumLength) + { + // Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though + // pooling such an array isn't valuable) as it's a valid length array, and we want the pool + // to be usable in general instead of using `new`, even for computed lengths. + if (minimumLength < 0) + { + throw new ArgumentOutOfRangeException(nameof(minimumLength)); + } + else if (minimumLength == 0) + { + // No need for events with the empty array. Our pool is effectively infinite + // and we'll never allocate for rents and never store for returns. + return s_emptyArray ?? (s_emptyArray = new T[0]); + } + + T[] buffer = null; + + int index = Utilities.SelectBucketIndex(minimumLength); + if (index < _buckets.Length) + { + // Search for an array starting at the 'index' bucket. If the bucket is empty, bump up to the + // next higher bucket and try that one, but only try at most a few buckets. + const int MaxBucketsToTry = 2; + int i = index; + do + { + // Attempt to rent from the bucket. If we get a buffer from it, return it. + buffer = _buckets[i].Rent(); + if (buffer != null) + { + return buffer; + } + } + while (++i < _buckets.Length && i != index + MaxBucketsToTry); + + // The pool was exhausted for this buffer size. Allocate a new buffer with a size corresponding + // to the appropriate bucket. + buffer = new T[_buckets[index]._bufferLength]; + } + else + { + // The request was for a size too large for the pool. Allocate an array of exactly the requested length. + // When it's returned to the pool, we'll simply throw it away. + buffer = new T[minimumLength]; + } + + return buffer; + } + + public override void Return(T[] array, bool clearArray = false) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + else if (array.Length == 0) + { + // Ignore empty arrays. When a zero-length array is rented, we return a singleton + // rather than actually taking a buffer out of the lowest bucket. + return; + } + + // Determine with what bucket this array length is associated + int bucket = Utilities.SelectBucketIndex(array.Length); + + // If we can tell that the buffer was allocated, drop it. Otherwise, check if we have space in the pool + if (bucket < _buckets.Length) + { + // Clear the array if the user requests + if (clearArray) + { + Array.Clear(array, 0, array.Length); + } + + // Return the buffer to its bucket. In the future, we might consider having Return return false + // instead of dropping a bucket, in which case we could try to return to a lower-sized bucket, + // just as how in Rent we allow renting from a higher-sized bucket. + _buckets[bucket].Return(array); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs b/src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs new file mode 100644 index 00000000..d6d2f878 --- /dev/null +++ b/src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#if NETCORE +using System.Diagnostics; +using System.Threading; + +namespace System.Buffers +{ + internal sealed partial class DefaultArrayPool : ArrayPool + { + /// Provides a thread-safe bucket containing buffers that can be Rent'd and Return'd. + private sealed class Bucket + { + internal readonly int _bufferLength; + private readonly T[][] _buffers; + private readonly int _poolId; + + private SpinLock _lock; // do not make this readonly; it's a mutable struct + private int _index; + + /// + /// Creates the pool with numberOfBuffers arrays where each buffer is of bufferLength length. + /// + internal Bucket(int bufferLength, int numberOfBuffers, int poolId) + { + _lock = new SpinLock(Debugger.IsAttached); // only enable thread tracking if debugger is attached; it adds non-trivial overheads to Enter/Exit + _buffers = new T[numberOfBuffers][]; + _bufferLength = bufferLength; + _poolId = poolId; + } + + /// Gets an ID for the bucket to use with events. + internal int Id => GetHashCode(); + + /// Takes an array from the bucket. If the bucket is empty, returns null. + internal T[] Rent() + { + T[][] buffers = _buffers; + T[] buffer = null; + + // While holding the lock, grab whatever is at the next available index and + // update the index. We do as little work as possible while holding the spin + // lock to minimize contention with other threads. The try/finally is + // necessary to properly handle thread aborts on platforms which have them. + bool lockTaken = false, allocateBuffer = false; + try + { + _lock.Enter(ref lockTaken); + + if (_index < buffers.Length) + { + buffer = buffers[_index]; + buffers[_index++] = null; + allocateBuffer = buffer == null; + } + } + finally + { + if (lockTaken) _lock.Exit(false); + } + + // While we were holding the lock, we grabbed whatever was at the next available index, if + // there was one. If we tried and if we got back null, that means we hadn't yet allocated + // for that slot, in which case we should do so now. + if (allocateBuffer) + { + buffer = new T[_bufferLength]; + } + + return buffer; + } + + /// + /// Attempts to return the buffer to the bucket. If successful, the buffer will be stored + /// in the bucket and true will be returned; otherwise, the buffer won't be stored, and false + /// will be returned. + /// + internal void Return(T[] array) + { + // Check to see if the buffer is the correct size for this bucket + if (array.Length != _bufferLength) + { + throw new ArgumentException("Buffer not from pool", nameof(array)); + } + + // While holding the spin lock, if there's room available in the bucket, + // put the buffer into the next available slot. Otherwise, we just drop it. + // The try/finally is necessary to properly handle thread aborts on platforms + // which have them. + bool lockTaken = false; + try + { + _lock.Enter(ref lockTaken); + + if (_index != 0) + { + _buffers[--_index] = array; + } + } + finally + { + if (lockTaken) _lock.Exit(false); + } + } + } + } +} +#endif \ No newline at end of file diff --git a/src/SharpCompress/Buffers/Utilities.cs b/src/SharpCompress/Buffers/Utilities.cs new file mode 100644 index 00000000..be536e1f --- /dev/null +++ b/src/SharpCompress/Buffers/Utilities.cs @@ -0,0 +1,38 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +#if NETCORE +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace System.Buffers +{ + internal static class Utilities + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int SelectBucketIndex(int bufferSize) + { + Debug.Assert(bufferSize > 0); + + uint bitsRemaining = ((uint)bufferSize - 1) >> 4; + + int poolIndex = 0; + if (bitsRemaining > 0xFFFF) { bitsRemaining >>= 16; poolIndex = 16; } + if (bitsRemaining > 0xFF) { bitsRemaining >>= 8; poolIndex += 8; } + if (bitsRemaining > 0xF) { bitsRemaining >>= 4; poolIndex += 4; } + if (bitsRemaining > 0x3) { bitsRemaining >>= 2; poolIndex += 2; } + if (bitsRemaining > 0x1) { bitsRemaining >>= 1; poolIndex += 1; } + + return poolIndex + (int)bitsRemaining; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static int GetMaxSizeForBucket(int binIndex) + { + int maxSize = 16 << binIndex; + Debug.Assert(maxSize >= 0); + return maxSize; + } + } +} +#endif \ No newline at end of file diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 91b0ff1a..58fba814 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -30,13 +30,4 @@ $(DefineConstants);NETCORE - - - - - - - - - \ No newline at end of file diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index b13429b8..075da693 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -1,5 +1,5 @@ using System; -#if NETCORE || NET45 +#if NETCORE using System.Buffers; #endif using System.Collections.Generic; @@ -168,7 +168,7 @@ namespace SharpCompress } finally { -#if NETCORE || NET45 +#if NETCORE ArrayPool.Shared.Return(buffer); #endif } @@ -186,7 +186,7 @@ namespace SharpCompress } finally { -#if NETCORE || NET45 +#if NETCORE ArrayPool.Shared.Return(buffer); #endif } @@ -267,7 +267,7 @@ namespace SharpCompress } finally { -#if NETCORE || NET45 +#if NETCORE ArrayPool.Shared.Return(array); #endif } @@ -292,7 +292,7 @@ namespace SharpCompress } finally { -#if NETCORE || NET45 +#if NETCORE ArrayPool.Shared.Return(array); #endif } @@ -305,7 +305,7 @@ namespace SharpCompress private static byte[] GetTransferByteArray() { -#if NETCORE || NET45 +#if NETCORE return ArrayPool.Shared.Rent(81920); #else return new byte[81920]; From 815f5e09e86a4a5d313b1bf66f534a94b7d1b001 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Fri, 15 Dec 2017 14:46:14 +0000 Subject: [PATCH 019/124] Mark for 0.19.1 --- src/SharpCompress/SharpCompress.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 58fba814..0dd97307 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -2,9 +2,9 @@ SharpCompress - Pure C# Decompression/Compression en-US - 0.19.0 - 0.19.0.0 - 0.19.0.0 + 0.19.1 + 0.19.1.0 + 0.19.1.0 Adam Hathcock net45;net35;netstandard1.0;netstandard1.3;netstandard2.0 true From 57b9133a0f8299f3e0e2878b83f97bbd5b0013d8 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 16 Dec 2017 09:05:21 +0000 Subject: [PATCH 020/124] Change namespace and visibility to avoid collisions (#333) --- src/SharpCompress/Buffers/ArrayPool.cs | 4 ++-- src/SharpCompress/Buffers/DefaultArrayPool.cs | 4 +++- src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs | 3 ++- src/SharpCompress/Buffers/Utilities.cs | 2 +- src/SharpCompress/Utility.cs | 6 +++--- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/SharpCompress/Buffers/ArrayPool.cs b/src/SharpCompress/Buffers/ArrayPool.cs index 0ef99b2e..d81ed4c0 100644 --- a/src/SharpCompress/Buffers/ArrayPool.cs +++ b/src/SharpCompress/Buffers/ArrayPool.cs @@ -5,7 +5,7 @@ using System.Runtime.CompilerServices; using System.Threading; -namespace System.Buffers +namespace SharpCompress.Buffers { /// /// Provides a resource pool that enables reusing instances of type . @@ -20,7 +20,7 @@ namespace System.Buffers /// This class is thread-safe. All members may be used by multiple threads concurrently. /// /// - public abstract class ArrayPool + internal abstract class ArrayPool { /// The lazily-initialized shared pool instance. private static ArrayPool s_sharedInstance = null; diff --git a/src/SharpCompress/Buffers/DefaultArrayPool.cs b/src/SharpCompress/Buffers/DefaultArrayPool.cs index cb35088e..43cd2c37 100644 --- a/src/SharpCompress/Buffers/DefaultArrayPool.cs +++ b/src/SharpCompress/Buffers/DefaultArrayPool.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #if NETCORE -namespace System.Buffers +using System; + +namespace SharpCompress.Buffers { internal sealed partial class DefaultArrayPool : ArrayPool { diff --git a/src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs b/src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs index d6d2f878..30124889 100644 --- a/src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs +++ b/src/SharpCompress/Buffers/DefaultArrayPoolBucket.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. #if NETCORE +using System; using System.Diagnostics; using System.Threading; -namespace System.Buffers +namespace SharpCompress.Buffers { internal sealed partial class DefaultArrayPool : ArrayPool { diff --git a/src/SharpCompress/Buffers/Utilities.cs b/src/SharpCompress/Buffers/Utilities.cs index be536e1f..f4100e37 100644 --- a/src/SharpCompress/Buffers/Utilities.cs +++ b/src/SharpCompress/Buffers/Utilities.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; -namespace System.Buffers +namespace SharpCompress.Buffers { internal static class Utilities { diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 075da693..751dd29d 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -1,10 +1,10 @@ using System; -#if NETCORE -using System.Buffers; -#endif using System.Collections.Generic; using System.IO; using System.Linq; +#if NETCORE +using SharpCompress.Buffers; +#endif using SharpCompress.Readers; namespace SharpCompress From 02f68b793c664c7d5f6dd4bbfcd06b9568d9e19e Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 16 Dec 2017 09:08:17 +0000 Subject: [PATCH 021/124] Mark for 0.19.2 --- src/SharpCompress/SharpCompress.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 0dd97307..cb2c3144 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -2,9 +2,9 @@ SharpCompress - Pure C# Decompression/Compression en-US - 0.19.1 - 0.19.1.0 - 0.19.1.0 + 0.19.2 + 0.19.2.0 + 0.19.2.0 Adam Hathcock net45;net35;netstandard1.0;netstandard1.3;netstandard2.0 true From d334b54846a47b82f8fa36251f9eccbcb7b83f53 Mon Sep 17 00:00:00 2001 From: coderb Date: Sat, 16 Dec 2017 12:54:32 -0500 Subject: [PATCH 022/124] rar5: add test archives --- .../TestArchives/Archives/Rar5.crc_blake2.rar | Bin 0 -> 60314 bytes .../Archives/Rar5.encrypted_filesAndHeader.rar | Bin 0 -> 60686 bytes .../Archives/Rar5.encrypted_filesOnly.rar | Bin 0 -> 60336 bytes .../Archives/Rar5.multi.part01.rar | Bin 0 -> 10240 bytes .../Archives/Rar5.multi.part02.rar | Bin 0 -> 10240 bytes .../Archives/Rar5.multi.part03.rar | Bin 0 -> 10240 bytes .../Archives/Rar5.multi.part04.rar | Bin 0 -> 10240 bytes .../Archives/Rar5.multi.part05.rar | Bin 0 -> 10240 bytes .../Archives/Rar5.multi.part06.rar | Bin 0 -> 9696 bytes tests/TestArchives/Archives/Rar5.none.rar | Bin 0 -> 101393 bytes tests/TestArchives/Archives/Rar5.rar | Bin 0 -> 60066 bytes tests/TestArchives/Archives/Rar5.solid.rar | Bin 0 -> 60046 bytes 12 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/TestArchives/Archives/Rar5.crc_blake2.rar create mode 100644 tests/TestArchives/Archives/Rar5.encrypted_filesAndHeader.rar create mode 100644 tests/TestArchives/Archives/Rar5.encrypted_filesOnly.rar create mode 100644 tests/TestArchives/Archives/Rar5.multi.part01.rar create mode 100644 tests/TestArchives/Archives/Rar5.multi.part02.rar create mode 100644 tests/TestArchives/Archives/Rar5.multi.part03.rar create mode 100644 tests/TestArchives/Archives/Rar5.multi.part04.rar create mode 100644 tests/TestArchives/Archives/Rar5.multi.part05.rar create mode 100644 tests/TestArchives/Archives/Rar5.multi.part06.rar create mode 100644 tests/TestArchives/Archives/Rar5.none.rar create mode 100644 tests/TestArchives/Archives/Rar5.rar create mode 100644 tests/TestArchives/Archives/Rar5.solid.rar diff --git a/tests/TestArchives/Archives/Rar5.crc_blake2.rar b/tests/TestArchives/Archives/Rar5.crc_blake2.rar new file mode 100644 index 0000000000000000000000000000000000000000..2e89e107c4e3f783a47caba92d7166e8c6771af3 GIT binary patch literal 60314 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 literal 0 HcmV?d00001 diff --git a/tests/TestArchives/Archives/Rar5.encrypted_filesAndHeader.rar b/tests/TestArchives/Archives/Rar5.encrypted_filesAndHeader.rar new file mode 100644 index 0000000000000000000000000000000000000000..d0bad6d438e329028acc65b2f16ae8aba513da0f GIT binary patch literal 60686 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�#g{akPlzqM^iJ^y$E@DHzovy&#mq9; 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

;yD&cI}LR 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 ich%~iM&HI`3!*+cg``eG(2EZSgSBCyn&g4 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 literal 0 HcmV?d00001 diff --git a/tests/TestArchives/Archives/Rar5.multi.part02.rar b/tests/TestArchives/Archives/Rar5.multi.part02.rar new file mode 100644 index 0000000000000000000000000000000000000000..d23d82e4c45c79ea7d1086517504a819c66f66e5 GIT binary patch literal 10240 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 literal 0 HcmV?d00001 diff --git a/tests/TestArchives/Archives/Rar5.multi.part03.rar b/tests/TestArchives/Archives/Rar5.multi.part03.rar new file mode 100644 index 0000000000000000000000000000000000000000..159c55d807a09ae035cb67e1eedc5aaf236c8313 GIT binary patch literal 10240 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 BFK7S& literal 0 HcmV?d00001 diff --git a/tests/TestArchives/Archives/Rar5.multi.part04.rar b/tests/TestArchives/Archives/Rar5.multi.part04.rar new file mode 100644 index 0000000000000000000000000000000000000000..cb0ed618847510fb77e571abebf29a13c899b6c5 GIT binary patch literal 10240 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 vyfUWOo{yPr2Lr>vj)RV`gPxWPkwxwT=+* literal 0 HcmV?d00001 diff --git a/tests/TestArchives/Archives/Rar5.multi.part05.rar b/tests/TestArchives/Archives/Rar5.multi.part05.rar new file mode 100644 index 0000000000000000000000000000000000000000..ec132ab8e5a1f3bcb11484de8e6523c224141867 GIT binary patch literal 10240 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 literal 0 HcmV?d00001 diff --git a/tests/TestArchives/Archives/Rar5.multi.part06.rar b/tests/TestArchives/Archives/Rar5.multi.part06.rar new file mode 100644 index 0000000000000000000000000000000000000000..8b90861086cd1e39d0f0b3a1932ecb6d5d7a044b GIT binary patch literal 9696 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 L9u~;V%EABub8Xi_ literal 0 HcmV?d00001 diff --git a/tests/TestArchives/Archives/Rar5.none.rar b/tests/TestArchives/Archives/Rar5.none.rar new file mode 100644 index 0000000000000000000000000000000000000000..276234c5f1a425b33d8d981f8eb0966408976f0f GIT binary patch literal 101393 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 q2M0)FRzW&MBg7HGEpNI&j+iIEB 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 RZ@NH^kSz}jWM*Yy004WVd&B?$ literal 0 HcmV?d00001 From c346a4ca948c5137a1adf511b243bfb4df7a708e Mon Sep 17 00:00:00 2001 From: coderb Date: Sat, 16 Dec 2017 17:35:07 -0500 Subject: [PATCH 023/124] rar5: change rar MarkHeader detection logic to support rar5 --- src/SharpCompress/Archives/Rar/RarArchive.cs | 5 +- .../Archives/Rar/RarArchiveVolumeFactory.cs | 2 +- .../Common/Rar/Headers/MarkHeader.cs | 98 +++++++++++++--- .../Common/Rar/Headers/RarHeader.cs | 12 +- .../Common/Rar/Headers/RarHeaderFactory.cs | 109 +++--------------- .../Common/Rar/RarCrcBinaryReader.cs | 7 ++ .../Common/Rar/RarCryptoBinaryReader.cs | 39 ++++++- src/SharpCompress/Common/Rar/RarVolume.cs | 2 +- src/SharpCompress/IO/MarkingBinaryReader.cs | 41 ++++++- .../SharpCompress.Test/Rar/RarArchiveTests.cs | 8 +- .../Rar/RarHeaderFactoryTest.cs | 4 - 11 files changed, 189 insertions(+), 138 deletions(-) diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index eecd27b6..adb9117c 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -134,9 +134,8 @@ namespace SharpCompress.Archives.Rar { try { - var headerFactory = new RarHeaderFactory(StreamingMode.Seekable, options ?? new ReaderOptions()); - var markHeader = headerFactory.ReadHeaders(stream).FirstOrDefault() as MarkHeader; - return markHeader != null && markHeader.IsValid(); + MarkHeader.Read(stream, true, false); + return true; } catch { diff --git a/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs b/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs index 4bf39967..7270a4bd 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs @@ -56,7 +56,7 @@ namespace SharpCompress.Archives.Rar return null; } bool oldNumbering = !ah.ArchiveHeaderFlags.HasFlag(ArchiveFlags.NEWNUMBERING) - || currentFilePart.MarkHeader.OldFormat; + || currentFilePart.MarkHeader.OldNumberingFormat; if (oldNumbering) { return FindNextFileWithOldNumbering(currentFilePart.FileInfo); diff --git a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs index f5c61274..364202fc 100644 --- a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs @@ -1,26 +1,92 @@ -using SharpCompress.IO; +using System; +using System.IO; namespace SharpCompress.Common.Rar.Headers { - internal class MarkHeader : RarHeader + internal class MarkHeader : IRarHeader { - protected override void ReadFromReader(MarkingBinaryReader reader) - { + private const int MAX_SFX_SIZE = 0x80000 - 16; //archive.cpp line 136 + + internal bool OldNumberingFormat { get; private set; } + public bool IsRar5 { get; private set; } + + private MarkHeader(bool isRar5) { + IsRar5 = isRar5; } - internal bool IsValid() - { - // Rar old signature: 52 45 7E 5E (not supported) - - // Rar4 signature: 52 61 72 21 1A 07 00 - return HeadCRC == 0x6152 && - HeaderType == HeaderType.MarkHeader && - Flags == 0x1A21 && - HeaderSize == 0x07; - - // Rar5 signature: 52 61 72 21 1A 07 01 00 (not supported yet) + public HeaderType HeaderType { + get { return HeaderType.MarkHeader; } } - internal bool OldFormat { get; private set; } + private static byte GetByte(Stream stream) { + var b = stream.ReadByte(); + if (b != -1) { + return (byte)b; + } + throw new EndOfStreamException(); + } + + public static MarkHeader Read(Stream stream, bool leaveStreamOpen, bool lookForHeader) { + int maxScanIndex = lookForHeader ? MAX_SFX_SIZE : 0; + try + { + int start = -1; + var b = GetByte(stream); start++; + while (start <= maxScanIndex) { + // Rar old signature: 52 45 7E 5E + // Rar4 signature: 52 61 72 21 1A 07 00 + // Rar5 signature: 52 61 72 21 1A 07 01 00 + if (b == 0x52) { + b = GetByte(stream); start++; + if (b == 0x61) { + b = GetByte(stream); start++; + if (b != 0x72) continue; + b = GetByte(stream); start++; + if (b != 0x21) continue; + b = GetByte(stream); start++; + if (b != 0x1a) continue; + b = GetByte(stream); start++; + if (b != 0x07) continue; + + b = GetByte(stream); start++; + if (b == 1) { + b = GetByte(stream); start++; + if (b != 0) continue; + return new MarkHeader(true); // Rar5 + } else if (b == 0) { + return new MarkHeader(false); // Rar4 + } else { + continue; + } + } else if (b == 0x45) { + b = GetByte(stream); start++; + if (b != 0x7e) continue; + b = GetByte(stream); start++; + if (b != 0x5e) continue; + throw new InvalidFormatException("Rar format version pre-4 is unsupported."); + //return new MarkHeader(); + } else { + continue; + } + } else { + b = GetByte(stream); start++; + continue; + } + + // unreachable + throw new InvalidOperationException(); + } + } + catch (Exception e) + { + if (!leaveStreamOpen) + { + stream.Dispose(); + } + throw new InvalidFormatException("Error trying to read rar signature.", e); + } + + throw new InvalidFormatException("Rar signature not found"); + } } } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index 30753aee..55434c94 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -1,11 +1,10 @@ using System; using System.IO; using SharpCompress.IO; -using System.Text; namespace SharpCompress.Common.Rar.Headers { - internal class RarHeader + internal class RarHeader : IRarHeader { internal const short BaseBlockSize = 7; internal const short LONG_BLOCK = -0x8000; @@ -25,13 +24,11 @@ namespace SharpCompress.Common.Rar.Headers { try { - RarHeader header = new RarHeader(); - + var header = new RarHeader(); header.ArchiveEncoding = archiveEncoding; reader.Mark(); header.ReadStartFromReader(reader); header.ReadBytes += reader.CurrentReadByteCount; - return header; } catch (EndOfStreamException) @@ -44,7 +41,7 @@ namespace SharpCompress.Common.Rar.Headers { HeadCRC = reader.ReadUInt16(); reader.ResetCrc(); - HeaderType = (HeaderType)(reader.ReadByte() & 0xff); + HeaderType = (HeaderType)reader.ReadByte(); Flags = reader.ReadInt16(); HeaderSize = reader.ReadInt16(); if (FlagUtility.HasFlag(Flags, LONG_BLOCK)) @@ -69,7 +66,6 @@ namespace SharpCompress.Common.Rar.Headers header.ReadBytes += reader.CurrentReadByteCount; int headerSizeDiff = header.HeaderSize - (int)header.ReadBytes; - if (headerSizeDiff > 0) { reader.ReadBytes(headerSizeDiff); @@ -102,7 +98,7 @@ namespace SharpCompress.Common.Rar.Headers protected ushort HeadCRC { get; private set; } - internal HeaderType HeaderType { get; private set; } + public HeaderType HeaderType { get; private set; } ///

/// Untyped flags. These should be typed when Promoting to another header diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index d05840ee..66d4c024 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -8,7 +8,7 @@ namespace SharpCompress.Common.Rar.Headers { internal class RarHeaderFactory { - private const int MAX_SFX_SIZE = 0x80000 - 16; //archive.cpp line 136 + private bool isRar5; internal RarHeaderFactory(StreamingMode mode, ReaderOptions options) { @@ -20,12 +20,11 @@ namespace SharpCompress.Common.Rar.Headers internal StreamingMode StreamingMode { get; } internal bool IsEncrypted { get; private set; } - internal IEnumerable ReadHeaders(Stream stream) + internal IEnumerable ReadHeaders(Stream stream) { - if (Options.LookForHeader) - { - stream = CheckSFX(stream); - } + var markHeader = MarkHeader.Read(stream, Options.LeaveStreamOpen, Options.LookForHeader); + this.isRar5 = markHeader.IsRar5; + yield return markHeader; RarHeader header; while ((header = ReadNextHeader(stream)) != null) @@ -38,102 +37,24 @@ namespace SharpCompress.Common.Rar.Headers } } - private Stream CheckSFX(Stream stream) - { - RewindableStream rewindableStream = GetRewindableStream(stream); - stream = rewindableStream; - BinaryReader reader = new BinaryReader(rewindableStream); - try - { - int count = 0; - while (true) - { - byte firstByte = reader.ReadByte(); - if (firstByte == 0x52) - { - MemoryStream buffer = new MemoryStream(); - byte[] nextThreeBytes = reader.ReadBytes(3); - if ((nextThreeBytes[0] == 0x45) - && (nextThreeBytes[1] == 0x7E) - && (nextThreeBytes[2] == 0x5E)) - { - //old format and isvalid - buffer.WriteByte(0x52); - buffer.Write(nextThreeBytes, 0, 3); - rewindableStream.Rewind(buffer); - break; - } - byte[] secondThreeBytes = reader.ReadBytes(3); - if ((nextThreeBytes[0] == 0x61) - && (nextThreeBytes[1] == 0x72) - && (nextThreeBytes[2] == 0x21) - && (secondThreeBytes[0] == 0x1A) - && (secondThreeBytes[1] == 0x07) - && (secondThreeBytes[2] == 0x00)) - { - //new format and isvalid - buffer.WriteByte(0x52); - buffer.Write(nextThreeBytes, 0, 3); - buffer.Write(secondThreeBytes, 0, 3); - rewindableStream.Rewind(buffer); - break; - } - buffer.Write(nextThreeBytes, 0, 3); - buffer.Write(secondThreeBytes, 0, 3); - rewindableStream.Rewind(buffer); - } - if (count > MAX_SFX_SIZE) - { - break; - } - } - } - catch (Exception e) - { - if (!Options.LeaveStreamOpen) - { -#if NET35 - reader.Close(); -#else - reader.Dispose(); -#endif - } - throw new InvalidFormatException("Error trying to read rar signature.", e); - } - return stream; - } - - private RewindableStream GetRewindableStream(Stream stream) - { - RewindableStream rewindableStream = stream as RewindableStream; - if (rewindableStream == null) - { - rewindableStream = new RewindableStream(stream); - } - return rewindableStream; - } - private RarHeader ReadNextHeader(Stream stream) { + RarCrcBinaryReader reader; + if (!IsEncrypted) { + reader = new RarCrcBinaryReader(stream); + } else { #if !NO_CRYPTO - var reader = new RarCryptoBinaryReader(stream, Options.Password); - - if (IsEncrypted) - { if (Options.Password == null) { throw new CryptographicException("Encrypted Rar archive has no password specified."); } - reader.SkipQueue(); - byte[] salt = reader.ReadBytes(8); - reader.InitializeAes(salt); - } + reader = new RarCryptoBinaryReader(stream, Options.Password); #else - var reader = new RarCrcBinaryReader(stream); - + throw new CryptographicException("Rar encryption unsupported on this platform"); #endif + } - RarHeader header = RarHeader.Create(reader, Options.ArchiveEncoding); + var header = RarHeader.Create(reader, Options.ArchiveEncoding); if (header == null) { return null; @@ -146,10 +67,6 @@ namespace SharpCompress.Common.Rar.Headers IsEncrypted = ah.HasPassword; return ah; } - case HeaderType.MarkHeader: - { - return header.PromoteHeader(reader); - } case HeaderType.ProtectHeader: { diff --git a/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs index 6df617b5..aa8c73fd 100644 --- a/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs @@ -30,6 +30,13 @@ namespace SharpCompress.Common.Rar { return base.ReadBytes(count); } + public override byte ReadByte() + { + var b = base.ReadByte(); + currentCrc = RarCRC.CheckCrc(currentCrc, b); + return b; + } + public override byte[] ReadBytes(int count) { var result = base.ReadBytes(count); diff --git a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs index 9635375b..4a14a375 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs @@ -2,7 +2,6 @@ #if !NO_CRYPTO using System.Collections.Generic; using System.IO; -using SharpCompress.IO; namespace SharpCompress.Common.Rar { @@ -18,6 +17,10 @@ namespace SharpCompress.Common.Rar : base(stream) { this.password = password; + // coderb: not sure why this was being done at this logical point + //SkipQueue(); + byte[] salt = ReadBytes(8); + InitializeAes(salt); } // track read count ourselves rather than using the underlying stream since we buffer @@ -47,6 +50,36 @@ namespace SharpCompress.Common.Rar rijndael = RarRijndael.InitializeFrom(password, salt); } + public override byte ReadByte() { + if (UseEncryption) + { + return ReadAndDecryptBytes(1)[0]; + } + this.readCount++; + return base.ReadByte(); + } +//x +// public override byte ReadByte() { +// if (UseEncryption) +// { +// int queueSize = this.data.Count; +// if (queueSize < 1) +// { +// byte[] cipherText = ReadBytesNoCrc(16); +// var readBytes = this.rijndael.ProcessBlock(cipherText); +// foreach (var readByte in readBytes) +// this.data.Enqueue(readByte); +// } +// +// var b = this.data.Dequeue(); +// UpdateCrc(b); +// this.readCount++; +// return b; +// } +// this.readCount++; +// return base.ReadByte(); +// } + public override byte[] ReadBytes(int count) { if (UseEncryption) @@ -68,13 +101,11 @@ namespace SharpCompress.Common.Rar for (int i = 0; i < alignedSize / 16; i++) { //long ax = System.currentTimeMillis(); - byte[] cipherText = base.ReadBytesNoCrc(16); + byte[] cipherText = ReadBytesNoCrc(16); var readBytes = rijndael.ProcessBlock(cipherText); foreach (var readByte in readBytes) data.Enqueue(readByte); - } - } var decryptedBytes = new byte[count]; diff --git a/src/SharpCompress/Common/Rar/RarVolume.cs b/src/SharpCompress/Common/Rar/RarVolume.cs index 89fee688..4bed1696 100644 --- a/src/SharpCompress/Common/Rar/RarVolume.cs +++ b/src/SharpCompress/Common/Rar/RarVolume.cs @@ -30,7 +30,7 @@ namespace SharpCompress.Common.Rar internal IEnumerable GetVolumeFileParts() { MarkHeader previousMarkHeader = null; - foreach (RarHeader header in headerFactory.ReadHeaders(Stream)) + foreach (var header in headerFactory.ReadHeaders(Stream)) { switch (header.HeaderType) { diff --git a/src/SharpCompress/IO/MarkingBinaryReader.cs b/src/SharpCompress/IO/MarkingBinaryReader.cs index aa7c820b..42163d53 100644 --- a/src/SharpCompress/IO/MarkingBinaryReader.cs +++ b/src/SharpCompress/IO/MarkingBinaryReader.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Linq; using SharpCompress.Converters; namespace SharpCompress.IO @@ -36,12 +35,22 @@ namespace SharpCompress.IO public override bool ReadBoolean() { - return ReadBytes(1).Single() != 0; + return ReadByte() != 0; } + // NOTE: there is a somewhat fragile dependency on the internals of this class + // with RarCrcBinaryReader and RarCryptoBinaryReader. + // + // RarCrcBinaryReader/RarCryptoBinaryReader need to override any specific methods + // that call directly to the base BinaryReader and do not delegate to other methods + // in this class so that it can track the each byte being read. + // + // if altering this class in a way that changes the implementation be sure to + // update RarCrcBinaryReader/RarCryptoBinaryReader. public override byte ReadByte() { - return ReadBytes(1).Single(); + CurrentReadByteCount++; + return base.ReadByte(); } public override byte[] ReadBytes(int count) @@ -121,5 +130,31 @@ namespace SharpCompress.IO { return DataConverter.LittleEndian.GetUInt64(ReadBytes(8), 0); } + + // RAR5 style variable length encoded value + // maximum value of 0xffffffffffffffff (64 bits) + // implies max 10 bytes consumed + // + // Variable length integer. Can include one or more bytes, where lower 7 bits of every byte contain integer data + // and highest bit in every byte is the continuation flag. If highest bit is 0, this is the last byte in sequence. + // So first byte contains 7 least significant bits of integer and continuation flag. Second byte, if present, + // contains next 7 bits and so on. + public ulong ReadRarVInt() { + int shift = 0; + ulong result = 0; + do { + ulong n = ReadByte(); + result |= n << shift; + shift += 7; + if ((n & 0x80) == 0) { + return result; + } + // note: we're actually only allowing a max high bit of 2^56 + // to avoid an extra complex check for shift overflow due to the + // 10th byte having high bits set + } while (shift < 63); + + throw new FormatException("malformed vint"); + } } } \ No newline at end of file diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index e227a74e..55d79994 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -14,14 +14,12 @@ namespace SharpCompress.Test.Rar public void Rar_EncryptedFileAndHeader_Archive() { ReadRarPassword("Rar.encrypted_filesAndHeader.rar", "test"); - } [Fact] public void Rar_EncryptedFileOnly_Archive() { ReadRarPassword("Rar.encrypted_filesOnly.rar", "test"); - } [Fact] @@ -209,6 +207,12 @@ namespace SharpCompress.Test.Rar ArchiveFileRead("Rar.none.rar"); } + [Fact] + public void Rar5NoneArchiveFileRead() + { + ArchiveFileRead("Rar5.none.rar"); + } + [Fact] public void Rar_ArchiveFileRead() { diff --git a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs index 29309cff..9d5b07d2 100644 --- a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs +++ b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs @@ -26,11 +26,7 @@ namespace SharpCompress.Test.Rar [Fact] public void ReadHeaders_RecognizeEncryptedFlag() { - ReadEncryptedFlag("Rar.encrypted_filesAndHeader.rar", true); - - - } private void ReadEncryptedFlag(string testArchive, bool isEncrypted) From 9f549b98da822151cde78d53e2d09ff585ec4ddc Mon Sep 17 00:00:00 2001 From: coderb Date: Sat, 16 Dec 2017 20:39:53 -0500 Subject: [PATCH 024/124] rar5: support ArchiveHeader, additional rar5 header implementation --- .../Archives/Rar/RarArchiveVolumeFactory.cs | 6 +- .../Common/Rar/Headers/ArchiveHeader.cs | 82 ++++++++++++++-- .../Common/Rar/Headers/FileHeader.cs | 2 +- src/SharpCompress/Common/Rar/Headers/Flags.cs | 50 +++++++--- .../Common/Rar/Headers/MarkHeader.cs | 1 + .../Common/Rar/Headers/ProtectHeader.cs | 2 +- .../Common/Rar/Headers/RarHeader.cs | 97 ++++++++++++++----- .../Common/Rar/Headers/RarHeaderFactory.cs | 6 +- .../Common/Rar/RarCrcBinaryReader.cs | 4 +- src/SharpCompress/Common/Rar/RarVolume.cs | 7 +- src/SharpCompress/IO/MarkingBinaryReader.cs | 44 ++++++++- 11 files changed, 239 insertions(+), 62 deletions(-) diff --git a/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs b/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs index 7270a4bd..091f24ae 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs @@ -33,11 +33,11 @@ namespace SharpCompress.Archives.Rar FileInfoRarArchiveVolume part = new FileInfoRarArchiveVolume(fileInfo, options); yield return part; - if (!part.ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.VOLUME)) + ArchiveHeader ah = part.ArchiveHeader; + if (!ah.IsVolume) { yield break; //if file isn't volume then there is no reason to look } - ArchiveHeader ah = part.ArchiveHeader; fileInfo = GetNextFileInfo(ah, part.FileParts.FirstOrDefault() as FileInfoRarFilePart); //we use fileinfo because rar is dumb and looks at file names rather than archive info for another volume while (fileInfo != null && fileInfo.Exists) @@ -55,7 +55,7 @@ namespace SharpCompress.Archives.Rar { return null; } - bool oldNumbering = !ah.ArchiveHeaderFlags.HasFlag(ArchiveFlags.NEWNUMBERING) + bool oldNumbering = ah.OldNumberingFormat || currentFilePart.MarkHeader.OldNumberingFormat; if (oldNumbering) { diff --git a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs index 214a25c0..82a32910 100644 --- a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs @@ -1,3 +1,4 @@ +using System; using SharpCompress.IO; namespace SharpCompress.Common.Rar.Headers @@ -7,24 +8,91 @@ namespace SharpCompress.Common.Rar.Headers internal const short mainHeaderSizeWithEnc = 7; internal const short mainHeaderSize = 6; + public ArchiveHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader) { + } + protected override void ReadFromReader(MarkingBinaryReader reader) { - HighPosAv = reader.ReadInt16(); - PosAv = reader.ReadInt32(); - if (ArchiveHeaderFlags.HasFlag(ArchiveFlags.ENCRYPTVER)) + if (this.isRar5) { - EncryptionVersion = reader.ReadByte(); + ArchiveHeaderFlags = reader.ReadRarVIntUInt16(); + if ((ArchiveHeaderFlags & ArchiveFlagsV5.HasVolumeNumber) == ArchiveFlagsV5.HasVolumeNumber) + { + VolumeNumber = (int)reader.ReadRarVIntUInt32(); + } + if (ExtraSize != 0) { + ReadLocator(reader); + } + } else { + ArchiveHeaderFlags = Flags; + HighPosAv = reader.ReadInt16(); + PosAv = reader.ReadInt32(); + if ((ArchiveHeaderFlags & ArchiveFlagsV4.EncryptVer) == ArchiveFlagsV4.EncryptVer) + { + EncryptionVersion = reader.ReadByte(); + } } } - internal ArchiveFlags ArchiveHeaderFlags => (ArchiveFlags)Flags; + private void ReadLocator(MarkingBinaryReader reader) { + var size = reader.ReadRarVIntUInt16(); + var type = reader.ReadRarVIntUInt16(); + if (type != 1) throw new InvalidFormatException("expected locator record"); + var flags = reader.ReadRarVIntUInt16(); + const ushort HasQuickOpenOffset = 0x01; + const ushort HasRecoveryOffset = 0x02; + ulong quickOpenOffset = 0; + if ((flags & HasQuickOpenOffset) == HasQuickOpenOffset) { + quickOpenOffset = reader.ReadRarVInt(); + } + ulong recoveryOffset = 0; + if ((flags & HasRecoveryOffset) == HasRecoveryOffset) { + recoveryOffset = reader.ReadRarVInt(); + } + } + + internal ushort ArchiveHeaderFlags { get; private set; } + + internal int VolumeNumber { get; private set; } internal short HighPosAv { get; private set; } internal int PosAv { get; private set; } - internal byte EncryptionVersion { get; private set; } + private byte EncryptionVersion { get; set; } - public bool HasPassword => ArchiveHeaderFlags.HasFlag(ArchiveFlags.PASSWORD); + public bool HasPassword => (ArchiveHeaderFlags & ArchiveFlagsV4.Password) == ArchiveFlagsV4.Password; + + public bool OldNumberingFormat { + get { + return !this.isRar5 && + ((ArchiveHeaderFlags & ArchiveFlagsV4.NewNumbering) != ArchiveFlagsV4.NewNumbering); + } + } + + public bool IsVolume { + get { + return this.isRar5 + ? ((ArchiveHeaderFlags & ArchiveFlagsV5.Volume) == ArchiveFlagsV5.Volume) + : ((ArchiveHeaderFlags & ArchiveFlagsV4.Volume) == ArchiveFlagsV4.Volume); + } + } + + public bool IsFirstVolume { + get { + return this.isRar5 + ? VolumeNumber == 1 + : ((ArchiveHeaderFlags & ArchiveFlagsV4.FirstVolume) == ArchiveFlagsV4.FirstVolume); + } + } + + public bool IsSolid { + get { + return this.isRar5 + ? ((ArchiveHeaderFlags & ArchiveFlagsV5.Solid) == ArchiveFlagsV5.Solid) + : ((ArchiveHeaderFlags & ArchiveFlagsV4.Solid) == ArchiveFlagsV4.Solid); + } + } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index 1b539eb7..335f343c 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -42,7 +42,7 @@ namespace SharpCompress.Common.Rar.Headers highUncompressedkSize = int.MaxValue; } } - CompressedSize = UInt32To64(highCompressedSize, AdditionalSize); + CompressedSize = UInt32To64(highCompressedSize, AdditionalDataSize); UncompressedSize = UInt32To64(highUncompressedkSize, lowUncompressedSize); nameSize = nameSize > 4 * 1024 ? (short)(4 * 1024) : nameSize; diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index a2dbd550..09c58083 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -2,8 +2,14 @@ using System; namespace SharpCompress.Common.Rar.Headers { - internal enum HeaderType + internal enum HeaderType : byte { + Rar5ArchiveHeader = 0x01, + Rar5FileHeader = 0x02, + Rar5ServiceHeader = 0x03, + Rar5EncryptionHeader = 0x04, + Rar5EndArchiveHeader = 0x05, + MarkHeader = 0x72, ArchiveHeader = 0x73, FileHeader = 0x74, @@ -16,24 +22,38 @@ namespace SharpCompress.Common.Rar.Headers EndArchiveHeader = 0x7b } - internal enum HeaderFlags : short + internal static class HeaderFlagsV4 { - LONG_BLOCK = -0x8000 + public const ushort HasData = 0x8000; } - [Flags] - internal enum ArchiveFlags + internal static class HeaderFlagsV5 { - VOLUME = 0x0001, - COMMENT = 0x0002, - LOCK = 0x0004, - SOLID = 0x0008, - NEWNUMBERING = 0x0010, - AV = 0x0020, - PROTECT = 0x0040, - PASSWORD = 0x0080, - FIRSTVOLUME = 0x0100, - ENCRYPTVER = 0x0200 + public const ushort HasExtra = 0x01; + public const ushort HasData = 0x02; + } + + internal static class ArchiveFlagsV4 + { + public const ushort Volume = 0x0001; + public const ushort Comment = 0x0002; + public const ushort Lock = 0x0004; + public const ushort Solid = 0x0008; + public const ushort NewNumbering = 0x0010; + public const ushort AV = 0x0020; + public const ushort Protect = 0x0040; + public const ushort Password = 0x0080; + public const ushort FirstVolume = 0x0100; + public const ushort EncryptVer = 0x0200; + } + + internal static class ArchiveFlagsV5 + { + public const ushort Volume = 0x0001; + public const ushort HasVolumeNumber = 0x0002; + public const ushort Solid = 0x0004; + public const ushort Protect = 0x0008; + public const ushort Lock = 0x0010; } internal enum HostOS diff --git a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs index 364202fc..30225bc7 100644 --- a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs @@ -8,6 +8,7 @@ namespace SharpCompress.Common.Rar.Headers private const int MAX_SFX_SIZE = 0x80000 - 16; //archive.cpp line 136 internal bool OldNumberingFormat { get; private set; } + public bool IsRar5 { get; private set; } private MarkHeader(bool isRar5) { diff --git a/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs b/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs index e2f78c32..c47c5676 100644 --- a/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs @@ -13,7 +13,7 @@ namespace SharpCompress.Common.Rar.Headers Mark = reader.ReadBytes(8); } - internal uint DataSize => AdditionalSize; + internal uint DataSize => AdditionalDataSize; internal byte Version { get; private set; } internal ushort RecSectors { get; private set; } internal uint TotalBlocks { get; private set; } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index 55434c94..310e8bbf 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -6,8 +6,32 @@ namespace SharpCompress.Common.Rar.Headers { internal class RarHeader : IRarHeader { - internal const short BaseBlockSize = 7; - internal const short LONG_BLOCK = -0x8000; + protected readonly bool isRar5; + + protected RarHeader() { + //TODO + //throw new NotImplementedException(); + } + + private RarHeader(bool isRar5) + { + this.isRar5 = isRar5; + } + + protected RarHeader(RarHeader header, RarCrcBinaryReader reader) { + this.isRar5 = header.isRar5; + FillBase(header); + ReadFromReader(reader); + ReadBytes = reader.CurrentReadByteCount; + + int headerSizeDiff = HeaderSize - (int)ReadBytes; + if (headerSizeDiff > 0) + { + reader.ReadBytes(headerSizeDiff); + } + + VerifyHeaderCrc(reader.GetCrc32()); + } private void FillBase(RarHeader baseHeader) { @@ -15,18 +39,18 @@ namespace SharpCompress.Common.Rar.Headers HeaderType = baseHeader.HeaderType; Flags = baseHeader.Flags; HeaderSize = baseHeader.HeaderSize; - AdditionalSize = baseHeader.AdditionalSize; + ExtraSize = baseHeader.ExtraSize; + AdditionalDataSize = baseHeader.AdditionalDataSize; ReadBytes = baseHeader.ReadBytes; ArchiveEncoding = baseHeader.ArchiveEncoding; } - internal static RarHeader Create(RarCrcBinaryReader reader, ArchiveEncoding archiveEncoding) + internal static RarHeader ReadBase(RarCrcBinaryReader reader, ArchiveEncoding archiveEncoding, bool isRar5) { try { - var header = new RarHeader(); + var header = new RarHeader(isRar5); header.ArchiveEncoding = archiveEncoding; - reader.Mark(); header.ReadStartFromReader(reader); header.ReadBytes += reader.CurrentReadByteCount; return header; @@ -39,14 +63,33 @@ namespace SharpCompress.Common.Rar.Headers private void ReadStartFromReader(RarCrcBinaryReader reader) { - HeadCRC = reader.ReadUInt16(); - reader.ResetCrc(); - HeaderType = (HeaderType)reader.ReadByte(); - Flags = reader.ReadInt16(); - HeaderSize = reader.ReadInt16(); - if (FlagUtility.HasFlag(Flags, LONG_BLOCK)) + if (this.isRar5) { - AdditionalSize = reader.ReadUInt32(); + HeadCRC = reader.ReadUInt32(); + reader.ResetCrc(); + HeaderSize = (int)reader.ReadRarVIntUInt32(3); + reader.Mark(); + HeaderType = (HeaderType)reader.ReadRarVIntUInt32(1); + Flags = reader.ReadRarVIntUInt16(2); + if ((Flags & HeaderFlagsV5.HasExtra) == HeaderFlagsV5.HasExtra) + { + ExtraSize = reader.ReadRarVIntUInt32(); + } + if ((Flags & HeaderFlagsV5.HasData) == HeaderFlagsV5.HasData) + { + AdditionalDataSize = reader.ReadRarVIntUInt32(); + } + } else { + reader.Mark(); + HeadCRC = reader.ReadUInt16(); + reader.ResetCrc(); + HeaderType = (HeaderType)reader.ReadByte(); + Flags = reader.ReadUInt16(); + HeaderSize = reader.ReadInt16(); + if ((Flags & HeaderFlagsV4.HasData) == HeaderFlagsV4.HasData) + { + AdditionalDataSize = reader.ReadUInt32(); + } } } @@ -54,16 +97,14 @@ namespace SharpCompress.Common.Rar.Headers { throw new NotImplementedException(); } - +//x internal T PromoteHeader(RarCrcBinaryReader reader) where T : RarHeader, new() { T header = new T(); header.FillBase(this); - - reader.Mark(); header.ReadFromReader(reader); - header.ReadBytes += reader.CurrentReadByteCount; + header.ReadBytes = reader.CurrentReadByteCount; int headerSizeDiff = header.HeaderSize - (int)header.ReadBytes; if (headerSizeDiff > 0) @@ -71,16 +112,17 @@ namespace SharpCompress.Common.Rar.Headers reader.ReadBytes(headerSizeDiff); } - VerifyHeaderCrc(reader.GetCrc()); + VerifyHeaderCrc(reader.GetCrc32()); return header; } - private void VerifyHeaderCrc(ushort crc) + private void VerifyHeaderCrc(uint crc32) { if (HeaderType != HeaderType.MarkHeader) { - if (crc != HeadCRC) + var b = (this.isRar5 ? crc32 : (ushort)crc32) == HeadCRC; + if (!b) { throw new InvalidFormatException("rar header crc mismatch"); } @@ -96,22 +138,27 @@ namespace SharpCompress.Common.Rar.Headers /// protected long ReadBytes { get; private set; } - protected ushort HeadCRC { get; private set; } + protected uint HeadCRC { get; private set; } public HeaderType HeaderType { get; private set; } /// /// Untyped flags. These should be typed when Promoting to another header /// - protected short Flags { get; private set; } + protected ushort Flags { get; private set; } - protected short HeaderSize { get; private set; } + protected int HeaderSize { get; private set; } internal ArchiveEncoding ArchiveEncoding { get; private set; } /// - /// This additional size of the header could be file data + /// Extra header data. /// - protected uint AdditionalSize { get; private set; } + protected uint ExtraSize { get; private set; } + + /// + /// Additional data size, eg recovery record + /// + protected uint AdditionalDataSize { get; private set; } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index 66d4c024..22ac412f 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -54,16 +54,18 @@ namespace SharpCompress.Common.Rar.Headers #endif } - var header = RarHeader.Create(reader, Options.ArchiveEncoding); + var header = RarHeader.ReadBase(reader, Options.ArchiveEncoding, this.isRar5); if (header == null) { return null; } switch (header.HeaderType) { + case HeaderType.Rar5ArchiveHeader: case HeaderType.ArchiveHeader: { - var ah = header.PromoteHeader(reader); + var ah = new ArchiveHeader(header, reader); + //x var ah = header.PromoteHeader(reader); IsEncrypted = ah.HasPassword; return ah; } diff --git a/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs index aa8c73fd..3251552b 100644 --- a/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs @@ -10,9 +10,9 @@ namespace SharpCompress.Common.Rar { { } - public ushort GetCrc() + public uint GetCrc32() { - return (ushort)~currentCrc; + return ~currentCrc; } public void ResetCrc() diff --git a/src/SharpCompress/Common/Rar/RarVolume.cs b/src/SharpCompress/Common/Rar/RarVolume.cs index 4bed1696..521cae19 100644 --- a/src/SharpCompress/Common/Rar/RarVolume.cs +++ b/src/SharpCompress/Common/Rar/RarVolume.cs @@ -81,7 +81,8 @@ namespace SharpCompress.Common.Rar get { EnsureArchiveHeaderLoaded(); - return ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.FIRSTVOLUME); + //return (ArchiveHeader.ArchiveHeaderFlags & ArchiveFlagsV4.FirstVolume) == ArchiveFlagsV4.FirstVolume; + return ArchiveHeader.IsFirstVolume; } } @@ -93,7 +94,7 @@ namespace SharpCompress.Common.Rar get { EnsureArchiveHeaderLoaded(); - return ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.VOLUME); + return ArchiveHeader.IsVolume; } } @@ -106,7 +107,7 @@ namespace SharpCompress.Common.Rar get { EnsureArchiveHeaderLoaded(); - return ArchiveHeader.ArchiveHeaderFlags.HasFlag(ArchiveFlags.SOLID); + return ArchiveHeader.IsSolid; } } } diff --git a/src/SharpCompress/IO/MarkingBinaryReader.cs b/src/SharpCompress/IO/MarkingBinaryReader.cs index 42163d53..ed33d8a4 100644 --- a/src/SharpCompress/IO/MarkingBinaryReader.cs +++ b/src/SharpCompress/IO/MarkingBinaryReader.cs @@ -143,18 +143,56 @@ namespace SharpCompress.IO int shift = 0; ulong result = 0; do { - ulong n = ReadByte(); + byte b0 = ReadByte(); + var b1 = b0 & 0x7f; + ulong n = (ulong)b1; result |= n << shift; - shift += 7; - if ((n & 0x80) == 0) { + if (b0 == b1) { return result; } // note: we're actually only allowing a max high bit of 2^56 // to avoid an extra complex check for shift overflow due to the // 10th byte having high bits set + shift += 7; } while (shift < 63); throw new FormatException("malformed vint"); } + + public uint ReadRarVIntUInt32(int maxBytes = 5) { + int shift = 0; + uint result = 0; + do { + byte b0 = ReadByte(); + var b1 = b0 & 0x7f; + uint n = (uint)b1; + result |= n << shift; + if (b0 == b1) { + return result; + } + shift += 7; + // NOTE: we are too strict here but handling the full range adds complexity and we don't need it + } while (shift < 28 && --maxBytes > 0); + + throw new FormatException("malformed vint"); + } + + public ushort ReadRarVIntUInt16(int maxBytes = 3) { + int shift = 0; + uint result = 0; + do { + byte b0 = ReadByte(); + var b1 = b0 & 0x7f; + uint n = (uint)b1; + result |= n << shift; + if (b0 == b1) { + return checked((ushort)result); + } + shift += 7; + // NOTE: we are too strict here but handling the full range adds complexity and we don't need it + } while (shift < 14 && --maxBytes > 0); + + throw new FormatException("malformed vint"); + } } } \ No newline at end of file From 72f52359e6238419fdb2d9e6f26e1c358807c332 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 01:58:31 -0500 Subject: [PATCH 025/124] rar5: wip --- .../Archives/Rar/RarArchiveEntry.cs | 13 +- .../Archives/Rar/RarArchiveEntryFactory.cs | 2 +- .../Archives/Rar/SeekableFilePart.cs | 4 +- .../Common/Rar/Headers/ArchiveHeader.cs | 82 ++--- .../Common/Rar/Headers/EndArchiveHeader.cs | 29 +- .../Common/Rar/Headers/FileHeader.cs | 311 ++++++++++++++---- src/SharpCompress/Common/Rar/Headers/Flags.cs | 136 +++++--- .../Common/Rar/Headers/IRarHeader.cs | 6 + .../Common/Rar/Headers/MarkHeader.cs | 53 ++- .../Common/Rar/Headers/ProtectHeader.cs | 2 +- .../Common/Rar/Headers/RarHeader.cs | 79 ++--- .../Common/Rar/Headers/RarHeaderFactory.cs | 43 ++- src/SharpCompress/Common/Rar/RarEntry.cs | 8 +- src/SharpCompress/Common/Rar/RarVolume.cs | 6 +- .../Rar/MultiVolumeReadOnlyStream.cs | 6 +- src/SharpCompress/Compressors/Rar/Unpack.cs | 9 +- src/SharpCompress/IO/MarkingBinaryReader.cs | 18 + src/SharpCompress/Utility.cs | 10 +- .../Rar/RarHeaderFactoryTest.cs | 2 +- 19 files changed, 550 insertions(+), 269 deletions(-) create mode 100644 src/SharpCompress/Common/Rar/Headers/IRarHeader.cs diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs index fe3d5106..8043784d 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs @@ -24,7 +24,7 @@ namespace SharpCompress.Archives.Rar public IArchive Archive => archive; - internal override IEnumerable Parts => parts.Cast(); + internal override IEnumerable Parts => this.parts; internal override FileHeader FileHeader => parts.First().FileHeader; @@ -33,8 +33,7 @@ namespace SharpCompress.Archives.Rar get { CheckIncomplete(); - return parts.Select(fp => fp.FileHeader) - .Single(fh => !fh.FileFlags.HasFlag(FileFlags.SPLIT_AFTER)).FileCRC; + return parts.Select(fp => fp.FileHeader).Single(fh => !fh.IsSplit).FileCrc; } } @@ -65,7 +64,13 @@ namespace SharpCompress.Archives.Rar return new RarStream(archive.Unpack, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); } - public bool IsComplete { get { return parts.Select(fp => fp.FileHeader).Any(fh => !fh.FileFlags.HasFlag(FileFlags.SPLIT_AFTER)); } } + public bool IsComplete + { + get + { + return parts.Select(fp => fp.FileHeader).Any(fh => !fh.IsSplit); + } + } private void CheckIncomplete() { diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs index 23057c61..b5c4865a 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs @@ -25,7 +25,7 @@ namespace SharpCompress.Archives.Rar { groupedParts.Add(fp); - if (!FlagUtility.HasFlag((long)fp.FileHeader.FileFlags, (long)FileFlags.SPLIT_AFTER)) + if (!fp.FileHeader.IsSplit) { yield return groupedParts; groupedParts = new List(); diff --git a/src/SharpCompress/Archives/Rar/SeekableFilePart.cs b/src/SharpCompress/Archives/Rar/SeekableFilePart.cs index 1e583f07..b7d3affc 100644 --- a/src/SharpCompress/Archives/Rar/SeekableFilePart.cs +++ b/src/SharpCompress/Archives/Rar/SeekableFilePart.cs @@ -20,9 +20,9 @@ namespace SharpCompress.Archives.Rar { stream.Position = FileHeader.DataStartPosition; #if !NO_CRYPTO - if (FileHeader.Salt != null) + if (FileHeader.R4Salt != null) { - return new RarCryptoWrapper(stream, password, FileHeader.Salt); + return new RarCryptoWrapper(stream, password, FileHeader.R4Salt); } #endif return stream; diff --git a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs index 82a32910..d9630296 100644 --- a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs @@ -1,34 +1,32 @@ -using System; using SharpCompress.IO; namespace SharpCompress.Common.Rar.Headers { internal class ArchiveHeader : RarHeader { - internal const short mainHeaderSizeWithEnc = 7; - internal const short mainHeaderSize = 6; - public ArchiveHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader) { + : base(header, reader, HeaderType.Archive) { } protected override void ReadFromReader(MarkingBinaryReader reader) { - if (this.isRar5) + if (this.IsRar5) { ArchiveHeaderFlags = reader.ReadRarVIntUInt16(); - if ((ArchiveHeaderFlags & ArchiveFlagsV5.HasVolumeNumber) == ArchiveFlagsV5.HasVolumeNumber) + if (HasArchiveHeaderFlag(ArchiveFlagsV5.HasVolumeNumber)) { VolumeNumber = (int)reader.ReadRarVIntUInt32(); } if (ExtraSize != 0) { ReadLocator(reader); } - } else { - ArchiveHeaderFlags = Flags; + } + else + { + ArchiveHeaderFlags = HeaderFlags; HighPosAv = reader.ReadInt16(); PosAv = reader.ReadInt32(); - if ((ArchiveHeaderFlags & ArchiveFlagsV4.EncryptVer) == ArchiveFlagsV4.EncryptVer) + if (HasArchiveHeaderFlag(ArchiveFlagsV4.EncryptVer)) { EncryptionVersion = reader.ReadByte(); } @@ -40,59 +38,41 @@ namespace SharpCompress.Common.Rar.Headers var type = reader.ReadRarVIntUInt16(); if (type != 1) throw new InvalidFormatException("expected locator record"); var flags = reader.ReadRarVIntUInt16(); - const ushort HasQuickOpenOffset = 0x01; - const ushort HasRecoveryOffset = 0x02; + const ushort hasQuickOpenOffset = 0x01; + const ushort hasRecoveryOffset = 0x02; ulong quickOpenOffset = 0; - if ((flags & HasQuickOpenOffset) == HasQuickOpenOffset) { + if ((flags & hasQuickOpenOffset) == hasQuickOpenOffset) { quickOpenOffset = reader.ReadRarVInt(); } ulong recoveryOffset = 0; - if ((flags & HasRecoveryOffset) == HasRecoveryOffset) { + if ((flags & hasRecoveryOffset) == hasRecoveryOffset) { recoveryOffset = reader.ReadRarVInt(); } } - internal ushort ArchiveHeaderFlags { get; private set; } + private ushort ArchiveHeaderFlags { get; set; } - internal int VolumeNumber { get; private set; } - - internal short HighPosAv { get; private set; } - - internal int PosAv { get; private set; } - - private byte EncryptionVersion { get; set; } - - public bool HasPassword => (ArchiveHeaderFlags & ArchiveFlagsV4.Password) == ArchiveFlagsV4.Password; - - public bool OldNumberingFormat { - get { - return !this.isRar5 && - ((ArchiveHeaderFlags & ArchiveFlagsV4.NewNumbering) != ArchiveFlagsV4.NewNumbering); - } + private bool HasArchiveHeaderFlag(ushort flag) + { + return (ArchiveHeaderFlags & flag) == flag; } - public bool IsVolume { - get { - return this.isRar5 - ? ((ArchiveHeaderFlags & ArchiveFlagsV5.Volume) == ArchiveFlagsV5.Volume) - : ((ArchiveHeaderFlags & ArchiveFlagsV4.Volume) == ArchiveFlagsV4.Volume); - } - } + internal int? VolumeNumber { get; private set; } - public bool IsFirstVolume { - get { - return this.isRar5 - ? VolumeNumber == 1 - : ((ArchiveHeaderFlags & ArchiveFlagsV4.FirstVolume) == ArchiveFlagsV4.FirstVolume); - } - } + internal short? HighPosAv { get; private set; } - public bool IsSolid { - get { - return this.isRar5 - ? ((ArchiveHeaderFlags & ArchiveFlagsV5.Solid) == ArchiveFlagsV5.Solid) - : ((ArchiveHeaderFlags & ArchiveFlagsV4.Solid) == ArchiveFlagsV4.Solid); - } - } + internal int? PosAv { get; private set; } + + private byte? EncryptionVersion { get; set; } + + public bool? IsEncrypted => this.IsRar5 ? (bool?)null : HasArchiveHeaderFlag(ArchiveFlagsV4.Password); + + public bool OldNumberingFormat => !this.IsRar5 && !HasArchiveHeaderFlag(ArchiveFlagsV4.NewNumbering); + + public bool IsVolume => HasArchiveHeaderFlag(this.IsRar5 ? ArchiveFlagsV5.Volume : ArchiveFlagsV4.Volume); + + public bool IsFirstVolume => this.IsRar5 ? VolumeNumber == 1 : HasArchiveHeaderFlag(ArchiveFlagsV4.FirstVolume); + + public bool IsSolid => HasArchiveHeaderFlag(this.IsRar5 ? ArchiveFlagsV5.Solid : ArchiveFlagsV4.Solid); } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs index b2f62039..caee1a83 100644 --- a/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs @@ -4,21 +4,38 @@ namespace SharpCompress.Common.Rar.Headers { internal class EndArchiveHeader : RarHeader { + public EndArchiveHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.EndArchive) { + } + protected override void ReadFromReader(MarkingBinaryReader reader) { - if (EndArchiveFlags.HasFlag(EndArchiveFlags.EARC_DATACRC)) + if (this.IsRar5) { - ArchiveCRC = reader.ReadInt32(); + EndArchiveFlags = reader.ReadRarVIntUInt16(); } - if (EndArchiveFlags.HasFlag(EndArchiveFlags.EARC_VOLNUMBER)) + else { - VolumeNumber = reader.ReadInt16(); + EndArchiveFlags = HeaderFlags; + if (HasEndArchiveFlag(EndArchiveFlagsV4.DataCrc)) + { + ArchiveCrc = reader.ReadInt32(); + } + if (HasEndArchiveFlag(EndArchiveFlagsV4.VolumeNumber)) + { + VolumeNumber = reader.ReadInt16(); + } } } - internal EndArchiveFlags EndArchiveFlags => (EndArchiveFlags)Flags; + private ushort EndArchiveFlags { get; set; } - internal int? ArchiveCRC { get; private set; } + private bool HasEndArchiveFlag(ushort flag) + { + return (EndArchiveFlags & flag) == flag; + } + + internal int? ArchiveCrc { get; private set; } internal short? VolumeNumber { get; private set; } } diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index 335f343c..dd789c6b 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -1,35 +1,203 @@ using SharpCompress.IO; using System; using System.IO; +using System.Text; namespace SharpCompress.Common.Rar.Headers { internal class FileHeader : RarHeader { - private const byte SALT_SIZE = 8; + private uint fileCrc; - private const byte NEWLHD_SIZE = 32; + public FileHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.File) { + } - protected override void ReadFromReader(MarkingBinaryReader reader) + protected override void ReadFromReader(MarkingBinaryReader reader) { + if (this.IsRar5) + { + ReadFromReaderV5(reader); + } + else + { + ReadFromReaderV4(reader); + } + } + + private void ReadFromReaderV5(MarkingBinaryReader reader) + { + FileFlags = reader.ReadRarVIntUInt16(); + + var lvalue = checked((long)reader.ReadRarVInt()); +//!!! rar5 TODO: this may cause problems with user code, Unpack20 seems to use this field to decompress + UncompressedSize = HasFileFlag(FileFlagsV5.UnpackedSizeUnknown) ? long.MaxValue : lvalue; + + FileAttributes = reader.ReadRarVIntUInt32(); + + if (HasFileFlag(FileFlagsV5.HasModTime)) { + FileLastModifiedTime = Utility.UnixTimeToDateTime(reader.ReadUInt32()); + } + + if (HasFileFlag(FileFlagsV5.HasCrc32)) { + FileCrc = reader.ReadUInt32(); + } + + var us = reader.ReadRarVIntUInt16(); + + // Lower 6 bits (0x003f mask) contain the version of compression algorithm, resulting in possible 0 - 63 values. Current version is 0. + R5CompressionAlgorithm = (byte)(us & 0x3f); + if (R5CompressionAlgorithm != 0) throw new InvalidFormatException("unknown rar5 compression algorithm " + R5CompressionAlgorithm); + + // 7th bit (0x0040) defines the solid flag. If it is set, RAR continues to use the compression dictionary left after processing preceding files. + // It can be set only for file headers and is never set for service headers. + R5IsSolid = (us & 0x40) == 0x40; + + // Bits 8 - 10 (0x0380 mask) define the compression method. Currently only values 0 - 5 are used. 0 means no compression. + R5CompressionMethod = (byte)((us >> 7) & 0x7); + + // Bits 11 - 14 (0x3c00) define the minimum size of dictionary size required to extract data. Value 0 means 128 KB, 1 - 256 KB, ..., 14 - 2048 MB, 15 - 4096 MB. + // 2 ^ (17 + x) + R5DictSize_2_17plusX = (byte)((us >> 10) & 0xf); + + HostOs = reader.ReadRarVIntByte(); + + var nameSize = reader.ReadRarVIntUInt16(); + + // Variable length field containing Name length bytes in UTF-8 format without trailing zero. + // For file header this is a name of archived file. Forward slash character is used as the path separator both for Unix and Windows names. + // Backslashes are treated as a part of name for Unix names and as invalid character for Windows file names. Type of name is defined by Host OS field. + // + // TODO: not sure if anything needs to be done to handle the following: + // If Unix file name contains any high ASCII characters which cannot be correctly converted to Unicode and UTF-8 + // we map such characters to to 0xE080 - 0xE0FF private use Unicode area and insert 0xFFFE Unicode non-character + // to resulting string to indicate that it contains mapped characters, which need to be converted back when extracting. + // Concrete position of 0xFFFE is not defined, we need to search the entire string for it. Such mapped names are not + // portable and can be correctly unpacked only on the same system where they were created. + // + // For service header this field contains a name of service header. Now the following names are used: + // CMT Archive comment + // QO Archive quick open data + // ACL NTFS file permissions + // STM NTFS alternate data stream + // RR Recovery record + var b = reader.ReadBytes(nameSize); + FileName = ConvertPathV5(Encoding.UTF8.GetString(b, 0, b.Length)); + + if (ExtraSize != 0) { + var size = reader.ReadRarVIntUInt16(); + var type = reader.ReadRarVIntUInt16(); + switch (type) { +//TODO +// case 1: // file encryption +// { +// var version = reader.ReadRarVIntByte(); +// if (version != 0) throw new InvalidFormatException("unknown encryption algorithm "+ version); +// +// } +// break; +// case 2: // file hash +// { +// +// } +// break; + case 3: // file time + { + ushort flags = reader.ReadRarVIntUInt16(); + var isWindowsTime = (flags & 1) == 0; + if ((flags & 0x2) == 0x2) { + FileLastModifiedTime = ReadExtendedTimeV5(reader, isWindowsTime); + } + if ((flags & 0x4) == 0x4) { + FileCreatedTime = ReadExtendedTimeV5(reader, isWindowsTime); + } + if ((flags & 0x8) == 0x8) { + FileLastAccessedTime = ReadExtendedTimeV5(reader, isWindowsTime); + } + } + break; +//TODO +// case 4: // file version +// { +// +// } +// break; +// case 5: // file system redirection +// { +// +// } +// break; +// case 6: // unix owner +// { +// +// } +// break; +// case 7: // service data +// { +// +// } +// break; + + default: throw new InvalidFormatException("unexpected extra data record type " + type); + } + + } + + if (AdditionalDataSize != 0) { + CompressedSize = AdditionalDataSize; + } + } + + private static DateTime ReadExtendedTimeV5(MarkingBinaryReader reader, bool isWindowsTime) + { + if (isWindowsTime) + { + return DateTime.FromFileTime(reader.ReadInt64()); + } + else + { + return Utility.UnixTimeToDateTime(reader.ReadUInt32()); + } + } + + private static string ConvertPathV5(string path) + { +#if NO_FILE + // not sure what to do here + throw new NotImplementedException("TODO"); +#else + if (Path.DirectorySeparatorChar == '\\') + { + // replace embedded \\ with valid filename char + return path.Replace('\\', '-').Replace('/', '\\'); + } + return path; +#endif + } + + + private void ReadFromReaderV4(MarkingBinaryReader reader) + { + FileFlags = HeaderFlags; + uint lowUncompressedSize = reader.ReadUInt32(); - HostOS = (HostOS)reader.ReadByte(); + HostOs = reader.ReadByte(); - FileCRC = reader.ReadUInt32(); + FileCrc = reader.ReadUInt32(); - FileLastModifiedTime = Utility.DosDateToDateTime(reader.ReadInt32()); + FileLastModifiedTime = Utility.DosDateToDateTime(reader.ReadUInt32()); - RarVersion = reader.ReadByte(); - PackingMethod = reader.ReadByte(); + R4RarVersion = reader.ReadByte(); + R4PackingMethod = reader.ReadByte(); short nameSize = reader.ReadInt16(); - FileAttributes = reader.ReadInt32(); + FileAttributes = reader.ReadUInt32(); uint highCompressedSize = 0; uint highUncompressedkSize = 0; - if (FileFlags.HasFlag(FileFlags.LARGE)) + if (HasFileFlag(FileFlagsV4.Large)) { highCompressedSize = reader.ReadUInt32(); highUncompressedkSize = reader.ReadUInt32(); @@ -42,18 +210,21 @@ namespace SharpCompress.Common.Rar.Headers highUncompressedkSize = int.MaxValue; } } - CompressedSize = UInt32To64(highCompressedSize, AdditionalDataSize); + CompressedSize = UInt32To64(highCompressedSize, checked((uint)AdditionalDataSize)); UncompressedSize = UInt32To64(highUncompressedkSize, lowUncompressedSize); nameSize = nameSize > 4 * 1024 ? (short)(4 * 1024) : nameSize; byte[] fileNameBytes = reader.ReadBytes(nameSize); - switch (HeaderType) + const int saltSize = 8; + const int newLhdSize = 32; + + switch (HeaderCode) { - case HeaderType.FileHeader: + case HeaderCodeV.FileHeader: { - if (FileFlags.HasFlag(FileFlags.UNICODE)) + if (HasFileFlag(FileFlagsV4.Unicode)) { int length = 0; while (length < fileNameBytes.Length @@ -75,15 +246,15 @@ namespace SharpCompress.Common.Rar.Headers { FileName = ArchiveEncoding.Decode(fileNameBytes); } - FileName = ConvertPath(FileName, HostOS); + FileName = ConvertPathV4(FileName); } break; - case HeaderType.NewSubHeader: + case HeaderCodeV.NewSubHeader: { - int datasize = HeaderSize - NEWLHD_SIZE - nameSize; - if (FileFlags.HasFlag(FileFlags.SALT)) + int datasize = HeaderSize - newLhdSize - nameSize; + if (HasFileFlag(FileFlagsV4.Salt)) { - datasize -= SALT_SIZE; + datasize -= saltSize; } if (datasize > 0) { @@ -99,34 +270,33 @@ namespace SharpCompress.Common.Rar.Headers break; } - if (FileFlags.HasFlag(FileFlags.SALT)) + if (HasFileFlag(FileFlagsV4.Salt)) { - Salt = reader.ReadBytes(SALT_SIZE); + R4Salt = reader.ReadBytes(saltSize); } - if (FileFlags.HasFlag(FileFlags.EXTTIME)) + if (HasFileFlag(FileFlagsV4.ExtTime)) { // verify that the end of the header hasn't been reached before reading the Extended Time. // some tools incorrectly omit Extended Time despite specifying FileFlags.EXTTIME, which most parsers tolerate. if (ReadBytes + reader.CurrentReadByteCount <= HeaderSize - 2) { ushort extendedFlags = reader.ReadUInt16(); - FileLastModifiedTime = ProcessExtendedTime(extendedFlags, FileLastModifiedTime, reader, 0); - FileCreatedTime = ProcessExtendedTime(extendedFlags, null, reader, 1); - FileLastAccessedTime = ProcessExtendedTime(extendedFlags, null, reader, 2); - FileArchivedTime = ProcessExtendedTime(extendedFlags, null, reader, 3); + FileLastModifiedTime = ProcessExtendedTimeV4(extendedFlags, FileLastModifiedTime, reader, 0); + FileCreatedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 1); + FileLastAccessedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 2); + FileArchivedTime = ProcessExtendedTimeV4(extendedFlags, null, reader, 3); } } } - private long UInt32To64(uint x, uint y) + private static long UInt32To64(uint x, uint y) { long l = x; l <<= 32; return l + y; } - private static DateTime? ProcessExtendedTime(ushort extendedFlags, DateTime? time, MarkingBinaryReader reader, - int i) + private static DateTime? ProcessExtendedTimeV4(ushort extendedFlags, DateTime? time, MarkingBinaryReader reader, int i) { uint rmode = (uint)extendedFlags >> (3 - i) * 4; if ((rmode & 8) == 0) @@ -154,7 +324,7 @@ namespace SharpCompress.Common.Rar.Headers return time.Value.AddMilliseconds(nanosecondHundreds * Math.Pow(10, -4)); } - private static string ConvertPath(string path, HostOS os) + private static string ConvertPathV4(string path) { #if NO_FILE return path.Replace('\\', '/'); @@ -171,11 +341,61 @@ namespace SharpCompress.Common.Rar.Headers #endif } + public override string ToString() + { + return FileName; + } + + private ushort FileFlags { get; set; } + + private bool HasFileFlag(ushort flag) + { + return (FileFlags & flag) == flag; + } + + internal uint FileCrc + { + get { + if (this.IsRar5 && !HasFileFlag(FileFlagsV5.HasCrc32)) { +//!!! rar5: + throw new InvalidOperationException("TODO rar5"); + } + return this.fileCrc; + } + private set { this.fileCrc = value; } + } + + internal byte R4RarVersion { get; private set; } + internal byte R4PackingMethod { get; private set; } + internal byte[] R4Salt { get; private set; } + + internal byte R5CompressionAlgorithm { get; private set; } + internal bool R5IsSolid{ get; private set; } + internal byte R5CompressionMethod { get; private set; } + internal byte R5DictSize_2_17plusX { 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 int RecoverySectors { get; private set; } internal long DataStartPosition { get; set; } + public Stream PackedStream { get; set; } - internal HostOS HostOS { get; private set; } +//!!! TODO rar5 + public bool IsSplit => HasFileFlag(FileFlagsV4.SplitAfter); + + public bool IsDirectory => HasFileFlag(this.IsRar5 ? FileFlagsV5.Directory : FileFlagsV4.Directory); + +//!!! TODO rar5 + public bool IsEncrypted => HasFileFlag(FileFlagsV4.Password); + +//!!! TODO rar5; R5IsSolid + public bool IsSolid => HasFileFlag(FileFlagsV4.Solid); - internal uint FileCRC { get; private set; } internal DateTime? FileLastModifiedTime { get; private set; } @@ -184,32 +404,5 @@ namespace SharpCompress.Common.Rar.Headers internal DateTime? FileLastAccessedTime { get; private set; } internal DateTime? FileArchivedTime { get; private set; } - - internal byte RarVersion { get; private set; } - - internal byte PackingMethod { get; private set; } - - internal int FileAttributes { get; private set; } - - internal FileFlags FileFlags => (FileFlags)Flags; - - 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 int RecoverySectors { get; private set; } - - internal byte[] Salt { get; private set; } - - public override string ToString() - { - return FileName; - } - - public Stream PackedStream { get; set; } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index 09c58083..dba0d245 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -4,22 +4,37 @@ namespace SharpCompress.Common.Rar.Headers { internal enum HeaderType : byte { - Rar5ArchiveHeader = 0x01, - Rar5FileHeader = 0x02, - Rar5ServiceHeader = 0x03, - Rar5EncryptionHeader = 0x04, - Rar5EndArchiveHeader = 0x05, + Null, + Mark, + Archive, + File, +// Comment, +// Av, +// Sub, +// Protect, +// Sign, +// NewSub, + EndArchive + } - MarkHeader = 0x72, - ArchiveHeader = 0x73, - FileHeader = 0x74, - CommHeader = 0x75, - AvHeader = 0x76, - SubHeader = 0x77, - ProtectHeader = 0x78, - SignHeader = 0x79, - NewSubHeader = 0x7a, - EndArchiveHeader = 0x7b + internal static class HeaderCodeV + { + public const byte Rar5ArchiveHeader = 0x01; + public const byte Rar5FileHeader = 0x02; + public const byte Rar5ServiceHeader = 0x03; + public const byte Rar5EncryptionHeader = 0x04; + public const byte Rar5EndArchiveHeader = 0x05; + + public const byte MarkHeader = 0x72; + public const byte ArchiveHeader = 0x73; + public const byte FileHeader = 0x74; + public const byte CommHeader = 0x75; + public const byte AvHeader = 0x76; + public const byte SubHeader = 0x77; + public const byte ProtectHeader = 0x78; + public const byte SignHeader = 0x79; + public const byte NewSubHeader = 0x7a; + public const byte EndArchiveHeader = 0x7b; } internal static class HeaderFlagsV4 @@ -56,49 +71,66 @@ namespace SharpCompress.Common.Rar.Headers public const ushort Lock = 0x0010; } - internal enum HostOS + internal static class HostOsV4 { - MSDOS = 0, - OS2 = 1, - Win32 = 2, - Unix = 3, - MacOS = 4, - BeOS = 5 + public const byte MsDos = 0; + public const byte Os2 = 1; + public const byte Win32 = 2; + public const byte Unix = 3; + public const byte MacOs = 4; + public const byte BeOs = 5; } - [Flags] - internal enum FileFlags : ushort + internal static class HostOsV5 { - SPLIT_BEFORE = 0x0001, - SPLIT_AFTER = 0x0002, - PASSWORD = 0x0004, - COMMENT = 0x0008, - SOLID = 0x0010, - - WINDOWMASK = 0x00e0, - WINDOW64 = 0x0000, - WINDOW128 = 0x0020, - WINDOW256 = 0x0040, - WINDOW512 = 0x0060, - WINDOW1024 = 0x0080, - WINDOW2048 = 0x00a0, - WINDOW4096 = 0x00c0, - DIRECTORY = 0x00e0, - - LARGE = 0x0100, - UNICODE = 0x0200, - SALT = 0x0400, - VERSION = 0x0800, - EXTTIME = 0x1000, - EXTFLAGS = 0x2000 + public const byte Windows = 0; + public const byte Unix = 1; } - [Flags] - internal enum EndArchiveFlags + internal static class FileFlagsV4 { - EARC_NEXT_VOLUME = 0x0001, - EARC_DATACRC = 0x0002, - EARC_REVSPACE = 0x0004, - EARC_VOLNUMBER = 0x0008 + public const ushort SplitBefore = 0x0001; + public const ushort SplitAfter = 0x0002; + public const ushort Password = 0x0004; + public const ushort Comment = 0x0008; + public const ushort Solid = 0x0010; + + public const ushort WindowMask = 0x00e0; + public const ushort Window64 = 0x0000; + public const ushort Window128 = 0x0020; + public const ushort Window256 = 0x0040; + public const ushort Window512 = 0x0060; + public const ushort Window1024 = 0x0080; + public const ushort Window2048 = 0x00a0; + public const ushort Window4096 = 0x00c0; + public const ushort Directory = 0x00e0; + + public const ushort Large = 0x0100; + public const ushort Unicode = 0x0200; + public const ushort Salt = 0x0400; + public const ushort Version = 0x0800; + public const ushort ExtTime = 0x1000; + public const ushort ExtFlags = 0x2000; + } + + internal static class FileFlagsV5 + { + public const ushort Directory = 0x0001; + public const ushort HasModTime = 0x0002; + public const ushort HasCrc32 = 0x0004; + public const ushort UnpackedSizeUnknown = 0x0008; + } + + internal static class EndArchiveFlagsV4 + { + public const ushort NextVolume = 0x0001; + public const ushort DataCrc = 0x0002; + public const ushort RevSpace = 0x0004; + public const ushort VolumeNumber = 0x0008; + } + + internal static class EndArchiveFlagsV5 + { + public const ushort HasNextVolume = 0x0001; } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/IRarHeader.cs b/src/SharpCompress/Common/Rar/Headers/IRarHeader.cs new file mode 100644 index 00000000..93f9b0ca --- /dev/null +++ b/src/SharpCompress/Common/Rar/Headers/IRarHeader.cs @@ -0,0 +1,6 @@ +namespace SharpCompress.Common.Rar.Headers +{ + internal interface IRarHeader { + HeaderType HeaderType { get; } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs index 30225bc7..4e972913 100644 --- a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs @@ -5,41 +5,48 @@ namespace SharpCompress.Common.Rar.Headers { internal class MarkHeader : IRarHeader { - private const int MAX_SFX_SIZE = 0x80000 - 16; //archive.cpp line 136 + private const int MaxSfxSize = 0x80000 - 16; //archive.cpp line 136 internal bool OldNumberingFormat { get; private set; } - public bool IsRar5 { get; private set; } + public bool IsRar5 { get; } - private MarkHeader(bool isRar5) { + private MarkHeader(bool isRar5) + { IsRar5 = isRar5; } public HeaderType HeaderType { - get { return HeaderType.MarkHeader; } + get { return HeaderType.Mark; } } - private static byte GetByte(Stream stream) { + private static byte GetByte(Stream stream) + { var b = stream.ReadByte(); - if (b != -1) { + if (b != -1) + { return (byte)b; } throw new EndOfStreamException(); } - public static MarkHeader Read(Stream stream, bool leaveStreamOpen, bool lookForHeader) { - int maxScanIndex = lookForHeader ? MAX_SFX_SIZE : 0; + public static MarkHeader Read(Stream stream, bool leaveStreamOpen, bool lookForHeader) + { + int maxScanIndex = lookForHeader ? MaxSfxSize : 0; try { int start = -1; var b = GetByte(stream); start++; - while (start <= maxScanIndex) { + while (start <= maxScanIndex) + { // Rar old signature: 52 45 7E 5E // Rar4 signature: 52 61 72 21 1A 07 00 // Rar5 signature: 52 61 72 21 1A 07 01 00 - if (b == 0x52) { + if (b == 0x52) + { b = GetByte(stream); start++; - if (b == 0x61) { + if (b == 0x61) + { b = GetByte(stream); start++; if (b != 0x72) continue; b = GetByte(stream); start++; @@ -50,26 +57,36 @@ namespace SharpCompress.Common.Rar.Headers if (b != 0x07) continue; b = GetByte(stream); start++; - if (b == 1) { + if (b == 1) + { b = GetByte(stream); start++; if (b != 0) continue; return new MarkHeader(true); // Rar5 - } else if (b == 0) { + } + else if (b == 0) + { return new MarkHeader(false); // Rar4 - } else { + } + else + { continue; } - } else if (b == 0x45) { + } + else if (b == 0x45) + { b = GetByte(stream); start++; if (b != 0x7e) continue; b = GetByte(stream); start++; if (b != 0x5e) continue; throw new InvalidFormatException("Rar format version pre-4 is unsupported."); - //return new MarkHeader(); - } else { + } + else + { continue; } - } else { + } + else + { b = GetByte(stream); start++; continue; } diff --git a/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs b/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs index c47c5676..75dbcb9e 100644 --- a/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs @@ -13,7 +13,7 @@ namespace SharpCompress.Common.Rar.Headers Mark = reader.ReadBytes(8); } - internal uint DataSize => AdditionalDataSize; + internal uint DataSize => checked((uint)AdditionalDataSize); internal byte Version { get; private set; } internal ushort RecSectors { get; private set; } internal uint TotalBlocks { get; private set; } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index 310e8bbf..e81f82f2 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -6,20 +6,23 @@ namespace SharpCompress.Common.Rar.Headers { internal class RarHeader : IRarHeader { - protected readonly bool isRar5; + private readonly HeaderType headerType; + private readonly bool isRar5; protected RarHeader() { //TODO - //throw new NotImplementedException(); + throw new NotImplementedException(); } private RarHeader(bool isRar5) { + this.headerType = HeaderType.Null; this.isRar5 = isRar5; } - protected RarHeader(RarHeader header, RarCrcBinaryReader reader) { - this.isRar5 = header.isRar5; + protected RarHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) { + this.headerType = headerType; + this.isRar5 = header.IsRar5; FillBase(header); ReadFromReader(reader); ReadBytes = reader.CurrentReadByteCount; @@ -33,16 +36,16 @@ namespace SharpCompress.Common.Rar.Headers VerifyHeaderCrc(reader.GetCrc32()); } - private void FillBase(RarHeader baseHeader) + private void FillBase(RarHeader other) { - HeadCRC = baseHeader.HeadCRC; - HeaderType = baseHeader.HeaderType; - Flags = baseHeader.Flags; - HeaderSize = baseHeader.HeaderSize; - ExtraSize = baseHeader.ExtraSize; - AdditionalDataSize = baseHeader.AdditionalDataSize; - ReadBytes = baseHeader.ReadBytes; - ArchiveEncoding = baseHeader.ArchiveEncoding; + HeaderCrc = other.HeaderCrc; + HeaderCode = other.HeaderCode; + HeaderFlags = other.HeaderFlags; + HeaderSize = other.HeaderSize; + ExtraSize = other.ExtraSize; + AdditionalDataSize = other.AdditionalDataSize; + ReadBytes = other.ReadBytes; + ArchiveEncoding = other.ArchiveEncoding; } internal static RarHeader ReadBase(RarCrcBinaryReader reader, ArchiveEncoding archiveEncoding, bool isRar5) @@ -63,30 +66,30 @@ namespace SharpCompress.Common.Rar.Headers private void ReadStartFromReader(RarCrcBinaryReader reader) { - if (this.isRar5) + if (this.IsRar5) { - HeadCRC = reader.ReadUInt32(); + HeaderCrc = reader.ReadUInt32(); reader.ResetCrc(); HeaderSize = (int)reader.ReadRarVIntUInt32(3); reader.Mark(); - HeaderType = (HeaderType)reader.ReadRarVIntUInt32(1); - Flags = reader.ReadRarVIntUInt16(2); - if ((Flags & HeaderFlagsV5.HasExtra) == HeaderFlagsV5.HasExtra) + HeaderCode = reader.ReadRarVIntByte(); + HeaderFlags = reader.ReadRarVIntUInt16(2); + if ((HeaderFlags & HeaderFlagsV5.HasExtra) == HeaderFlagsV5.HasExtra) { ExtraSize = reader.ReadRarVIntUInt32(); } - if ((Flags & HeaderFlagsV5.HasData) == HeaderFlagsV5.HasData) + if ((HeaderFlags & HeaderFlagsV5.HasData) == HeaderFlagsV5.HasData) { - AdditionalDataSize = reader.ReadRarVIntUInt32(); + AdditionalDataSize = (long)reader.ReadRarVInt(); } } else { reader.Mark(); - HeadCRC = reader.ReadUInt16(); + HeaderCrc = reader.ReadUInt16(); reader.ResetCrc(); - HeaderType = (HeaderType)reader.ReadByte(); - Flags = reader.ReadUInt16(); + HeaderCode = reader.ReadByte(); + HeaderFlags = reader.ReadUInt16(); HeaderSize = reader.ReadInt16(); - if ((Flags & HeaderFlagsV4.HasData) == HeaderFlagsV4.HasData) + if ((HeaderFlags & HeaderFlagsV4.HasData) == HeaderFlagsV4.HasData) { AdditionalDataSize = reader.ReadUInt32(); } @@ -119,13 +122,10 @@ namespace SharpCompress.Common.Rar.Headers private void VerifyHeaderCrc(uint crc32) { - if (HeaderType != HeaderType.MarkHeader) + var b = (this.IsRar5 ? crc32 : (ushort)crc32) == HeaderCrc; + if (!b) { - var b = (this.isRar5 ? crc32 : (ushort)crc32) == HeadCRC; - if (!b) - { - throw new InvalidFormatException("rar header crc mismatch"); - } + throw new InvalidFormatException("rar header crc mismatch"); } } @@ -133,32 +133,33 @@ namespace SharpCompress.Common.Rar.Headers { } + public HeaderType HeaderType { get { return this.headerType; } } + + protected bool IsRar5 { get { return this.isRar5; } } + /// /// This is the number of bytes read when reading the header /// protected long ReadBytes { get; private set; } - protected uint HeadCRC { get; private set; } + protected uint HeaderCrc { get; private set; } - public HeaderType HeaderType { get; private set; } + internal byte HeaderCode { get; private set; } - /// - /// Untyped flags. These should be typed when Promoting to another header - /// - protected ushort Flags { get; private set; } + protected ushort HeaderFlags { get; private set; } protected int HeaderSize { get; private set; } internal ArchiveEncoding ArchiveEncoding { get; private set; } /// - /// Extra header data. + /// Extra header size. /// protected uint ExtraSize { get; private set; } /// - /// Additional data size, eg recovery record + /// Size of additional data (eg file contents) /// - protected uint AdditionalDataSize { get; private set; } + protected long AdditionalDataSize { get; private set; } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index 22ac412f..005e03ac 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -30,9 +30,10 @@ namespace SharpCompress.Common.Rar.Headers while ((header = ReadNextHeader(stream)) != null) { yield return header; - if (header.HeaderType == HeaderType.EndArchiveHeader) + if (header.HeaderType == HeaderType.EndArchive) { - yield break; // the end? + // End of archive marker. RAR does not read anything after this header letting to use third party tools to add extra information such as a digital signature to archive. + yield break; } } } @@ -59,18 +60,20 @@ namespace SharpCompress.Common.Rar.Headers { return null; } - switch (header.HeaderType) + switch (header.HeaderCode) { - case HeaderType.Rar5ArchiveHeader: - case HeaderType.ArchiveHeader: + case HeaderCodeV.Rar5ArchiveHeader: + case HeaderCodeV.ArchiveHeader: { var ah = new ArchiveHeader(header, reader); - //x var ah = header.PromoteHeader(reader); - IsEncrypted = ah.HasPassword; + if (ah.IsEncrypted == true) { +//!!! rar5 we don't know yet + IsEncrypted = true; + } return ah; } - case HeaderType.ProtectHeader: + case HeaderCodeV.ProtectHeader: { ProtectHeader ph = header.PromoteHeader(reader); @@ -96,9 +99,10 @@ namespace SharpCompress.Common.Rar.Headers return ph; } - case HeaderType.NewSubHeader: + case HeaderCodeV.Rar5ServiceHeader: + case HeaderCodeV.NewSubHeader: { - FileHeader fh = header.PromoteHeader(reader); + var fh = new FileHeader(header, reader); switch (StreamingMode) { case StreamingMode.Seekable: @@ -120,9 +124,11 @@ namespace SharpCompress.Common.Rar.Headers } return fh; } - case HeaderType.FileHeader: + + case HeaderCodeV.Rar5FileHeader: + case HeaderCodeV.FileHeader: { - FileHeader fh = header.PromoteHeader(reader); + var fh = new FileHeader(header, reader); switch (StreamingMode) { case StreamingMode.Seekable: @@ -134,16 +140,16 @@ namespace SharpCompress.Common.Rar.Headers case StreamingMode.Streaming: { var ms = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize); - if (fh.Salt == null) + if (fh.R4Salt == null) { fh.PackedStream = ms; } else { #if !NO_CRYPTO - fh.PackedStream = new RarCryptoWrapper(ms, Options.Password, fh.Salt); + fh.PackedStream = new RarCryptoWrapper(ms, Options.Password, fh.R4Salt); #else - throw new NotSupportedException("RarCrypto not supported"); + throw new NotSupportedException("RarCrypto not supported"); #endif } } @@ -155,13 +161,14 @@ namespace SharpCompress.Common.Rar.Headers } return fh; } - case HeaderType.EndArchiveHeader: + case HeaderCodeV.Rar5EndArchiveHeader: + case HeaderCodeV.EndArchiveHeader: { - return header.PromoteHeader(reader); + return new EndArchiveHeader(header, reader); } default: { - throw new InvalidFormatException("Invalid Rar Header: " + header.HeaderType); + throw new InvalidFormatException("Invalid Rar Header: " + header.HeaderCode); } } } diff --git a/src/SharpCompress/Common/Rar/RarEntry.cs b/src/SharpCompress/Common/Rar/RarEntry.cs index cddfca3e..105a306a 100644 --- a/src/SharpCompress/Common/Rar/RarEntry.cs +++ b/src/SharpCompress/Common/Rar/RarEntry.cs @@ -10,7 +10,7 @@ namespace SharpCompress.Common.Rar /// /// The File's 32 bit CRC Hash /// - public override long Crc => FileHeader.FileCRC; + public override long Crc => FileHeader.FileCrc; /// /// The path of the file internal to the Rar Archive. @@ -40,14 +40,14 @@ namespace SharpCompress.Common.Rar /// /// Entry is password protected and encrypted and cannot be extracted. /// - public override bool IsEncrypted => FileHeader.FileFlags.HasFlag(FileFlags.PASSWORD); + public override bool IsEncrypted => FileHeader.IsEncrypted; /// /// Entry is password protected and encrypted and cannot be extracted. /// - public override bool IsDirectory => FileHeader.FileFlags.HasFlag(FileFlags.DIRECTORY); + public override bool IsDirectory => FileHeader.IsDirectory; - public override bool IsSplit => FileHeader.FileFlags.HasFlag(FileFlags.SPLIT_AFTER); + public override bool IsSplit => FileHeader.IsSplit; public override string ToString() { diff --git a/src/SharpCompress/Common/Rar/RarVolume.cs b/src/SharpCompress/Common/Rar/RarVolume.cs index 521cae19..17be5a30 100644 --- a/src/SharpCompress/Common/Rar/RarVolume.cs +++ b/src/SharpCompress/Common/Rar/RarVolume.cs @@ -34,17 +34,17 @@ namespace SharpCompress.Common.Rar { switch (header.HeaderType) { - case HeaderType.ArchiveHeader: + case HeaderType.Archive: { ArchiveHeader = header as ArchiveHeader; } break; - case HeaderType.MarkHeader: + case HeaderType.Mark: { previousMarkHeader = header as MarkHeader; } break; - case HeaderType.FileHeader: + case HeaderType.File: { FileHeader fh = header as FileHeader; RarFilePart fp = CreateFilePart(fh, previousMarkHeader); diff --git a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs index d5618d5f..235b8720 100644 --- a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs +++ b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs @@ -59,7 +59,7 @@ namespace SharpCompress.Compressors.Rar currentPartTotalReadBytes = 0; - CurrentCrc = filePartEnumerator.Current.FileHeader.FileCRC; + CurrentCrc = filePartEnumerator.Current.FileHeader.FileCrc; streamListener.FireFilePartExtractionBegin(filePartEnumerator.Current.FilePartName, filePartEnumerator.Current.FileHeader.CompressedSize, @@ -90,9 +90,9 @@ namespace SharpCompress.Compressors.Rar currentCount -= read; totalRead += read; if (((maxPosition - currentPosition) == 0) - && filePartEnumerator.Current.FileHeader.FileFlags.HasFlag(FileFlags.SPLIT_AFTER)) + && filePartEnumerator.Current.FileHeader.IsSplit) { - if (filePartEnumerator.Current.FileHeader.Salt != null) + if (filePartEnumerator.Current.FileHeader.R4Salt != null) { throw new InvalidFormatException("Sharpcompress currently does not support multi-volume decryption."); } diff --git a/src/SharpCompress/Compressors/Rar/Unpack.cs b/src/SharpCompress/Compressors/Rar/Unpack.cs index 197d6e51..81c053b0 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack.cs @@ -132,8 +132,7 @@ namespace SharpCompress.Compressors.Rar this.fileHeader = fileHeader; this.writeStream = writeStream; this.readStream = readStream; - bool solid = FlagUtility.HasFlag(fileHeader.FileFlags, FileFlags.SOLID); - if (!solid) + if (!fileHeader.IsSolid) { init(null); } @@ -143,13 +142,13 @@ namespace SharpCompress.Compressors.Rar public void doUnpack() { - bool solid = FlagUtility.HasFlag(fileHeader.FileFlags, FileFlags.SOLID); - if (fileHeader.PackingMethod == 0x30) + if (fileHeader.R4PackingMethod == 0x30) { unstoreFile(); return; } - switch (fileHeader.RarVersion) + var solid = fileHeader.IsSolid; + switch (fileHeader.R4RarVersion) { case 15: // rar 1.5 compression unpack15(solid); diff --git a/src/SharpCompress/IO/MarkingBinaryReader.cs b/src/SharpCompress/IO/MarkingBinaryReader.cs index ed33d8a4..1df9e7df 100644 --- a/src/SharpCompress/IO/MarkingBinaryReader.cs +++ b/src/SharpCompress/IO/MarkingBinaryReader.cs @@ -194,5 +194,23 @@ namespace SharpCompress.IO throw new FormatException("malformed vint"); } + + public byte ReadRarVIntByte(int maxBytes = 1) { + int shift = 0; + uint result = 0; + do { + byte b0 = ReadByte(); + var b1 = b0 & 0x7f; + uint n = (uint)b1; + result |= n << shift; + if (b0 == b1) { + return checked((byte)result); + } + shift += 7; + // NOTE: we are too strict here but handling the full range adds complexity and we don't need it + } while (shift < 7 && --maxBytes > 0); + + throw new FormatException("malformed vint"); + } } } \ No newline at end of file diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 751dd29d..a77360fb 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -246,9 +246,15 @@ namespace SharpCompress (UInt16)(iTime % 65536)); } - public static DateTime DosDateToDateTime(Int32 iTime) + /// + /// Convert Unix time value to a DateTime object. + /// + /// The Unix time stamp you want to convert to DateTime. + /// Returns a DateTime object that represents value of the Unix time. + public static DateTime UnixTimeToDateTime(long unixtime) { - return DosDateToDateTime((UInt32)iTime); + DateTime sTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + return sTime.AddSeconds(unixtime); } public static long TransferTo(this Stream source, Stream destination) diff --git a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs index 9d5b07d2..ea42b06c 100644 --- a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs +++ b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs @@ -35,7 +35,7 @@ namespace SharpCompress.Test.Rar { foreach (var header in rarHeaderFactory.ReadHeaders(stream)) { - if (header.HeaderType == HeaderType.ArchiveHeader) + if (header.HeaderType == HeaderType.Archive) { Assert.Equal(isEncrypted, rarHeaderFactory.IsEncrypted); break; From 5f121c5da475b719ea42b27406647acbc08a19e7 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 07:39:02 -0500 Subject: [PATCH 026/124] rar5 wip --- .../Common/Rar/Headers/FileHeader.cs | 4 +- src/SharpCompress/Common/Rar/Headers/Flags.cs | 4 +- .../Common/Rar/Headers/RarHeaderFactory.cs | 48 +++++----- src/SharpCompress/Compressors/Rar/Unpack.cs | 10 +-- src/SharpCompress/IO/MarkingBinaryReader.cs | 90 +++++++++---------- 5 files changed, 76 insertions(+), 80 deletions(-) diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index dd789c6b..27fffe3b 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -9,8 +9,8 @@ namespace SharpCompress.Common.Rar.Headers { private uint fileCrc; - public FileHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.File) { + public FileHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) + : base(header, reader, headerType) { } protected override void ReadFromReader(MarkingBinaryReader reader) diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index dba0d245..0bbe87aa 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -8,9 +8,11 @@ namespace SharpCompress.Common.Rar.Headers Mark, Archive, File, + NewSub, + Service, // Comment, +// Sub // Av, -// Sub, // Protect, // Sign, // NewSub, diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index 005e03ac..9db50d8e 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -100,35 +100,23 @@ namespace SharpCompress.Common.Rar.Headers } case HeaderCodeV.Rar5ServiceHeader: + { + var fh = new FileHeader(header, reader, HeaderType.Service); + SkipData(fh, reader); + return fh; + } + case HeaderCodeV.NewSubHeader: { - var fh = new FileHeader(header, reader); - switch (StreamingMode) - { - case StreamingMode.Seekable: - { - fh.DataStartPosition = reader.BaseStream.Position; - reader.BaseStream.Position += fh.CompressedSize; - } - break; - case StreamingMode.Streaming: - { - //skip the data because it's useless? - reader.BaseStream.Skip(fh.CompressedSize); - } - break; - default: - { - throw new InvalidFormatException("Invalid StreamingMode"); - } - } + var fh = new FileHeader(header, reader, HeaderType.NewSub); + SkipData(fh, reader); return fh; } case HeaderCodeV.Rar5FileHeader: case HeaderCodeV.FileHeader: { - var fh = new FileHeader(header, reader); + var fh = new FileHeader(header, reader, HeaderType.File); switch (StreamingMode) { case StreamingMode.Seekable: @@ -172,5 +160,23 @@ namespace SharpCompress.Common.Rar.Headers } } } + + private void SkipData(FileHeader fh, RarCrcBinaryReader reader) { + switch (StreamingMode) { + case StreamingMode.Seekable: { + fh.DataStartPosition = reader.BaseStream.Position; + reader.BaseStream.Position += fh.CompressedSize; + } + break; + case StreamingMode.Streaming: { + //skip the data because it's useless? + reader.BaseStream.Skip(fh.CompressedSize); + } + break; + default: { + throw new InvalidFormatException("Invalid StreamingMode"); + } + } + } } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Unpack.cs b/src/SharpCompress/Compressors/Rar/Unpack.cs index 81c053b0..78450eeb 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack.cs @@ -154,19 +154,17 @@ namespace SharpCompress.Compressors.Rar unpack15(solid); break; - case 20: - - // rar 2.x compression + case 20: // rar 2.x compression case 26: // files larger than 2GB unpack20(solid); break; - case 29: - - // rar 3.x compression + case 29: // rar 3.x compression case 36: // alternative hash unpack29(solid); break; + + default: throw new InvalidFormatException("unknown rar compression version " + this.fileHeader.R4RarVersion); } } diff --git a/src/SharpCompress/IO/MarkingBinaryReader.cs b/src/SharpCompress/IO/MarkingBinaryReader.cs index 1df9e7df..e1037995 100644 --- a/src/SharpCompress/IO/MarkingBinaryReader.cs +++ b/src/SharpCompress/IO/MarkingBinaryReader.cs @@ -133,82 +133,72 @@ namespace SharpCompress.IO // RAR5 style variable length encoded value // maximum value of 0xffffffffffffffff (64 bits) - // implies max 10 bytes consumed + // technote: "implies max 10 bytes consumed" -- but not really because we could extend indefinitely using 0x80 0x80 ... 0x80 00 // // Variable length integer. Can include one or more bytes, where lower 7 bits of every byte contain integer data // and highest bit in every byte is the continuation flag. If highest bit is 0, this is the last byte in sequence. // So first byte contains 7 least significant bits of integer and continuation flag. Second byte, if present, // contains next 7 bits and so on. - public ulong ReadRarVInt() { + public ulong ReadRarVInt(int maxBytes = 10) { + // hopefully this gets inlined + return DoReadRarVInt((maxBytes - 1) * 7); + } + + private ulong DoReadRarVInt(int maxShift) { int shift = 0; ulong result = 0; do { byte b0 = ReadByte(); - var b1 = b0 & 0x7f; - ulong n = (ulong)b1; - result |= n << shift; + uint b1 = ((uint)b0) & 0x7f; + ulong n = b1; + ulong shifted = n << shift; + if (n != shifted >> shift) { + // overflow + break; + } + result |= shifted; if (b0 == b1) { return result; } - // note: we're actually only allowing a max high bit of 2^56 - // to avoid an extra complex check for shift overflow due to the - // 10th byte having high bits set shift += 7; - } while (shift < 63); + } while (shift <= maxShift); throw new FormatException("malformed vint"); } public uint ReadRarVIntUInt32(int maxBytes = 5) { + // hopefully this gets inlined + return DoReadRarVIntUInt32((maxBytes - 1) * 7); + } + + public ushort ReadRarVIntUInt16(int maxBytes = 3) { + // hopefully this gets inlined + return checked((ushort)DoReadRarVIntUInt32((maxBytes - 1) * 7)); + } + + public byte ReadRarVIntByte(int maxBytes = 2) { + // hopefully this gets inlined + return checked((byte)DoReadRarVIntUInt32((maxBytes - 1) * 7)); + } + + private uint DoReadRarVIntUInt32(int maxShift) { int shift = 0; uint result = 0; do { byte b0 = ReadByte(); - var b1 = b0 & 0x7f; - uint n = (uint)b1; - result |= n << shift; + uint b1 = ((uint)b0) & 0x7f; + uint n = b1; + uint shifted = n << shift; + if (n != shifted >> shift) { + // overflow + break; + } + result |= shifted; if (b0 == b1) { return result; } shift += 7; - // NOTE: we are too strict here but handling the full range adds complexity and we don't need it - } while (shift < 28 && --maxBytes > 0); - - throw new FormatException("malformed vint"); - } - - public ushort ReadRarVIntUInt16(int maxBytes = 3) { - int shift = 0; - uint result = 0; - do { - byte b0 = ReadByte(); - var b1 = b0 & 0x7f; - uint n = (uint)b1; - result |= n << shift; - if (b0 == b1) { - return checked((ushort)result); - } - shift += 7; - // NOTE: we are too strict here but handling the full range adds complexity and we don't need it - } while (shift < 14 && --maxBytes > 0); - - throw new FormatException("malformed vint"); - } - - public byte ReadRarVIntByte(int maxBytes = 1) { - int shift = 0; - uint result = 0; - do { - byte b0 = ReadByte(); - var b1 = b0 & 0x7f; - uint n = (uint)b1; - result |= n << shift; - if (b0 == b1) { - return checked((byte)result); - } - shift += 7; - // NOTE: we are too strict here but handling the full range adds complexity and we don't need it - } while (shift < 7 && --maxBytes > 0); + } while (shift <= maxShift); throw new FormatException("malformed vint"); } From 1c49ff63e227cadc572a2d8060b323f9ac1e0f14 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 08:08:02 -0500 Subject: [PATCH 027/124] rar5: replicate rar test cases --- .../SharpCompress.Test/Rar/RarArchiveTests.cs | 164 ++++++++++++++-- .../Rar/RarHeaderFactoryTest.cs | 37 ++-- .../SharpCompress.Test/Rar/RarReaderTests.cs | 176 ++++++++++++++---- ...udio_program.rar => Rar.Audio_program.rar} | Bin .../{Encrypted.rar => Rar.Encrypted.rar} | Bin ...rt01.rar => Rar.EncryptedParts.part01.rar} | Bin ...rt02.rar => Rar.EncryptedParts.part02.rar} | Bin ...rt03.rar => Rar.EncryptedParts.part03.rar} | Bin ...rt04.rar => Rar.EncryptedParts.part04.rar} | Bin ...rt05.rar => Rar.EncryptedParts.part05.rar} | Bin ...rt06.rar => Rar.EncryptedParts.part06.rar} | Bin .../Archives/{Rarjpeg.jpg => Rar.jpeg.jpg} | Bin ...ttime.rar => Rar.test_invalid_exttime.rar} | Bin 13 files changed, 305 insertions(+), 72 deletions(-) rename tests/TestArchives/Archives/{Audio_program.rar => Rar.Audio_program.rar} (100%) rename tests/TestArchives/Archives/{Encrypted.rar => Rar.Encrypted.rar} (100%) rename tests/TestArchives/Archives/{EncryptedParts.part01.rar => Rar.EncryptedParts.part01.rar} (100%) rename tests/TestArchives/Archives/{EncryptedParts.part02.rar => Rar.EncryptedParts.part02.rar} (100%) rename tests/TestArchives/Archives/{EncryptedParts.part03.rar => Rar.EncryptedParts.part03.rar} (100%) rename tests/TestArchives/Archives/{EncryptedParts.part04.rar => Rar.EncryptedParts.part04.rar} (100%) rename tests/TestArchives/Archives/{EncryptedParts.part05.rar => Rar.EncryptedParts.part05.rar} (100%) rename tests/TestArchives/Archives/{EncryptedParts.part06.rar => Rar.EncryptedParts.part06.rar} (100%) rename tests/TestArchives/Archives/{Rarjpeg.jpg => Rar.jpeg.jpg} (100%) rename tests/TestArchives/Archives/{test_invalid_exttime.rar => Rar.test_invalid_exttime.rar} (100%) diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index 55d79994..54619c92 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -16,16 +16,34 @@ namespace SharpCompress.Test.Rar ReadRarPassword("Rar.encrypted_filesAndHeader.rar", "test"); } + [Fact] + public void Rar5_EncryptedFileAndHeader_Archive() + { + ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test"); + } + [Fact] public void Rar_EncryptedFileOnly_Archive() { ReadRarPassword("Rar.encrypted_filesOnly.rar", "test"); } + [Fact] + public void Rar5_EncryptedFileOnly_Archive() + { + ReadRarPassword("Rar5.encrypted_filesOnly.rar", "test"); + } + [Fact] public void Rar_Encrypted_Archive() { - ReadRarPassword("Encrypted.rar", "test"); + ReadRarPassword("Rar.Encrypted.rar", "test"); + } + + [Fact] + public void Rar5_Encrypted_Archive() + { + ReadRarPassword("Rar5.Encrypted.rar", "test"); } private void ReadRarPassword(string testArchive, string password) @@ -57,7 +75,13 @@ namespace SharpCompress.Test.Rar [Fact] public void Rar_Multi_Archive_Encrypted() { - Assert.Throws(() => ArchiveFileReadPassword("EncryptedParts.part01.rar", "test")); + Assert.Throws(() => ArchiveFileReadPassword("Rar.EncryptedParts.part01.rar", "test")); + } + + [Fact] + public void Rar5_Multi_Archive_Encrypted() + { + Assert.Throws(() => ArchiveFileReadPassword("Rar5.EncryptedParts.part01.rar", "test")); } protected void ArchiveFileReadPassword(string archiveName, string password) @@ -88,6 +112,12 @@ namespace SharpCompress.Test.Rar ArchiveStreamRead("Rar.none.rar"); } + [Fact] + public void Rar5_None_ArchiveStreamRead() + { + ArchiveStreamRead("Rar5.none.rar"); + } + [Fact] public void Rar_ArchiveStreamRead() { @@ -95,10 +125,25 @@ namespace SharpCompress.Test.Rar } [Fact] - public void Rar_test_invalid_exttime_ArchiveStreamRead() + public void Rar5_ArchiveStreamRead() + { + ArchiveStreamRead("Rar5.rar"); + } + + [Fact] + public void Rar_test_invalid_exttime_ArchiveStreamRead() { + DoRar_test_invalid_exttime_ArchiveStreamRead("Rar.test_invalid_exttime.rar"); + } + + [Fact] + public void Rar5_test_invalid_exttime_ArchiveStreamRead() { + DoRar_test_invalid_exttime_ArchiveStreamRead("Rar5.test_invalid_exttime.rar"); + } + + private void DoRar_test_invalid_exttime_ArchiveStreamRead(string filename) { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "test_invalid_exttime.rar"))) + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) { using (var archive = ArchiveFactory.Open(stream)) { @@ -118,7 +163,7 @@ namespace SharpCompress.Test.Rar public void Rar_Jpg_ArchiveStreamRead() { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rarjpeg.jpg"))) + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"))) { using (var archive = RarArchive.Open(stream, new ReaderOptions() { @@ -140,17 +185,26 @@ namespace SharpCompress.Test.Rar } [Fact] - public void Rar_IsSolidArchiveCheck() + public void Rar_IsSolidArchiveCheck() { + DoRar_IsSolidArchiveCheck("Rar.rar"); + } + + [Fact] + public void Rar5_IsSolidArchiveCheck() { + DoRar_IsSolidArchiveCheck("Rar5.rar"); + } + + private void DoRar_IsSolidArchiveCheck(string filename) { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.rar"))) + using (var stream = File.OpenRead(Path.Combine(this.TEST_ARCHIVES_PATH, filename))) { using (var archive = RarArchive.Open(stream)) { Assert.False(archive.IsSolid); foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - entry.WriteToDirectory(SCRATCH_FILES_PATH, + entry.WriteToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, @@ -168,6 +222,12 @@ namespace SharpCompress.Test.Rar Assert.Throws(() => ArchiveStreamRead("Rar.solid.rar")); } + [Fact] + public void Rar5_Solid_ArchiveStreamRead() + { + Assert.Throws(() => ArchiveStreamRead("Rar5.solid.rar")); + } + [Fact] public void Rar_Solid_StreamRead_Extract_All() { @@ -175,23 +235,42 @@ namespace SharpCompress.Test.Rar } [Fact] - public void Rar_Multi_ArchiveStreamRead() + public void Rar5_Solid_StreamRead_Extract_All() { - var testArchives = new string[] { "Rar.multi.part01.rar", + ArchiveStreamReadExtractAll("Rar5.solid.rar", CompressionType.Rar); + } + + [Fact] + public void Rar_Multi_ArchiveStreamRead() { + DoRar_Multi_ArchiveStreamRead(new string[] { + "Rar.multi.part01.rar", "Rar.multi.part02.rar", "Rar.multi.part03.rar", "Rar.multi.part04.rar", "Rar.multi.part05.rar", - "Rar.multi.part06.rar"}; + "Rar.multi.part06.rar"}); + } + [Fact] + public void Rar5_Multi_ArchiveStreamRead() { + DoRar_Multi_ArchiveStreamRead(new string[] { + "Rar5.multi.part01.rar", + "Rar5.multi.part02.rar", + "Rar5.multi.part03.rar", + "Rar5.multi.part04.rar", + "Rar5.multi.part05.rar", + "Rar5.multi.part06.rar"}); + } + private void DoRar_Multi_ArchiveStreamRead(string[] archives) + { ResetScratch(); - using (var archive = RarArchive.Open(testArchives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) + using (var archive = RarArchive.Open(archives.Select(s => Path.Combine(this.TEST_ARCHIVES_PATH, s)) .Select(File.OpenRead))) { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - entry.WriteToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() + entry.WriteToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true @@ -220,10 +299,25 @@ namespace SharpCompress.Test.Rar } [Fact] - public void Rar_ArchiveFileRead_HasDirectories() + public void Rar5_ArchiveFileRead() + { + ArchiveFileRead("Rar5.rar"); + } + + [Fact] + public void Rar_ArchiveFileRead_HasDirectories() { + DoRar_ArchiveFileRead_HasDirectories("Rar.rar"); + } + + [Fact] + public void Rar5_ArchiveFileRead_HasDirectories() { + DoRar_ArchiveFileRead_HasDirectories("Rar5.rar"); + } + + private void DoRar_ArchiveFileRead_HasDirectories(string filename) { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.rar"))) + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) { using (var archive = RarArchive.Open(stream)) { @@ -237,7 +331,7 @@ namespace SharpCompress.Test.Rar public void Rar_Jpg_ArchiveFileRead() { ResetScratch(); - using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, "Rarjpeg.jpg"), new ReaderOptions() + using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"), new ReaderOptions() { LookForHeader = true })) @@ -260,6 +354,12 @@ namespace SharpCompress.Test.Rar Assert.Throws(() => ArchiveFileRead("Rar.solid.rar")); } + [Fact] + public void Rar5_Solid_ArchiveFileRead() + { + Assert.Throws(() => ArchiveFileRead("Rar5.solid.rar")); + } + [Fact] public void Rar_Multi_ArchiveFileRead() { @@ -267,9 +367,24 @@ namespace SharpCompress.Test.Rar } [Fact] - public void Rar_IsFirstVolume_True() + public void Rar5_Multi_ArchiveFileRead() { - using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part01.rar"))) + ArchiveFileRead("Rar5.multi.part01.rar"); + } + + [Fact] + public void Rar_IsFirstVolume_True() { + DoRar_IsFirstVolume_True("Rar.multi.part01.rar"); + } + + [Fact] + public void Rar5_IsFirstVolume_True() { + DoRar_IsFirstVolume_True("Rar5.multi.part01.rar"); + } + + private void DoRar_IsFirstVolume_True(string firstFilename) + { + using (var archive = RarArchive.Open(Path.Combine(this.TEST_ARCHIVES_PATH, firstFilename))) { Assert.True(archive.IsMultipartVolume()); Assert.True(archive.IsFirstVolume()); @@ -277,9 +392,18 @@ namespace SharpCompress.Test.Rar } [Fact] - public void Rar_IsFirstVolume_False() + public void Rar_IsFirstVolume_False() { + DoRar_IsFirstVolume_False("Rar.multi.part03.rar"); + } + + [Fact] + public void Rar5_IsFirstVolume_False() { + DoRar_IsFirstVolume_False("Rar5.multi.part03.rar"); + } + + private void DoRar_IsFirstVolume_False(string notFirstFilename) { - using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part03.rar"))) + using (var archive = RarArchive.Open(Path.Combine(this.TEST_ARCHIVES_PATH, notFirstFilename))) { Assert.True(archive.IsMultipartVolume()); Assert.False(archive.IsFirstVolume()); diff --git a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs index ea42b06c..f334903b 100644 --- a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs +++ b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs @@ -16,19 +16,35 @@ namespace SharpCompress.Test.Rar public RarHeaderFactoryTest() { ResetScratch(); - rarHeaderFactory = new RarHeaderFactory(StreamingMode.Seekable, new ReaderOptions() - { - LeaveStreamOpen = true - }); + rarHeaderFactory = new RarHeaderFactory( + StreamingMode.Seekable, + new ReaderOptions { LeaveStreamOpen = true }); } - [Fact] - public void ReadHeaders_RecognizeEncryptedFlag() + public void Rar_ReadHeaders_RecognizeEncryptedFlag() { ReadEncryptedFlag("Rar.encrypted_filesAndHeader.rar", true); } + [Fact] + public void Rar5_ReadHeaders_RecognizeEncryptedFlag() + { + ReadEncryptedFlag("Rar5.encrypted_filesAndHeader.rar", true); + } + + [Fact] + public void Rar_ReadHeaders_RecognizeNoEncryptedFlag() + { + ReadEncryptedFlag("Rar.rar", false); + } + + [Fact] + public void Rar5_ReadHeaders_RecognizeNoEncryptedFlag() + { + ReadEncryptedFlag("Rar5.rar", false); + } + private void ReadEncryptedFlag(string testArchive, bool isEncrypted) { using (var stream = GetReaderStream(testArchive)) @@ -44,16 +60,9 @@ namespace SharpCompress.Test.Rar } } - [Fact] - public void ReadHeaders_RecognizeNoEncryptedFlag() - { - ReadEncryptedFlag("Rar.rar", false); - } - private FileStream GetReaderStream(string testArchive) { - return new FileStream(Path.Combine(TEST_ARCHIVES_PATH, testArchive), - FileMode.Open); + return new FileStream(Path.Combine(TEST_ARCHIVES_PATH, testArchive), FileMode.Open); } } } diff --git a/tests/SharpCompress.Test/Rar/RarReaderTests.cs b/tests/SharpCompress.Test/Rar/RarReaderTests.cs index aa6d5aa0..bc98dfab 100644 --- a/tests/SharpCompress.Test/Rar/RarReaderTests.cs +++ b/tests/SharpCompress.Test/Rar/RarReaderTests.cs @@ -10,23 +10,36 @@ namespace SharpCompress.Test.Rar public class RarReaderTests : ReaderTests { [Fact] - public void Rar_Multi_Reader() - { - var testArchives = new string[] { "Rar.multi.part01.rar", + public void Rar_Multi_Reader() { + DoRar_Multi_Reader(new string[] { + "Rar.multi.part01.rar", "Rar.multi.part02.rar", "Rar.multi.part03.rar", "Rar.multi.part04.rar", "Rar.multi.part05.rar", - "Rar.multi.part06.rar"}; + "Rar.multi.part06.rar"}); + } + [Fact] + public void Rar5_Multi_Reader() { + DoRar_Multi_Reader(new string[] { + "Rar5.multi.part01.rar", + "Rar5.multi.part02.rar", + "Rar5.multi.part03.rar", + "Rar5.multi.part04.rar", + "Rar5.multi.part05.rar", + "Rar5.multi.part06.rar"}); + } + private void DoRar_Multi_Reader(string[] archives) + { ResetScratch(); - using (var reader = RarReader.Open(testArchives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) + using (var reader = RarReader.Open(archives.Select(s => Path.Combine(this.TEST_ARCHIVES_PATH, s)) .Select(p => File.OpenRead(p)))) { while (reader.MoveToNextEntry()) { - reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() + reader.WriteEntryToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true @@ -37,20 +50,33 @@ namespace SharpCompress.Test.Rar } [Fact] - public void Rar_Multi_Reader_Encrypted() + public void Rar_Multi_Reader_Encrypted() { + DoRar_Multi_Reader_Encrypted(new string[] { + "Rar.EncryptedParts.part01.rar", + "Rar.EncryptedParts.part02.rar", + "Rar.EncryptedParts.part03.rar", + "Rar.EncryptedParts.part04.rar", + "Rar.EncryptedParts.part05.rar", + "Rar.EncryptedParts.part06.rar"}); + } + + [Fact] + public void Rar5_Multi_Reader_Encrypted() { + DoRar_Multi_Reader_Encrypted(new string[] { + "Rar5.EncryptedParts.part01.rar", + "Rar5.EncryptedParts.part02.rar", + "Rar5.EncryptedParts.part03.rar", + "Rar5.EncryptedParts.part04.rar", + "Rar5.EncryptedParts.part05.rar", + "Rar5.EncryptedParts.part06.rar"}); + } + + private void DoRar_Multi_Reader_Encrypted(string[] archives) { - var testArchives = new string[] { "EncryptedParts.part01.rar", - "EncryptedParts.part02.rar", - "EncryptedParts.part03.rar", - "EncryptedParts.part04.rar", - "EncryptedParts.part05.rar", - "EncryptedParts.part06.rar"}; - - Assert.Throws(() => { ResetScratch(); - using (var reader = RarReader.Open(testArchives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) + using (var reader = RarReader.Open(archives.Select(s => Path.Combine(this.TEST_ARCHIVES_PATH, s)) .Select(p => File.OpenRead(p)), new ReaderOptions() { @@ -59,7 +85,7 @@ namespace SharpCompress.Test.Rar { while (reader.MoveToNextEntry()) { - reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, + reader.WriteEntryToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, @@ -72,28 +98,41 @@ namespace SharpCompress.Test.Rar } [Fact] - public void Rar_Multi_Reader_Delete_Files() - { - var testArchives = new string[] { "Rar.multi.part01.rar", + public void Rar_Multi_Reader_Delete_Files() { + DoRar_Multi_Reader_Delete_Files(new string[] { + "Rar.multi.part01.rar", "Rar.multi.part02.rar", "Rar.multi.part03.rar", "Rar.multi.part04.rar", "Rar.multi.part05.rar", - "Rar.multi.part06.rar"}; + "Rar.multi.part06.rar"}); + } + [Fact] + public void Rar5_Multi_Reader_Delete_Files() { + DoRar_Multi_Reader_Delete_Files(new string[] { + "Rar5.multi.part01.rar", + "Rar5.multi.part02.rar", + "Rar5.multi.part03.rar", + "Rar5.multi.part04.rar", + "Rar5.multi.part05.rar", + "Rar5.multi.part06.rar"}); + } + private void DoRar_Multi_Reader_Delete_Files(string[] archives) + { ResetScratch(); - foreach (var file in testArchives) + foreach (var file in archives) { - File.Copy(Path.Combine(TEST_ARCHIVES_PATH, file), Path.Combine(SCRATCH2_FILES_PATH, file)); + File.Copy(Path.Combine(this.TEST_ARCHIVES_PATH, file), Path.Combine(this.SCRATCH2_FILES_PATH, file)); } - var streams = testArchives.Select(s => Path.Combine(SCRATCH2_FILES_PATH, s)).Select(File.OpenRead).ToList(); + var streams = archives.Select(s => Path.Combine(this.SCRATCH2_FILES_PATH, s)).Select(File.OpenRead).ToList(); using (var reader = RarReader.Open(streams)) { while (reader.MoveToNextEntry()) { - reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() + reader.WriteEntryToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true @@ -106,7 +145,7 @@ namespace SharpCompress.Test.Rar } VerifyFiles(); - foreach (var file in testArchives.Select(s => Path.Combine(SCRATCH2_FILES_PATH, s))) + foreach (var file in archives.Select(s => Path.Combine(this.SCRATCH2_FILES_PATH, s))) { File.Delete(file); } @@ -118,30 +157,58 @@ namespace SharpCompress.Test.Rar Read("Rar.none.rar", CompressionType.Rar); } + [Fact] + public void Rar5_None_Reader() + { + Read("Rar5.none.rar", CompressionType.Rar); + } + [Fact] public void Rar_Reader() { Read("Rar.rar", CompressionType.Rar); } + [Fact] + public void Rar5_Reader() + { + Read("Rar5.rar", CompressionType.Rar); + } + [Fact] public void Rar_EncryptedFileAndHeader_Reader() { ReadRar("Rar.encrypted_filesAndHeader.rar", "test"); + } + [Fact] + public void Rar5_EncryptedFileAndHeader_Reader() + { + ReadRar("Rar5.encrypted_filesAndHeader.rar", "test"); } [Fact] public void Rar_EncryptedFileOnly_Reader() { ReadRar("Rar.encrypted_filesOnly.rar", "test"); + } + [Fact] + public void Rar5_EncryptedFileOnly_Reader() + { + ReadRar("Rar5.encrypted_filesOnly.rar", "test"); } [Fact] public void Rar_Encrypted_Reader() { - ReadRar("Encrypted.rar", "test"); + ReadRar("Rar.Encrypted.rar", "test"); + } + + [Fact] + public void Rar5_Encrypted_Reader() + { + ReadRar("Rar5.Encrypted.rar", "test"); } private void ReadRar(string testArchive, string password) @@ -170,10 +237,19 @@ namespace SharpCompress.Test.Rar } [Fact] - public void Rar_Entry_Stream() + public void Rar_Entry_Stream() { + DoRar_Entry_Stream("Rar.rar"); + } + + [Fact] + public void Rar5_Entry_Stream() { + DoRar_Entry_Stream("Rar5.rar"); + } + + private void DoRar_Entry_Stream(string filename) { ResetScratch(); - using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.rar"))) + using (Stream stream = File.OpenRead(Path.Combine(this.TEST_ARCHIVES_PATH, filename))) using (var reader = RarReader.Open(stream)) { while (reader.MoveToNextEntry()) @@ -185,7 +261,7 @@ namespace SharpCompress.Test.Rar { string file = Path.GetFileName(reader.Entry.Key); string folder = Path.GetDirectoryName(reader.Entry.Key); - string destdir = Path.Combine(SCRATCH_FILES_PATH, folder); + string destdir = Path.Combine(this.SCRATCH_FILES_PATH, folder); if (!Directory.Exists(destdir)) { Directory.CreateDirectory(destdir); @@ -207,7 +283,7 @@ namespace SharpCompress.Test.Rar public void Rar_Reader_Audio_program() { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Audio_program.rar"))) + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.Audio_program.rar"))) using (var reader = RarReader.Open(stream, new ReaderOptions() { LookForHeader = true @@ -231,7 +307,7 @@ namespace SharpCompress.Test.Rar public void Rar_Jpg_Reader() { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rarjpeg.jpg"))) + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.jpeg.jpg"))) using (var reader = RarReader.Open(stream, new ReaderOptions() { LookForHeader = true @@ -257,10 +333,25 @@ namespace SharpCompress.Test.Rar } [Fact] - public void Rar_Solid_Skip_Reader() + public void Rar5_Solid_Reader() + { + Read("Rar5.solid.rar", CompressionType.Rar); + } + + [Fact] + public void Rar_Solid_Skip_Reader() { + DoRar_Solid_Skip_Reader("Rar.solid.rar"); + } + + [Fact] + public void Rar5_Solid_Skip_Reader() { + DoRar_Solid_Skip_Reader("Rar5.solid.rar"); + } + + private void DoRar_Solid_Skip_Reader(string filename) { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.solid.rar"))) + using (var stream = File.OpenRead(Path.Combine(this.TEST_ARCHIVES_PATH, filename))) using (var reader = RarReader.Open(stream, new ReaderOptions() { LookForHeader = true @@ -271,7 +362,7 @@ namespace SharpCompress.Test.Rar if (reader.Entry.Key.Contains("jpg")) { Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); - reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() + reader.WriteEntryToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true @@ -282,10 +373,19 @@ namespace SharpCompress.Test.Rar } [Fact] - public void Rar_Reader_Skip() + public void Rar_Reader_Skip() { + DoRar_Reader_Skip("Rar.rar"); + } + + [Fact] + public void Rar5_Reader_Skip() { + DoRar_Reader_Skip("Rar5.rar"); + } + + private void DoRar_Reader_Skip(string filename) { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Rar.rar"))) + using (var stream = File.OpenRead(Path.Combine(this.TEST_ARCHIVES_PATH, filename))) using (var reader = RarReader.Open(stream, new ReaderOptions() { LookForHeader = true @@ -296,7 +396,7 @@ namespace SharpCompress.Test.Rar if (reader.Entry.Key.Contains("jpg")) { Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); - reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() + reader.WriteEntryToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true diff --git a/tests/TestArchives/Archives/Audio_program.rar b/tests/TestArchives/Archives/Rar.Audio_program.rar similarity index 100% rename from tests/TestArchives/Archives/Audio_program.rar rename to tests/TestArchives/Archives/Rar.Audio_program.rar diff --git a/tests/TestArchives/Archives/Encrypted.rar b/tests/TestArchives/Archives/Rar.Encrypted.rar similarity index 100% rename from tests/TestArchives/Archives/Encrypted.rar rename to tests/TestArchives/Archives/Rar.Encrypted.rar diff --git a/tests/TestArchives/Archives/EncryptedParts.part01.rar b/tests/TestArchives/Archives/Rar.EncryptedParts.part01.rar similarity index 100% rename from tests/TestArchives/Archives/EncryptedParts.part01.rar rename to tests/TestArchives/Archives/Rar.EncryptedParts.part01.rar diff --git a/tests/TestArchives/Archives/EncryptedParts.part02.rar b/tests/TestArchives/Archives/Rar.EncryptedParts.part02.rar similarity index 100% rename from tests/TestArchives/Archives/EncryptedParts.part02.rar rename to tests/TestArchives/Archives/Rar.EncryptedParts.part02.rar diff --git a/tests/TestArchives/Archives/EncryptedParts.part03.rar b/tests/TestArchives/Archives/Rar.EncryptedParts.part03.rar similarity index 100% rename from tests/TestArchives/Archives/EncryptedParts.part03.rar rename to tests/TestArchives/Archives/Rar.EncryptedParts.part03.rar diff --git a/tests/TestArchives/Archives/EncryptedParts.part04.rar b/tests/TestArchives/Archives/Rar.EncryptedParts.part04.rar similarity index 100% rename from tests/TestArchives/Archives/EncryptedParts.part04.rar rename to tests/TestArchives/Archives/Rar.EncryptedParts.part04.rar diff --git a/tests/TestArchives/Archives/EncryptedParts.part05.rar b/tests/TestArchives/Archives/Rar.EncryptedParts.part05.rar similarity index 100% rename from tests/TestArchives/Archives/EncryptedParts.part05.rar rename to tests/TestArchives/Archives/Rar.EncryptedParts.part05.rar diff --git a/tests/TestArchives/Archives/EncryptedParts.part06.rar b/tests/TestArchives/Archives/Rar.EncryptedParts.part06.rar similarity index 100% rename from tests/TestArchives/Archives/EncryptedParts.part06.rar rename to tests/TestArchives/Archives/Rar.EncryptedParts.part06.rar diff --git a/tests/TestArchives/Archives/Rarjpeg.jpg b/tests/TestArchives/Archives/Rar.jpeg.jpg similarity index 100% rename from tests/TestArchives/Archives/Rarjpeg.jpg rename to tests/TestArchives/Archives/Rar.jpeg.jpg diff --git a/tests/TestArchives/Archives/test_invalid_exttime.rar b/tests/TestArchives/Archives/Rar.test_invalid_exttime.rar similarity index 100% rename from tests/TestArchives/Archives/test_invalid_exttime.rar rename to tests/TestArchives/Archives/Rar.test_invalid_exttime.rar From 85631795921de9b8aa3729866062cbc46abfa87b Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 08:33:46 -0500 Subject: [PATCH 028/124] rar5: wip --- .../Common/Rar/Headers/AVHeader.cs | 26 ++++++++----- .../Common/Rar/Headers/CommentHeader.cs | 5 +++ src/SharpCompress/Common/Rar/Headers/Flags.cs | 14 +++---- .../{NewSubHeader.cs => NewSubHeaderType.cs} | 0 .../Common/Rar/Headers/ProtectHeader.cs | 5 +++ .../Common/Rar/Headers/RarHeader.cs | 24 ------------ .../Common/Rar/Headers/RarHeaderFactory.cs | 3 +- .../Common/Rar/Headers/SignHeader.cs | 24 ++++++------ src/SharpCompress/Compressors/Rar/Unpack.cs | 38 +++++++++++++++++++ 9 files changed, 84 insertions(+), 55 deletions(-) rename src/SharpCompress/Common/Rar/Headers/{NewSubHeader.cs => NewSubHeaderType.cs} (100%) diff --git a/src/SharpCompress/Common/Rar/Headers/AVHeader.cs b/src/SharpCompress/Common/Rar/Headers/AVHeader.cs index acb878ba..ce18fc42 100644 --- a/src/SharpCompress/Common/Rar/Headers/AVHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/AVHeader.cs @@ -2,19 +2,27 @@ namespace SharpCompress.Common.Rar.Headers { - internal class SignHeader : RarHeader + internal class AVHeader : RarHeader { - protected override void ReadFromReader(MarkingBinaryReader reader) - { - CreationTime = reader.ReadInt32(); - ArcNameSize = reader.ReadInt16(); - UserNameSize = reader.ReadInt16(); + public AVHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.Av) { + if (IsRar5) throw new InvalidFormatException("unexpected rar5 record"); } - internal int CreationTime { get; private set; } + protected override void ReadFromReader(MarkingBinaryReader reader) + { + UnpackVersion = reader.ReadByte(); + Method = reader.ReadByte(); + AVVersion = reader.ReadByte(); + AVInfoCRC = reader.ReadInt32(); + } - internal short ArcNameSize { get; private set; } + internal int AVInfoCRC { get; private set; } - internal short UserNameSize { get; private set; } + internal byte UnpackVersion { get; private set; } + + internal byte Method { get; private set; } + + internal byte AVVersion { get; private set; } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs b/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs index c9943bde..c9d71b0a 100644 --- a/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs @@ -4,6 +4,11 @@ namespace SharpCompress.Common.Rar.Headers { internal class CommentHeader : RarHeader { + protected CommentHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.Comment) { + if (IsRar5) throw new InvalidFormatException("unexpected rar5 record"); + } + protected override void ReadFromReader(MarkingBinaryReader reader) { UnpSize = reader.ReadInt16(); diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index 0bbe87aa..6f072396 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -1,5 +1,3 @@ -using System; - namespace SharpCompress.Common.Rar.Headers { internal enum HeaderType : byte @@ -8,14 +6,12 @@ namespace SharpCompress.Common.Rar.Headers Mark, Archive, File, - NewSub, Service, -// Comment, -// Sub -// Av, -// Protect, -// Sign, -// NewSub, + Comment, + Av, + Protect, + Sign, + NewSub, EndArchive } diff --git a/src/SharpCompress/Common/Rar/Headers/NewSubHeader.cs b/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs similarity index 100% rename from src/SharpCompress/Common/Rar/Headers/NewSubHeader.cs rename to src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs diff --git a/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs b/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs index 75dbcb9e..19529388 100644 --- a/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs @@ -5,6 +5,11 @@ namespace SharpCompress.Common.Rar.Headers // ProtectHeader is part of the Recovery Record feature internal class ProtectHeader : RarHeader { + public ProtectHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.Protect) { + if (IsRar5) throw new InvalidFormatException("unexpected rar5 record"); + } + protected override void ReadFromReader(MarkingBinaryReader reader) { Version = reader.ReadByte(); diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index e81f82f2..ca5ca38f 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -9,11 +9,6 @@ namespace SharpCompress.Common.Rar.Headers private readonly HeaderType headerType; private readonly bool isRar5; - protected RarHeader() { - //TODO - throw new NotImplementedException(); - } - private RarHeader(bool isRar5) { this.headerType = HeaderType.Null; @@ -100,25 +95,6 @@ namespace SharpCompress.Common.Rar.Headers { throw new NotImplementedException(); } -//x - internal T PromoteHeader(RarCrcBinaryReader reader) - where T : RarHeader, new() - { - T header = new T(); - header.FillBase(this); - header.ReadFromReader(reader); - header.ReadBytes = reader.CurrentReadByteCount; - - int headerSizeDiff = header.HeaderSize - (int)header.ReadBytes; - if (headerSizeDiff > 0) - { - reader.ReadBytes(headerSizeDiff); - } - - VerifyHeaderCrc(reader.GetCrc32()); - - return header; - } private void VerifyHeaderCrc(uint crc32) { diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index 9db50d8e..3dbb9f21 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -75,8 +75,7 @@ namespace SharpCompress.Common.Rar.Headers case HeaderCodeV.ProtectHeader: { - ProtectHeader ph = header.PromoteHeader(reader); - + var ph = new ProtectHeader(header, reader); // skip the recovery record data, we do not use it. switch (StreamingMode) { diff --git a/src/SharpCompress/Common/Rar/Headers/SignHeader.cs b/src/SharpCompress/Common/Rar/Headers/SignHeader.cs index 708241d8..2f945878 100644 --- a/src/SharpCompress/Common/Rar/Headers/SignHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/SignHeader.cs @@ -2,22 +2,24 @@ namespace SharpCompress.Common.Rar.Headers { - internal class AVHeader : RarHeader + internal class SignHeader : RarHeader { - protected override void ReadFromReader(MarkingBinaryReader reader) - { - UnpackVersion = reader.ReadByte(); - Method = reader.ReadByte(); - AVVersion = reader.ReadByte(); - AVInfoCRC = reader.ReadInt32(); + protected SignHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.Sign) { + if (IsRar5) throw new InvalidFormatException("unexpected rar5 record"); } - internal int AVInfoCRC { get; private set; } + protected override void ReadFromReader(MarkingBinaryReader reader) + { + CreationTime = reader.ReadInt32(); + ArcNameSize = reader.ReadInt16(); + UserNameSize = reader.ReadInt16(); + } - internal byte UnpackVersion { get; private set; } + internal int CreationTime { get; private set; } - internal byte Method { get; private set; } + internal short ArcNameSize { get; private set; } - internal byte AVVersion { get; private set; } + internal short UserNameSize { get; private set; } } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Unpack.cs b/src/SharpCompress/Compressors/Rar/Unpack.cs index 78450eeb..40d3087c 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack.cs @@ -150,6 +150,44 @@ namespace SharpCompress.Compressors.Rar var solid = fileHeader.IsSolid; switch (fileHeader.R4RarVersion) { + case 0: // no compression + unstoreFile(); + break; + + case 15: // rar 1.5 compression + unpack15(solid); + break; + + case 20: // rar 2.x compression + case 26: // files larger than 2GB + unpack20(solid); + break; + + case 29: // rar 3.x compression + case 36: // alternative hash + unpack29(solid); + break; + + default: throw new InvalidFormatException("unknown rar compression version " + this.fileHeader.R4RarVersion); + } + } + + public void doUnpackNEW() + { + var compressionMethod = fileHeader.R4RarVersion; + //??? if (fileHeader.R4PackingMethod == 0x30) +// { +// unstoreFile(); +// return; +// } + var solid = fileHeader.IsSolid; + //switch (fileHeader.R4RarVersion) + switch (compressionMethod) + { + case 0: // no compression + unstoreFile(); + break; + case 15: // rar 1.5 compression unpack15(solid); break; From c9d3563f31c473af8b7adc0af278f5c49b44ba95 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 09:00:11 -0500 Subject: [PATCH 029/124] rar5: some cleanup --- .../Common/Rar/Headers/AVHeader.cs | 5 ++- .../Common/Rar/Headers/ArchiveHeader.cs | 32 +++++++------- .../Common/Rar/Headers/CommentHeader.cs | 5 ++- .../Common/Rar/Headers/EndArchiveHeader.cs | 21 ++++----- .../Common/Rar/Headers/FileHeader.cs | 43 ++++++++++--------- .../Common/Rar/Headers/IRarHeader.cs | 3 +- .../Common/Rar/Headers/NewSubHeaderType.cs | 4 -- .../Common/Rar/Headers/ProtectHeader.cs | 5 ++- .../Common/Rar/Headers/RarHeader.cs | 40 +++++++---------- .../Common/Rar/Headers/RarHeaderFactory.cs | 14 +++--- .../Common/Rar/Headers/SignHeader.cs | 5 ++- .../Common/Rar/RarCrcBinaryReader.cs | 3 +- .../Common/Rar/RarCryptoBinaryReader.cs | 21 --------- src/SharpCompress/Common/Rar/RarVolume.cs | 1 - 14 files changed, 90 insertions(+), 112 deletions(-) diff --git a/src/SharpCompress/Common/Rar/Headers/AVHeader.cs b/src/SharpCompress/Common/Rar/Headers/AVHeader.cs index ce18fc42..73c1f3bd 100644 --- a/src/SharpCompress/Common/Rar/Headers/AVHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/AVHeader.cs @@ -5,11 +5,12 @@ namespace SharpCompress.Common.Rar.Headers internal class AVHeader : RarHeader { public AVHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.Av) { + : base(header, reader, HeaderType.Av) + { if (IsRar5) throw new InvalidFormatException("unexpected rar5 record"); } - protected override void ReadFromReader(MarkingBinaryReader reader) + protected override void ReadFinish(MarkingBinaryReader reader) { UnpackVersion = reader.ReadByte(); Method = reader.ReadByte(); diff --git a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs index d9630296..083a0bf2 100644 --- a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs @@ -5,15 +5,16 @@ namespace SharpCompress.Common.Rar.Headers internal class ArchiveHeader : RarHeader { public ArchiveHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.Archive) { + : base(header, reader, HeaderType.Archive) + { } - protected override void ReadFromReader(MarkingBinaryReader reader) + protected override void ReadFinish(MarkingBinaryReader reader) { - if (this.IsRar5) + if (IsRar5) { - ArchiveHeaderFlags = reader.ReadRarVIntUInt16(); - if (HasArchiveHeaderFlag(ArchiveFlagsV5.HasVolumeNumber)) + Flags = reader.ReadRarVIntUInt16(); + if (HasFlag(ArchiveFlagsV5.HasVolumeNumber)) { VolumeNumber = (int)reader.ReadRarVIntUInt32(); } @@ -23,10 +24,10 @@ namespace SharpCompress.Common.Rar.Headers } else { - ArchiveHeaderFlags = HeaderFlags; + Flags = HeaderFlags; HighPosAv = reader.ReadInt16(); PosAv = reader.ReadInt32(); - if (HasArchiveHeaderFlag(ArchiveFlagsV4.EncryptVer)) + if (HasFlag(ArchiveFlagsV4.EncryptVer)) { EncryptionVersion = reader.ReadByte(); } @@ -34,6 +35,7 @@ namespace SharpCompress.Common.Rar.Headers } private void ReadLocator(MarkingBinaryReader reader) { + // parse fields, unused var size = reader.ReadRarVIntUInt16(); var type = reader.ReadRarVIntUInt16(); if (type != 1) throw new InvalidFormatException("expected locator record"); @@ -50,11 +52,11 @@ namespace SharpCompress.Common.Rar.Headers } } - private ushort ArchiveHeaderFlags { get; set; } + private ushort Flags { get; set; } - private bool HasArchiveHeaderFlag(ushort flag) + private bool HasFlag(ushort flag) { - return (ArchiveHeaderFlags & flag) == flag; + return (Flags & flag) == flag; } internal int? VolumeNumber { get; private set; } @@ -65,14 +67,14 @@ namespace SharpCompress.Common.Rar.Headers private byte? EncryptionVersion { get; set; } - public bool? IsEncrypted => this.IsRar5 ? (bool?)null : HasArchiveHeaderFlag(ArchiveFlagsV4.Password); + public bool? IsEncrypted => IsRar5 ? (bool?)null : HasFlag(ArchiveFlagsV4.Password); - public bool OldNumberingFormat => !this.IsRar5 && !HasArchiveHeaderFlag(ArchiveFlagsV4.NewNumbering); + public bool OldNumberingFormat => !IsRar5 && !HasFlag(ArchiveFlagsV4.NewNumbering); - public bool IsVolume => HasArchiveHeaderFlag(this.IsRar5 ? ArchiveFlagsV5.Volume : ArchiveFlagsV4.Volume); + public bool IsVolume => HasFlag(IsRar5 ? ArchiveFlagsV5.Volume : ArchiveFlagsV4.Volume); - public bool IsFirstVolume => this.IsRar5 ? VolumeNumber == 1 : HasArchiveHeaderFlag(ArchiveFlagsV4.FirstVolume); + public bool IsFirstVolume => IsRar5 ? VolumeNumber == 1 : HasFlag(ArchiveFlagsV4.FirstVolume); - public bool IsSolid => HasArchiveHeaderFlag(this.IsRar5 ? ArchiveFlagsV5.Solid : ArchiveFlagsV4.Solid); + public bool IsSolid => HasFlag(IsRar5 ? ArchiveFlagsV5.Solid : ArchiveFlagsV4.Solid); } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs b/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs index c9d71b0a..d15a1b8e 100644 --- a/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs @@ -5,11 +5,12 @@ namespace SharpCompress.Common.Rar.Headers internal class CommentHeader : RarHeader { protected CommentHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.Comment) { + : base(header, reader, HeaderType.Comment) + { if (IsRar5) throw new InvalidFormatException("unexpected rar5 record"); } - protected override void ReadFromReader(MarkingBinaryReader reader) + protected override void ReadFinish(MarkingBinaryReader reader) { UnpSize = reader.ReadInt16(); UnpVersion = reader.ReadByte(); diff --git a/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs index caee1a83..07d1d95e 100644 --- a/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs @@ -5,34 +5,35 @@ namespace SharpCompress.Common.Rar.Headers internal class EndArchiveHeader : RarHeader { public EndArchiveHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.EndArchive) { + : base(header, reader, HeaderType.EndArchive) + { } - protected override void ReadFromReader(MarkingBinaryReader reader) + protected override void ReadFinish(MarkingBinaryReader reader) { - if (this.IsRar5) + if (IsRar5) { - EndArchiveFlags = reader.ReadRarVIntUInt16(); + Flags = reader.ReadRarVIntUInt16(); } else { - EndArchiveFlags = HeaderFlags; - if (HasEndArchiveFlag(EndArchiveFlagsV4.DataCrc)) + Flags = HeaderFlags; + if (HasFlag(EndArchiveFlagsV4.DataCrc)) { ArchiveCrc = reader.ReadInt32(); } - if (HasEndArchiveFlag(EndArchiveFlagsV4.VolumeNumber)) + if (HasFlag(EndArchiveFlagsV4.VolumeNumber)) { VolumeNumber = reader.ReadInt16(); } } } - private ushort EndArchiveFlags { get; set; } + private ushort Flags { get; set; } - private bool HasEndArchiveFlag(ushort flag) + private bool HasFlag(ushort flag) { - return (EndArchiveFlags & flag) == flag; + return (Flags & flag) == flag; } internal int? ArchiveCrc { get; private set; } diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index 27fffe3b..49c8bb03 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -10,12 +10,13 @@ namespace SharpCompress.Common.Rar.Headers private uint fileCrc; public FileHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) - : base(header, reader, headerType) { + : base(header, reader, headerType) + { } - protected override void ReadFromReader(MarkingBinaryReader reader) + protected override void ReadFinish(MarkingBinaryReader reader) { - if (this.IsRar5) + if (IsRar5) { ReadFromReaderV5(reader); } @@ -27,19 +28,19 @@ namespace SharpCompress.Common.Rar.Headers private void ReadFromReaderV5(MarkingBinaryReader reader) { - FileFlags = reader.ReadRarVIntUInt16(); + Flags = reader.ReadRarVIntUInt16(); var lvalue = checked((long)reader.ReadRarVInt()); //!!! rar5 TODO: this may cause problems with user code, Unpack20 seems to use this field to decompress - UncompressedSize = HasFileFlag(FileFlagsV5.UnpackedSizeUnknown) ? long.MaxValue : lvalue; + UncompressedSize = HasFlag(FileFlagsV5.UnpackedSizeUnknown) ? long.MaxValue : lvalue; FileAttributes = reader.ReadRarVIntUInt32(); - if (HasFileFlag(FileFlagsV5.HasModTime)) { + if (HasFlag(FileFlagsV5.HasModTime)) { FileLastModifiedTime = Utility.UnixTimeToDateTime(reader.ReadUInt32()); } - if (HasFileFlag(FileFlagsV5.HasCrc32)) { + if (HasFlag(FileFlagsV5.HasCrc32)) { FileCrc = reader.ReadUInt32(); } @@ -178,7 +179,7 @@ namespace SharpCompress.Common.Rar.Headers private void ReadFromReaderV4(MarkingBinaryReader reader) { - FileFlags = HeaderFlags; + Flags = HeaderFlags; uint lowUncompressedSize = reader.ReadUInt32(); @@ -197,7 +198,7 @@ namespace SharpCompress.Common.Rar.Headers uint highCompressedSize = 0; uint highUncompressedkSize = 0; - if (HasFileFlag(FileFlagsV4.Large)) + if (HasFlag(FileFlagsV4.Large)) { highCompressedSize = reader.ReadUInt32(); highUncompressedkSize = reader.ReadUInt32(); @@ -224,7 +225,7 @@ namespace SharpCompress.Common.Rar.Headers { case HeaderCodeV.FileHeader: { - if (HasFileFlag(FileFlagsV4.Unicode)) + if (HasFlag(FileFlagsV4.Unicode)) { int length = 0; while (length < fileNameBytes.Length @@ -252,7 +253,7 @@ namespace SharpCompress.Common.Rar.Headers case HeaderCodeV.NewSubHeader: { int datasize = HeaderSize - newLhdSize - nameSize; - if (HasFileFlag(FileFlagsV4.Salt)) + if (HasFlag(FileFlagsV4.Salt)) { datasize -= saltSize; } @@ -270,11 +271,11 @@ namespace SharpCompress.Common.Rar.Headers break; } - if (HasFileFlag(FileFlagsV4.Salt)) + if (HasFlag(FileFlagsV4.Salt)) { R4Salt = reader.ReadBytes(saltSize); } - if (HasFileFlag(FileFlagsV4.ExtTime)) + if (HasFlag(FileFlagsV4.ExtTime)) { // verify that the end of the header hasn't been reached before reading the Extended Time. // some tools incorrectly omit Extended Time despite specifying FileFlags.EXTTIME, which most parsers tolerate. @@ -346,17 +347,17 @@ namespace SharpCompress.Common.Rar.Headers return FileName; } - private ushort FileFlags { get; set; } + private ushort Flags { get; set; } - private bool HasFileFlag(ushort flag) + private bool HasFlag(ushort flag) { - return (FileFlags & flag) == flag; + return (Flags & flag) == flag; } internal uint FileCrc { get { - if (this.IsRar5 && !HasFileFlag(FileFlagsV5.HasCrc32)) { + if (IsRar5 && !HasFlag(FileFlagsV5.HasCrc32)) { //!!! rar5: throw new InvalidOperationException("TODO rar5"); } @@ -386,15 +387,15 @@ namespace SharpCompress.Common.Rar.Headers public Stream PackedStream { get; set; } //!!! TODO rar5 - public bool IsSplit => HasFileFlag(FileFlagsV4.SplitAfter); + public bool IsSplit => HasFlag(FileFlagsV4.SplitAfter); - public bool IsDirectory => HasFileFlag(this.IsRar5 ? FileFlagsV5.Directory : FileFlagsV4.Directory); + public bool IsDirectory => HasFlag(IsRar5 ? FileFlagsV5.Directory : FileFlagsV4.Directory); //!!! TODO rar5 - public bool IsEncrypted => HasFileFlag(FileFlagsV4.Password); + public bool IsEncrypted => HasFlag(FileFlagsV4.Password); //!!! TODO rar5; R5IsSolid - public bool IsSolid => HasFileFlag(FileFlagsV4.Solid); + public bool IsSolid => HasFlag(FileFlagsV4.Solid); internal DateTime? FileLastModifiedTime { get; private set; } diff --git a/src/SharpCompress/Common/Rar/Headers/IRarHeader.cs b/src/SharpCompress/Common/Rar/Headers/IRarHeader.cs index 93f9b0ca..bbc03593 100644 --- a/src/SharpCompress/Common/Rar/Headers/IRarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/IRarHeader.cs @@ -1,6 +1,7 @@ namespace SharpCompress.Common.Rar.Headers { - internal interface IRarHeader { + internal interface IRarHeader + { HeaderType HeaderType { get; } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs b/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs index 6218f123..d4b2a77b 100644 --- a/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs +++ b/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs @@ -47,13 +47,9 @@ namespace SharpCompress.Common.Rar.Headers return true; } - #region IEquatable Members - public bool Equals(NewSubHeaderType other) { return Equals(other.bytes); } - - #endregion } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs b/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs index 19529388..f7f0e8ba 100644 --- a/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ProtectHeader.cs @@ -6,11 +6,12 @@ namespace SharpCompress.Common.Rar.Headers internal class ProtectHeader : RarHeader { public ProtectHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.Protect) { + : base(header, reader, HeaderType.Protect) + { if (IsRar5) throw new InvalidFormatException("unexpected rar5 record"); } - protected override void ReadFromReader(MarkingBinaryReader reader) + protected override void ReadFinish(MarkingBinaryReader reader) { Version = reader.ReadByte(); RecSectors = reader.ReadUInt16(); diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index ca5ca38f..7feb72ea 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -18,8 +18,14 @@ namespace SharpCompress.Common.Rar.Headers protected RarHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) { this.headerType = headerType; this.isRar5 = header.IsRar5; - FillBase(header); - ReadFromReader(reader); + HeaderCrc = header.HeaderCrc; + HeaderCode = header.HeaderCode; + HeaderFlags = header.HeaderFlags; + HeaderSize = header.HeaderSize; + ExtraSize = header.ExtraSize; + AdditionalDataSize = header.AdditionalDataSize; + ArchiveEncoding = header.ArchiveEncoding; + ReadFinish(reader); ReadBytes = reader.CurrentReadByteCount; int headerSizeDiff = HeaderSize - (int)ReadBytes; @@ -31,25 +37,18 @@ namespace SharpCompress.Common.Rar.Headers VerifyHeaderCrc(reader.GetCrc32()); } - private void FillBase(RarHeader other) + protected virtual void ReadFinish(MarkingBinaryReader reader) { - HeaderCrc = other.HeaderCrc; - HeaderCode = other.HeaderCode; - HeaderFlags = other.HeaderFlags; - HeaderSize = other.HeaderSize; - ExtraSize = other.ExtraSize; - AdditionalDataSize = other.AdditionalDataSize; - ReadBytes = other.ReadBytes; - ArchiveEncoding = other.ArchiveEncoding; + throw new NotImplementedException(); } - internal static RarHeader ReadBase(RarCrcBinaryReader reader, ArchiveEncoding archiveEncoding, bool isRar5) + internal static RarHeader TryReadBase(RarCrcBinaryReader reader, ArchiveEncoding archiveEncoding, bool isRar5) { try { var header = new RarHeader(isRar5); header.ArchiveEncoding = archiveEncoding; - header.ReadStartFromReader(reader); + header.ReadStart(reader); header.ReadBytes += reader.CurrentReadByteCount; return header; } @@ -59,9 +58,9 @@ namespace SharpCompress.Common.Rar.Headers } } - private void ReadStartFromReader(RarCrcBinaryReader reader) + private void ReadStart(RarCrcBinaryReader reader) { - if (this.IsRar5) + if (IsRar5) { HeaderCrc = reader.ReadUInt32(); reader.ResetCrc(); @@ -91,24 +90,15 @@ namespace SharpCompress.Common.Rar.Headers } } - protected virtual void ReadFromReader(MarkingBinaryReader reader) - { - throw new NotImplementedException(); - } - private void VerifyHeaderCrc(uint crc32) { - var b = (this.IsRar5 ? crc32 : (ushort)crc32) == HeaderCrc; + var b = (IsRar5 ? crc32 : (ushort)crc32) == HeaderCrc; if (!b) { throw new InvalidFormatException("rar header crc mismatch"); } } - protected virtual void PostReadingBytes(MarkingBinaryReader reader) - { - } - public HeaderType HeaderType { get { return this.headerType; } } protected bool IsRar5 { get { return this.isRar5; } } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index 3dbb9f21..0a833fcf 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -32,7 +32,8 @@ namespace SharpCompress.Common.Rar.Headers yield return header; if (header.HeaderType == HeaderType.EndArchive) { - // End of archive marker. RAR does not read anything after this header letting to use third party tools to add extra information such as a digital signature to archive. + // End of archive marker. RAR does not read anything after this header letting to use third + // party tools to add extra information such as a digital signature to archive. yield break; } } @@ -41,9 +42,12 @@ namespace SharpCompress.Common.Rar.Headers private RarHeader ReadNextHeader(Stream stream) { RarCrcBinaryReader reader; - if (!IsEncrypted) { + if (!IsEncrypted) + { reader = new RarCrcBinaryReader(stream); - } else { + } + else + { #if !NO_CRYPTO if (Options.Password == null) { @@ -55,7 +59,7 @@ namespace SharpCompress.Common.Rar.Headers #endif } - var header = RarHeader.ReadBase(reader, Options.ArchiveEncoding, this.isRar5); + var header = RarHeader.TryReadBase(reader, Options.ArchiveEncoding, this.isRar5); if (header == null) { return null; @@ -155,7 +159,7 @@ namespace SharpCompress.Common.Rar.Headers } default: { - throw new InvalidFormatException("Invalid Rar Header: " + header.HeaderCode); + throw new InvalidFormatException("Unknown Rar Header: " + header.HeaderCode); } } } diff --git a/src/SharpCompress/Common/Rar/Headers/SignHeader.cs b/src/SharpCompress/Common/Rar/Headers/SignHeader.cs index 2f945878..58b3baef 100644 --- a/src/SharpCompress/Common/Rar/Headers/SignHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/SignHeader.cs @@ -5,11 +5,12 @@ namespace SharpCompress.Common.Rar.Headers internal class SignHeader : RarHeader { protected SignHeader(RarHeader header, RarCrcBinaryReader reader) - : base(header, reader, HeaderType.Sign) { + : base(header, reader, HeaderType.Sign) + { if (IsRar5) throw new InvalidFormatException("unexpected rar5 record"); } - protected override void ReadFromReader(MarkingBinaryReader reader) + protected override void ReadFinish(MarkingBinaryReader reader) { CreationTime = reader.ReadInt32(); ArcNameSize = reader.ReadInt16(); diff --git a/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs index 3251552b..6ab2923a 100644 --- a/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs @@ -3,7 +3,8 @@ using SharpCompress.Compressors.Rar; using SharpCompress.IO; namespace SharpCompress.Common.Rar { - internal class RarCrcBinaryReader : MarkingBinaryReader { + internal class RarCrcBinaryReader : MarkingBinaryReader + { private uint currentCrc; public RarCrcBinaryReader(Stream stream) : base(stream) diff --git a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs index 4a14a375..9ea3cb2c 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs @@ -58,27 +58,6 @@ namespace SharpCompress.Common.Rar this.readCount++; return base.ReadByte(); } -//x -// public override byte ReadByte() { -// if (UseEncryption) -// { -// int queueSize = this.data.Count; -// if (queueSize < 1) -// { -// byte[] cipherText = ReadBytesNoCrc(16); -// var readBytes = this.rijndael.ProcessBlock(cipherText); -// foreach (var readByte in readBytes) -// this.data.Enqueue(readByte); -// } -// -// var b = this.data.Dequeue(); -// UpdateCrc(b); -// this.readCount++; -// return b; -// } -// this.readCount++; -// return base.ReadByte(); -// } public override byte[] ReadBytes(int count) { diff --git a/src/SharpCompress/Common/Rar/RarVolume.cs b/src/SharpCompress/Common/Rar/RarVolume.cs index 17be5a30..201ced3b 100644 --- a/src/SharpCompress/Common/Rar/RarVolume.cs +++ b/src/SharpCompress/Common/Rar/RarVolume.cs @@ -81,7 +81,6 @@ namespace SharpCompress.Common.Rar get { EnsureArchiveHeaderLoaded(); - //return (ArchiveHeader.ArchiveHeaderFlags & ArchiveFlagsV4.FirstVolume) == ArchiveFlagsV4.FirstVolume; return ArchiveHeader.IsFirstVolume; } } From f0eac57bb96a244461e0f09532b2749a0cd27f14 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 09:14:08 -0500 Subject: [PATCH 030/124] rar5: more cleanup --- .../Common/Rar/Headers/RarHeader.cs | 113 +++++++++--------- .../Common/Rar/Headers/RarHeaderFactory.cs | 2 +- 2 files changed, 57 insertions(+), 58 deletions(-) diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index 7feb72ea..5f15914f 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -9,10 +9,52 @@ namespace SharpCompress.Common.Rar.Headers private readonly HeaderType headerType; private readonly bool isRar5; - private RarHeader(bool isRar5) + internal static RarHeader TryReadBase(RarCrcBinaryReader reader, bool isRar5, ArchiveEncoding archiveEncoding) + { + try + { + return new RarHeader(reader, isRar5, archiveEncoding); + } + catch (EndOfStreamException) + { + return null; + } + } + + private RarHeader(RarCrcBinaryReader reader, bool isRar5, ArchiveEncoding archiveEncoding) { this.headerType = HeaderType.Null; this.isRar5 = isRar5; + ArchiveEncoding = archiveEncoding; + if (IsRar5) + { + HeaderCrc = reader.ReadUInt32(); + reader.ResetCrc(); + HeaderSize = (int)reader.ReadRarVIntUInt32(3); + reader.Mark(); + HeaderCode = reader.ReadRarVIntByte(); + HeaderFlags = reader.ReadRarVIntUInt16(2); + if (HasHeaderFlag(HeaderFlagsV5.HasExtra)) + { + ExtraSize = reader.ReadRarVIntUInt32(); + } + if (HasHeaderFlag(HeaderFlagsV5.HasData)) + { + AdditionalDataSize = (long)reader.ReadRarVInt(); + } + } else { + reader.Mark(); + HeaderCrc = reader.ReadUInt16(); + reader.ResetCrc(); + HeaderCode = reader.ReadByte(); + HeaderFlags = reader.ReadUInt16(); + HeaderSize = reader.ReadInt16(); + if (HasHeaderFlag(HeaderFlagsV4.HasData)) + { + AdditionalDataSize = reader.ReadUInt32(); + } + } + ReadBytes = reader.CurrentReadByteCount; } protected RarHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) { @@ -42,54 +84,6 @@ namespace SharpCompress.Common.Rar.Headers throw new NotImplementedException(); } - internal static RarHeader TryReadBase(RarCrcBinaryReader reader, ArchiveEncoding archiveEncoding, bool isRar5) - { - try - { - var header = new RarHeader(isRar5); - header.ArchiveEncoding = archiveEncoding; - header.ReadStart(reader); - header.ReadBytes += reader.CurrentReadByteCount; - return header; - } - catch (EndOfStreamException) - { - return null; - } - } - - private void ReadStart(RarCrcBinaryReader reader) - { - if (IsRar5) - { - HeaderCrc = reader.ReadUInt32(); - reader.ResetCrc(); - HeaderSize = (int)reader.ReadRarVIntUInt32(3); - reader.Mark(); - HeaderCode = reader.ReadRarVIntByte(); - HeaderFlags = reader.ReadRarVIntUInt16(2); - if ((HeaderFlags & HeaderFlagsV5.HasExtra) == HeaderFlagsV5.HasExtra) - { - ExtraSize = reader.ReadRarVIntUInt32(); - } - if ((HeaderFlags & HeaderFlagsV5.HasData) == HeaderFlagsV5.HasData) - { - AdditionalDataSize = (long)reader.ReadRarVInt(); - } - } else { - reader.Mark(); - HeaderCrc = reader.ReadUInt16(); - reader.ResetCrc(); - HeaderCode = reader.ReadByte(); - HeaderFlags = reader.ReadUInt16(); - HeaderSize = reader.ReadInt16(); - if ((HeaderFlags & HeaderFlagsV4.HasData) == HeaderFlagsV4.HasData) - { - AdditionalDataSize = reader.ReadUInt32(); - } - } - } - private void VerifyHeaderCrc(uint crc32) { var b = (IsRar5 ? crc32 : (ushort)crc32) == HeaderCrc; @@ -106,26 +100,31 @@ namespace SharpCompress.Common.Rar.Headers /// /// This is the number of bytes read when reading the header /// - protected long ReadBytes { get; private set; } + protected long ReadBytes { get; } - protected uint HeaderCrc { get; private set; } + protected uint HeaderCrc { get; } - internal byte HeaderCode { get; private set; } + internal byte HeaderCode { get; } - protected ushort HeaderFlags { get; private set; } + protected ushort HeaderFlags { get; } - protected int HeaderSize { get; private set; } + private bool HasHeaderFlag(ushort flag) + { + return (HeaderFlags & flag) == flag; + } - internal ArchiveEncoding ArchiveEncoding { get; private set; } + protected int HeaderSize { get; } + + internal ArchiveEncoding ArchiveEncoding { get; } /// /// Extra header size. /// - protected uint ExtraSize { get; private set; } + protected uint ExtraSize { get; } /// /// Size of additional data (eg file contents) /// - protected long AdditionalDataSize { get; private set; } + protected long AdditionalDataSize { get; } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index 0a833fcf..c9d37c16 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -59,7 +59,7 @@ namespace SharpCompress.Common.Rar.Headers #endif } - var header = RarHeader.TryReadBase(reader, Options.ArchiveEncoding, this.isRar5); + var header = RarHeader.TryReadBase(reader, this.isRar5, Options.ArchiveEncoding); if (header == null) { return null; From f802b4166574224a2e04e0478321643f00c4a962 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 10:08:21 -0500 Subject: [PATCH 031/124] rar5: wip --- .../Archives/Rar/RarArchiveEntryFactory.cs | 2 -- .../Archives/Rar/RarArchiveVolumeFactory.cs | 1 - .../Common/Rar/Headers/ArchiveHeader.cs | 8 ++--- .../Common/Rar/Headers/FileHeader.cs | 27 ++++++++++++---- src/SharpCompress/Common/Rar/Headers/Flags.cs | 31 +++++++++++-------- .../Common/Rar/Headers/RarHeader.cs | 17 +++++----- .../Common/Rar/Headers/RarHeaderFactory.cs | 10 +++--- .../Rar/MultiVolumeReadOnlyStream.cs | 1 - .../Readers/Rar/MultiVolumeRarReader.cs | 11 ++----- src/SharpCompress/Readers/Rar/RarReader.cs | 12 ++----- .../Readers/Rar/SingleVolumeRarReader.cs | 7 ++--- 11 files changed, 64 insertions(+), 63 deletions(-) diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs index b5c4865a..91305cfd 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntryFactory.cs @@ -1,7 +1,5 @@ using System.Collections.Generic; -using SharpCompress.Common; using SharpCompress.Common.Rar; -using SharpCompress.Common.Rar.Headers; namespace SharpCompress.Archives.Rar { diff --git a/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs b/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs index 091f24ae..57eb3c69 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveVolumeFactory.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using SharpCompress.Common; using SharpCompress.Common.Rar; using SharpCompress.Readers; #if !NO_FILE diff --git a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs index 083a0bf2..83eda7f1 100644 --- a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs @@ -18,9 +18,10 @@ namespace SharpCompress.Common.Rar.Headers { VolumeNumber = (int)reader.ReadRarVIntUInt32(); } - if (ExtraSize != 0) { - ReadLocator(reader); - } + // later: we may have a locator record if we need it + //if (ExtraSize != 0) { + // ReadLocator(reader); + //} } else { @@ -35,7 +36,6 @@ namespace SharpCompress.Common.Rar.Headers } private void ReadLocator(MarkingBinaryReader reader) { - // parse fields, unused var size = reader.ReadRarVIntUInt16(); var type = reader.ReadRarVIntUInt16(); if (type != 1) throw new InvalidFormatException("expected locator record"); diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index 49c8bb03..0a3bebde 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -85,8 +85,15 @@ namespace SharpCompress.Common.Rar.Headers var b = reader.ReadBytes(nameSize); FileName = ConvertPathV5(Encoding.UTF8.GetString(b, 0, b.Length)); - if (ExtraSize != 0) { + // extra size seems to be redudant since we know the total header size + if (ExtraSize != RemainingHeaderBytes(reader)) + { + throw new InvalidFormatException("rar5 header size / extra size inconsistency"); + } + + while (RemainingHeaderBytes(reader) > 0) { var size = reader.ReadRarVIntUInt16(); + int n = RemainingHeaderBytes(reader); var type = reader.ReadRarVIntUInt16(); switch (type) { //TODO @@ -139,9 +146,17 @@ namespace SharpCompress.Common.Rar.Headers // } // break; - default: throw new InvalidFormatException("unexpected extra data record type " + type); + default: + // skip unknown record types to allow new record types to be added in the future + break; + } + // drain any trailing bytes of extra record + int did = n - RemainingHeaderBytes(reader); + int drain = size - did; + if (drain > 0) + { + reader.ReadBytes(drain); } - } if (AdditionalDataSize != 0) { @@ -223,7 +238,7 @@ namespace SharpCompress.Common.Rar.Headers switch (HeaderCode) { - case HeaderCodeV.FileHeader: + case HeaderCodeV.Rar4FileHeader: { if (HasFlag(FileFlagsV4.Unicode)) { @@ -250,7 +265,7 @@ namespace SharpCompress.Common.Rar.Headers FileName = ConvertPathV4(FileName); } break; - case HeaderCodeV.NewSubHeader: + case HeaderCodeV.Rar4NewSubHeader: { int datasize = HeaderSize - newLhdSize - nameSize; if (HasFlag(FileFlagsV4.Salt)) @@ -279,7 +294,7 @@ namespace SharpCompress.Common.Rar.Headers { // verify that the end of the header hasn't been reached before reading the Extended Time. // some tools incorrectly omit Extended Time despite specifying FileFlags.EXTTIME, which most parsers tolerate. - if (ReadBytes + reader.CurrentReadByteCount <= HeaderSize - 2) + if (RemainingHeaderBytes(reader) >= 2) { ushort extendedFlags = reader.ReadUInt16(); FileLastModifiedTime = ProcessExtendedTimeV4(extendedFlags, FileLastModifiedTime, reader, 0); diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index 6f072396..e9048b37 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -17,22 +17,22 @@ namespace SharpCompress.Common.Rar.Headers internal static class HeaderCodeV { + public const byte Rar4MarkHeader = 0x72; + public const byte Rar4ArchiveHeader = 0x73; + public const byte Rar4FileHeader = 0x74; + public const byte Rar4CommentHeader = 0x75; + public const byte Rar4AvHeader = 0x76; + public const byte Rar4SubHeader = 0x77; + public const byte Rar4ProtectHeader = 0x78; + public const byte Rar4SignHeader = 0x79; + public const byte Rar4NewSubHeader = 0x7a; + public const byte Rar4EndArchiveHeader = 0x7b; + public const byte Rar5ArchiveHeader = 0x01; public const byte Rar5FileHeader = 0x02; public const byte Rar5ServiceHeader = 0x03; public const byte Rar5EncryptionHeader = 0x04; public const byte Rar5EndArchiveHeader = 0x05; - - public const byte MarkHeader = 0x72; - public const byte ArchiveHeader = 0x73; - public const byte FileHeader = 0x74; - public const byte CommHeader = 0x75; - public const byte AvHeader = 0x76; - public const byte SubHeader = 0x77; - public const byte ProtectHeader = 0x78; - public const byte SignHeader = 0x79; - public const byte NewSubHeader = 0x7a; - public const byte EndArchiveHeader = 0x7b; } internal static class HeaderFlagsV4 @@ -42,8 +42,13 @@ namespace SharpCompress.Common.Rar.Headers internal static class HeaderFlagsV5 { - public const ushort HasExtra = 0x01; - public const ushort HasData = 0x02; + public const ushort HasExtra = 0x0001; + public const ushort HasData = 0x0002; + public const ushort Lock = 0x0004; // block must be kept during an update + public const ushort VolumeContinuation = 0x0008; + public const ushort VolumeIncomplete = 0x0010; + public const ushort Solid = 0x0020; // ??? + public const ushort PreserveChild = 0x0040; } internal static class ArchiveFlagsV4 diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index 5f15914f..4713b3ef 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -54,7 +54,6 @@ namespace SharpCompress.Common.Rar.Headers AdditionalDataSize = reader.ReadUInt32(); } } - ReadBytes = reader.CurrentReadByteCount; } protected RarHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) { @@ -68,17 +67,20 @@ namespace SharpCompress.Common.Rar.Headers AdditionalDataSize = header.AdditionalDataSize; ArchiveEncoding = header.ArchiveEncoding; ReadFinish(reader); - ReadBytes = reader.CurrentReadByteCount; - int headerSizeDiff = HeaderSize - (int)ReadBytes; - if (headerSizeDiff > 0) + int n = RemainingHeaderBytes(reader); + if (n > 0) { - reader.ReadBytes(headerSizeDiff); + reader.ReadBytes(n); } VerifyHeaderCrc(reader.GetCrc32()); } + protected int RemainingHeaderBytes(MarkingBinaryReader reader) { + return checked(HeaderSize - (int)reader.CurrentReadByteCount); + } + protected virtual void ReadFinish(MarkingBinaryReader reader) { throw new NotImplementedException(); @@ -97,11 +99,6 @@ namespace SharpCompress.Common.Rar.Headers protected bool IsRar5 { get { return this.isRar5; } } - /// - /// This is the number of bytes read when reading the header - /// - protected long ReadBytes { get; } - protected uint HeaderCrc { get; } internal byte HeaderCode { get; } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index c9d37c16..d4ce8cd2 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -67,7 +67,7 @@ namespace SharpCompress.Common.Rar.Headers switch (header.HeaderCode) { case HeaderCodeV.Rar5ArchiveHeader: - case HeaderCodeV.ArchiveHeader: + case HeaderCodeV.Rar4ArchiveHeader: { var ah = new ArchiveHeader(header, reader); if (ah.IsEncrypted == true) { @@ -77,7 +77,7 @@ namespace SharpCompress.Common.Rar.Headers return ah; } - case HeaderCodeV.ProtectHeader: + case HeaderCodeV.Rar4ProtectHeader: { var ph = new ProtectHeader(header, reader); // skip the recovery record data, we do not use it. @@ -109,7 +109,7 @@ namespace SharpCompress.Common.Rar.Headers return fh; } - case HeaderCodeV.NewSubHeader: + case HeaderCodeV.Rar4NewSubHeader: { var fh = new FileHeader(header, reader, HeaderType.NewSub); SkipData(fh, reader); @@ -117,7 +117,7 @@ namespace SharpCompress.Common.Rar.Headers } case HeaderCodeV.Rar5FileHeader: - case HeaderCodeV.FileHeader: + case HeaderCodeV.Rar4FileHeader: { var fh = new FileHeader(header, reader, HeaderType.File); switch (StreamingMode) @@ -153,7 +153,7 @@ namespace SharpCompress.Common.Rar.Headers return fh; } case HeaderCodeV.Rar5EndArchiveHeader: - case HeaderCodeV.EndArchiveHeader: + case HeaderCodeV.Rar4EndArchiveHeader: { return new EndArchiveHeader(header, reader); } diff --git a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs index 235b8720..69e7734d 100644 --- a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs +++ b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using SharpCompress.Common; using SharpCompress.Common.Rar; -using SharpCompress.Common.Rar.Headers; namespace SharpCompress.Compressors.Rar { diff --git a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs index 7bc61e2e..068d7572 100644 --- a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs +++ b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs @@ -33,14 +33,9 @@ namespace SharpCompress.Readers.Rar internal override bool NextEntryForCurrentStream() { - if (!base.NextEntryForCurrentStream()) - { - //if we're got another stream to try to process then do so - if (streams.MoveNext() && LoadStreamForReading(streams.Current)) - { - return true; - } - return false; + if (!base.NextEntryForCurrentStream()) { + // if we're got another stream to try to process then do so + return this.streams.MoveNext() && LoadStreamForReading(this.streams.Current); } return true; } diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index 92e4d737..444f0147 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -24,8 +24,6 @@ namespace SharpCompress.Readers.Rar public override RarVolume Volume => volume; - #region Open - /// /// Opens a RarReader for Non-seeking usage with a single volume /// @@ -50,8 +48,6 @@ namespace SharpCompress.Readers.Rar return new MultiVolumeRarReader(streams, options ?? new ReaderOptions()); } - #endregion - internal override IEnumerable GetEntries(Stream stream) { volume = new RarReaderVolume(stream, Options); @@ -67,11 +63,9 @@ namespace SharpCompress.Readers.Rar return Entry.Parts; } - protected override EntryStream GetEntryStream() - { - return CreateEntryStream(new RarCrcStream(pack, Entry.FileHeader, - new MultiVolumeReadOnlyStream( - CreateFilePartEnumerableForCurrentEntry().Cast(), this))); + protected override EntryStream GetEntryStream() { + var stream = new MultiVolumeReadOnlyStream(CreateFilePartEnumerableForCurrentEntry().Cast(), this); + return CreateEntryStream(new RarCrcStream(pack, Entry.FileHeader, stream)); } } } diff --git a/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs b/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs index 3ffede69..cc7befa1 100644 --- a/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs +++ b/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs @@ -16,10 +16,9 @@ namespace SharpCompress.Readers.Rar internal override void ValidateArchive(RarVolume archive) { - if (archive.IsMultiVolume) - { - throw new MultiVolumeExtractionException( - "Streamed archive is a Multi-volume archive. Use different RarReader method to extract."); + if (archive.IsMultiVolume) { + var msg = "Streamed archive is a Multi-volume archive. Use different RarReader method to extract."; + throw new MultiVolumeExtractionException(msg); } } From b9e89ca64b109c6f358bd8aa2eec1cd9a8701182 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 10:40:41 -0500 Subject: [PATCH 032/124] rar5: wip --- .../Archives/Rar/FileInfoRarArchiveVolume.cs | 2 +- src/SharpCompress/Archives/Rar/RarArchive.cs | 1 - .../Archives/Rar/StreamRarArchiveVolume.cs | 2 +- src/SharpCompress/Common/Rar/Headers/Flags.cs | 2 +- .../Common/Rar/Headers/RarHeader.cs | 6 ++++ .../Common/Rar/Headers/RarHeaderFactory.cs | 4 +-- src/SharpCompress/Common/Rar/RarVolume.cs | 29 +++++++++---------- .../Readers/Rar/RarReaderVolume.cs | 2 +- .../SharpCompress.Test/Rar/RarArchiveTests.cs | 5 ---- 9 files changed, 26 insertions(+), 27 deletions(-) diff --git a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs index 9555735f..7932caad 100644 --- a/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs +++ b/src/SharpCompress/Archives/Rar/FileInfoRarArchiveVolume.cs @@ -32,7 +32,7 @@ namespace SharpCompress.Archives.Rar internal FileInfo FileInfo { get; } - internal override RarFilePart CreateFilePart(FileHeader fileHeader, MarkHeader markHeader) + internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) { return new FileInfoRarFilePart(this, ReaderOptions.Password, markHeader, fileHeader, FileInfo); } diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index adb9117c..3d61dd0a 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -5,7 +5,6 @@ using SharpCompress.Common; using SharpCompress.Common.Rar; using SharpCompress.Common.Rar.Headers; using SharpCompress.Compressors.Rar; -using SharpCompress.IO; using SharpCompress.Readers; using SharpCompress.Readers.Rar; diff --git a/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs b/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs index 3da1a531..92602ae9 100644 --- a/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs +++ b/src/SharpCompress/Archives/Rar/StreamRarArchiveVolume.cs @@ -19,7 +19,7 @@ namespace SharpCompress.Archives.Rar return GetVolumeFileParts(); } - internal override RarFilePart CreateFilePart(FileHeader fileHeader, MarkHeader markHeader) + internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) { return new SeekableFilePart(markHeader, fileHeader, Stream, ReaderOptions.Password); } diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index e9048b37..a3811740 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -44,7 +44,7 @@ namespace SharpCompress.Common.Rar.Headers { public const ushort HasExtra = 0x0001; public const ushort HasData = 0x0002; - public const ushort Lock = 0x0004; // block must be kept during an update + public const ushort Keep = 0x0004; // block must be kept during an update public const ushort VolumeContinuation = 0x0008; public const ushort VolumeIncomplete = 0x0010; public const ushort Solid = 0x0020; // ??? diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index 4713b3ef..8abf8670 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -34,6 +34,12 @@ namespace SharpCompress.Common.Rar.Headers reader.Mark(); HeaderCode = reader.ReadRarVIntByte(); HeaderFlags = reader.ReadRarVIntUInt16(2); + +// if (HasHeaderFlag(HeaderFlagsV5.VolumeContinuation)) throw new NotImplementedException(); +// if (HasHeaderFlag(HeaderFlagsV5.VolumeIncomplete)) throw new NotImplementedException(); +// if (HasHeaderFlag(HeaderFlagsV5.Solid)) throw new NotImplementedException(); +// if (HasHeaderFlag(HeaderFlagsV5.PreserveChild)) throw new NotImplementedException(); + if (HasHeaderFlag(HeaderFlagsV5.HasExtra)) { ExtraSize = reader.ReadRarVIntUInt32(); diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index d4ce8cd2..05660583 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -27,7 +27,7 @@ namespace SharpCompress.Common.Rar.Headers yield return markHeader; RarHeader header; - while ((header = ReadNextHeader(stream)) != null) + while ((header = TryReadNextHeader(stream)) != null) { yield return header; if (header.HeaderType == HeaderType.EndArchive) @@ -39,7 +39,7 @@ namespace SharpCompress.Common.Rar.Headers } } - private RarHeader ReadNextHeader(Stream stream) + private RarHeader TryReadNextHeader(Stream stream) { RarCrcBinaryReader reader; if (!IsEncrypted) diff --git a/src/SharpCompress/Common/Rar/RarVolume.cs b/src/SharpCompress/Common/Rar/RarVolume.cs index 201ced3b..31bc7636 100644 --- a/src/SharpCompress/Common/Rar/RarVolume.cs +++ b/src/SharpCompress/Common/Rar/RarVolume.cs @@ -21,42 +21,41 @@ namespace SharpCompress.Common.Rar headerFactory = new RarHeaderFactory(mode, options); } - internal StreamingMode Mode => headerFactory.StreamingMode; + internal ArchiveHeader ArchiveHeader { get; private set; } + + internal StreamingMode Mode => this.headerFactory.StreamingMode; internal abstract IEnumerable ReadFileParts(); - internal abstract RarFilePart CreateFilePart(FileHeader fileHeader, MarkHeader markHeader); + internal abstract RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader); internal IEnumerable GetVolumeFileParts() { - MarkHeader previousMarkHeader = null; - foreach (var header in headerFactory.ReadHeaders(Stream)) + MarkHeader lastMarkHeader = null; + foreach (var header in this.headerFactory.ReadHeaders(Stream)) { switch (header.HeaderType) { + case HeaderType.Mark: + { + lastMarkHeader = header as MarkHeader; + } + break; case HeaderType.Archive: { ArchiveHeader = header as ArchiveHeader; } break; - case HeaderType.Mark: - { - previousMarkHeader = header as MarkHeader; - } - break; case HeaderType.File: { - FileHeader fh = header as FileHeader; - RarFilePart fp = CreateFilePart(fh, previousMarkHeader); - yield return fp; + var fh = header as FileHeader; + yield return CreateFilePart(lastMarkHeader, fh); } break; } } } - internal ArchiveHeader ArchiveHeader { get; private set; } - private void EnsureArchiveHeaderLoaded() { if (ArchiveHeader == null) @@ -66,7 +65,7 @@ namespace SharpCompress.Common.Rar throw new InvalidOperationException("ArchiveHeader should never been null in a streaming read."); } - //we only want to load the archive header to avoid overhead but have to do the nasty thing and reset the stream + // we only want to load the archive header to avoid overhead but have to do the nasty thing and reset the stream GetVolumeFileParts().First(); Stream.Position = 0; } diff --git a/src/SharpCompress/Readers/Rar/RarReaderVolume.cs b/src/SharpCompress/Readers/Rar/RarReaderVolume.cs index bf68043f..98a8c5c0 100644 --- a/src/SharpCompress/Readers/Rar/RarReaderVolume.cs +++ b/src/SharpCompress/Readers/Rar/RarReaderVolume.cs @@ -13,7 +13,7 @@ namespace SharpCompress.Readers.Rar { } - internal override RarFilePart CreateFilePart(FileHeader fileHeader, MarkHeader markHeader) + internal override RarFilePart CreateFilePart(MarkHeader markHeader, FileHeader fileHeader) { return new NonSeekableStreamFilePart(markHeader, fileHeader); } diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index 54619c92..d320f782 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -135,11 +135,6 @@ namespace SharpCompress.Test.Rar DoRar_test_invalid_exttime_ArchiveStreamRead("Rar.test_invalid_exttime.rar"); } - [Fact] - public void Rar5_test_invalid_exttime_ArchiveStreamRead() { - DoRar_test_invalid_exttime_ArchiveStreamRead("Rar5.test_invalid_exttime.rar"); - } - private void DoRar_test_invalid_exttime_ArchiveStreamRead(string filename) { ResetScratch(); From 7969bbaac4d750fae171395c49883e0f098496f7 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 11:04:13 -0500 Subject: [PATCH 033/124] rar5: working on decompression --- .../Common/Rar/Headers/FileHeader.cs | 14 +++++-- src/SharpCompress/Compressors/Rar/Unpack.cs | 40 +------------------ tests/SharpCompress.Test/ArchiveTests.cs | 1 + 3 files changed, 12 insertions(+), 43 deletions(-) diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index 0a3bebde..f8fd127e 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -55,7 +55,7 @@ namespace SharpCompress.Common.Rar.Headers R5IsSolid = (us & 0x40) == 0x40; // Bits 8 - 10 (0x0380 mask) define the compression method. Currently only values 0 - 5 are used. 0 means no compression. - R5CompressionMethod = (byte)((us >> 7) & 0x7); + CompressionMethod = (byte)((us >> 7) & 0x7); // Bits 11 - 14 (0x3c00) define the minimum size of dictionary size required to extract data. Value 0 means 128 KB, 1 - 256 KB, ..., 14 - 2048 MB, 15 - 4096 MB. // 2 ^ (17 + x) @@ -205,7 +205,7 @@ namespace SharpCompress.Common.Rar.Headers FileLastModifiedTime = Utility.DosDateToDateTime(reader.ReadUInt32()); R4RarVersion = reader.ReadByte(); - R4PackingMethod = reader.ReadByte(); + CompressionMethod = (byte)(reader.ReadByte() - 0x30); short nameSize = reader.ReadInt16(); @@ -381,13 +381,19 @@ namespace SharpCompress.Common.Rar.Headers private set { this.fileCrc = value; } } + // 0 - storing + // 1 - fastest compression + // 2 - fast compression + // 3 - normal compression + // 4 - good compression + // 5 - best compression + internal byte CompressionMethod { get; private set; } + internal byte R4RarVersion { get; private set; } - internal byte R4PackingMethod { get; private set; } internal byte[] R4Salt { get; private set; } internal byte R5CompressionAlgorithm { get; private set; } internal bool R5IsSolid{ get; private set; } - internal byte R5CompressionMethod { get; private set; } internal byte R5DictSize_2_17plusX { get; private set; } diff --git a/src/SharpCompress/Compressors/Rar/Unpack.cs b/src/SharpCompress/Compressors/Rar/Unpack.cs index 40d3087c..5346495a 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack.cs @@ -142,7 +142,7 @@ namespace SharpCompress.Compressors.Rar public void doUnpack() { - if (fileHeader.R4PackingMethod == 0x30) + if (fileHeader.CompressionMethod == 0) { unstoreFile(); return; @@ -150,44 +150,6 @@ namespace SharpCompress.Compressors.Rar var solid = fileHeader.IsSolid; switch (fileHeader.R4RarVersion) { - case 0: // no compression - unstoreFile(); - break; - - case 15: // rar 1.5 compression - unpack15(solid); - break; - - case 20: // rar 2.x compression - case 26: // files larger than 2GB - unpack20(solid); - break; - - case 29: // rar 3.x compression - case 36: // alternative hash - unpack29(solid); - break; - - default: throw new InvalidFormatException("unknown rar compression version " + this.fileHeader.R4RarVersion); - } - } - - public void doUnpackNEW() - { - var compressionMethod = fileHeader.R4RarVersion; - //??? if (fileHeader.R4PackingMethod == 0x30) -// { -// unstoreFile(); -// return; -// } - var solid = fileHeader.IsSolid; - //switch (fileHeader.R4RarVersion) - switch (compressionMethod) - { - case 0: // no compression - unstoreFile(); - break; - case 15: // rar 1.5 compression unpack15(solid); break; diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index 757b10a1..b6f367c7 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -88,6 +88,7 @@ namespace SharpCompress.Test testArchive = Path.Combine(TEST_ARCHIVES_PATH, testArchive); ArchiveFileRead(testArchive.AsEnumerable(), readerOptions); } + protected void ArchiveFileRead(IEnumerable testArchives, ReaderOptions readerOptions = null) { foreach (var path in testArchives) From d268cc768517694f8882080f0da22e1c6de88c34 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 11:16:42 -0500 Subject: [PATCH 034/124] rar5: wip unpack50() --- .../Common/Rar/Headers/FileHeader.cs | 27 +++++++++---------- src/SharpCompress/Compressors/Rar/Unpack.cs | 9 +++++-- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index f8fd127e..b6181c5b 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -47,12 +47,15 @@ namespace SharpCompress.Common.Rar.Headers var us = reader.ReadRarVIntUInt16(); // Lower 6 bits (0x003f mask) contain the version of compression algorithm, resulting in possible 0 - 63 values. Current version is 0. - R5CompressionAlgorithm = (byte)(us & 0x3f); - if (R5CompressionAlgorithm != 0) throw new InvalidFormatException("unknown rar5 compression algorithm " + R5CompressionAlgorithm); + // "+ 50" to not mix with old RAR format algorithms. For example, + // we may need to use the compression algorithm 15 in the future, + // but it was already used in RAR 1.5 and Unpack needs to distinguish + // them. + CompressionAlgorithm = (byte)((us & 0x3f) + 50); // 7th bit (0x0040) defines the solid flag. If it is set, RAR continues to use the compression dictionary left after processing preceding files. // It can be set only for file headers and is never set for service headers. - R5IsSolid = (us & 0x40) == 0x40; + IsSolid = (us & 0x40) == 0x40; // Bits 8 - 10 (0x0380 mask) define the compression method. Currently only values 0 - 5 are used. 0 means no compression. CompressionMethod = (byte)((us >> 7) & 0x7); @@ -195,6 +198,7 @@ namespace SharpCompress.Common.Rar.Headers private void ReadFromReaderV4(MarkingBinaryReader reader) { Flags = HeaderFlags; + IsSolid = HasFlag(FileFlagsV4.Solid); uint lowUncompressedSize = reader.ReadUInt32(); @@ -204,7 +208,7 @@ namespace SharpCompress.Common.Rar.Headers FileLastModifiedTime = Utility.DosDateToDateTime(reader.ReadUInt32()); - R4RarVersion = reader.ReadByte(); + CompressionAlgorithm = reader.ReadByte(); CompressionMethod = (byte)(reader.ReadByte() - 0x30); short nameSize = reader.ReadInt16(); @@ -389,14 +393,13 @@ namespace SharpCompress.Common.Rar.Headers // 5 - best compression internal byte CompressionMethod { get; private set; } - internal byte R4RarVersion { get; private set; } + // see unpack.cs + internal byte CompressionAlgorithm { get; private set; } + public bool IsSolid { get; private set; } + internal byte[] R4Salt { get; private set; } - - internal byte R5CompressionAlgorithm { get; private set; } - internal bool R5IsSolid{ get; private set; } internal byte R5DictSize_2_17plusX { get; private set; } - private byte HostOs { get; set; } internal uint FileAttributes { get; private set; } internal long CompressedSize { get; private set; } @@ -414,11 +417,7 @@ namespace SharpCompress.Common.Rar.Headers //!!! TODO rar5 public bool IsEncrypted => HasFlag(FileFlagsV4.Password); - -//!!! TODO rar5; R5IsSolid - public bool IsSolid => HasFlag(FileFlagsV4.Solid); - - + internal DateTime? FileLastModifiedTime { get; private set; } internal DateTime? FileCreatedTime { get; private set; } diff --git a/src/SharpCompress/Compressors/Rar/Unpack.cs b/src/SharpCompress/Compressors/Rar/Unpack.cs index 5346495a..5a77beae 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack.cs @@ -148,7 +148,7 @@ namespace SharpCompress.Compressors.Rar return; } var solid = fileHeader.IsSolid; - switch (fileHeader.R4RarVersion) + switch (fileHeader.CompressionAlgorithm) { case 15: // rar 1.5 compression unpack15(solid); @@ -163,8 +163,13 @@ namespace SharpCompress.Compressors.Rar case 36: // alternative hash unpack29(solid); break; + +// case 50: // rar 5.x compression +// unpack50(solid); +// break; - default: throw new InvalidFormatException("unknown rar compression version " + this.fileHeader.R4RarVersion); + default: + throw new InvalidFormatException("unknown rar compression version " + this.fileHeader.CompressionAlgorithm); } } From 1445b0a48b1a80610aa45764979a2cb46fc3cd8a Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 11:33:31 -0500 Subject: [PATCH 035/124] rar5: refactor unpack classes --- .../Compressors/Rar/Decode/BitDecode.cs | 2 +- .../Compressors/Rar/Decode/DistDecode.cs | 2 +- .../Compressors/Rar/Decode/LitDecode.cs | 2 +- .../Compressors/Rar/Decode/LowDistDecode.cs | 2 +- .../Compressors/Rar/Decode/MultDecode.cs | 2 +- .../Rar/Decode/{Compress.cs => PackDef.cs} | 2 +- .../Compressors/Rar/Decode/RepDecode.cs | 2 +- src/SharpCompress/Compressors/Rar/Unpack.cs | 56 +- src/SharpCompress/Compressors/Rar/Unpack15.cs | 10 +- src/SharpCompress/Compressors/Rar/Unpack20.cs | 32 +- src/SharpCompress/Compressors/Rar/Unpack50.cs | 696 ++++++++++++++++++ 11 files changed, 752 insertions(+), 56 deletions(-) rename src/SharpCompress/Compressors/Rar/Decode/{Compress.cs => PackDef.cs} (97%) create mode 100644 src/SharpCompress/Compressors/Rar/Unpack50.cs diff --git a/src/SharpCompress/Compressors/Rar/Decode/BitDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/BitDecode.cs index ba13c9b8..eef2007f 100644 --- a/src/SharpCompress/Compressors/Rar/Decode/BitDecode.cs +++ b/src/SharpCompress/Compressors/Rar/Decode/BitDecode.cs @@ -3,7 +3,7 @@ namespace SharpCompress.Compressors.Rar.Decode internal class BitDecode : Decode { internal BitDecode() - : base(new int[Compress.BC]) + : base(new int[PackDef.BC]) { } } diff --git a/src/SharpCompress/Compressors/Rar/Decode/DistDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/DistDecode.cs index 0c26c391..b5127e2d 100644 --- a/src/SharpCompress/Compressors/Rar/Decode/DistDecode.cs +++ b/src/SharpCompress/Compressors/Rar/Decode/DistDecode.cs @@ -3,7 +3,7 @@ namespace SharpCompress.Compressors.Rar.Decode internal class DistDecode : Decode { internal DistDecode() - : base(new int[Compress.DC]) + : base(new int[PackDef.DC]) { } } diff --git a/src/SharpCompress/Compressors/Rar/Decode/LitDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/LitDecode.cs index 6084d0fe..0c58184e 100644 --- a/src/SharpCompress/Compressors/Rar/Decode/LitDecode.cs +++ b/src/SharpCompress/Compressors/Rar/Decode/LitDecode.cs @@ -3,7 +3,7 @@ namespace SharpCompress.Compressors.Rar.Decode internal class LitDecode : Decode { internal LitDecode() - : base(new int[Compress.NC]) + : base(new int[PackDef.NC]) { } } diff --git a/src/SharpCompress/Compressors/Rar/Decode/LowDistDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/LowDistDecode.cs index 266bf9e1..bafb0c2f 100644 --- a/src/SharpCompress/Compressors/Rar/Decode/LowDistDecode.cs +++ b/src/SharpCompress/Compressors/Rar/Decode/LowDistDecode.cs @@ -3,7 +3,7 @@ namespace SharpCompress.Compressors.Rar.Decode internal class LowDistDecode : Decode { internal LowDistDecode() - : base(new int[Compress.LDC]) + : base(new int[PackDef.LDC]) { } } diff --git a/src/SharpCompress/Compressors/Rar/Decode/MultDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/MultDecode.cs index d42a83eb..6c21dc83 100644 --- a/src/SharpCompress/Compressors/Rar/Decode/MultDecode.cs +++ b/src/SharpCompress/Compressors/Rar/Decode/MultDecode.cs @@ -3,7 +3,7 @@ namespace SharpCompress.Compressors.Rar.Decode internal class MultDecode : Decode { internal MultDecode() - : base(new int[Compress.MC20]) + : base(new int[PackDef.MC20]) { } } diff --git a/src/SharpCompress/Compressors/Rar/Decode/Compress.cs b/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs similarity index 97% rename from src/SharpCompress/Compressors/Rar/Decode/Compress.cs rename to src/SharpCompress/Compressors/Rar/Decode/PackDef.cs index 3689daf0..15ecdf13 100644 --- a/src/SharpCompress/Compressors/Rar/Decode/Compress.cs +++ b/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs @@ -1,6 +1,6 @@ namespace SharpCompress.Compressors.Rar.Decode { - internal class Compress + internal class PackDef { public const int CODEBUFSIZE = 0x4000; public const int MAXWINSIZE = 0x400000; diff --git a/src/SharpCompress/Compressors/Rar/Decode/RepDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/RepDecode.cs index 3a59587f..1f4a42ef 100644 --- a/src/SharpCompress/Compressors/Rar/Decode/RepDecode.cs +++ b/src/SharpCompress/Compressors/Rar/Decode/RepDecode.cs @@ -3,7 +3,7 @@ namespace SharpCompress.Compressors.Rar.Decode internal class RepDecode : Decode { internal RepDecode() - : base(new int[Compress.RC]) + : base(new int[PackDef.RC]) { } } diff --git a/src/SharpCompress/Compressors/Rar/Unpack.cs b/src/SharpCompress/Compressors/Rar/Unpack.cs index 5a77beae..f0cd37b5 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack.cs @@ -82,7 +82,7 @@ namespace SharpCompress.Compressors.Rar //UPGRADE_NOTE: The initialization of 'unpOldTable' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'" - private readonly byte[] unpOldTable = new byte[Compress.HUFF_TABLE_SIZE]; + private readonly byte[] unpOldTable = new byte[PackDef.HUFF_TABLE_SIZE]; private BlockTypes unpBlockType; @@ -114,7 +114,7 @@ namespace SharpCompress.Compressors.Rar { if (window == null) { - this.window = new byte[Compress.MAXWINSIZE]; + this.window = new byte[PackDef.MAXWINSIZE]; } else { @@ -198,8 +198,8 @@ namespace SharpCompress.Compressors.Rar private void unpack29(bool solid) { - int[] DDecode = new int[Compress.DC]; - byte[] DBits = new byte[Compress.DC]; + int[] DDecode = new int[PackDef.DC]; + byte[] DBits = new byte[PackDef.DC]; int Bits; @@ -239,7 +239,7 @@ namespace SharpCompress.Compressors.Rar while (true) { - unpPtr &= Compress.MAXWINMASK; + unpPtr &= PackDef.MAXWINMASK; if (inAddr > readBorder) { @@ -251,7 +251,7 @@ namespace SharpCompress.Compressors.Rar // System.out.println(((wrPtr - unpPtr) & // Compress.MAXWINMASK)+":"+wrPtr+":"+unpPtr); - if (((wrPtr - unpPtr) & Compress.MAXWINMASK) < 260 && wrPtr != unpPtr) + if (((wrPtr - unpPtr) & PackDef.MAXWINMASK) < 260 && wrPtr != unpPtr) { UnpWriteBuf(); if (destUnpSize <= 0) @@ -378,7 +378,7 @@ namespace SharpCompress.Compressors.Rar int LowDist = this.decodeNumber(LDD); if (LowDist == 16) { - lowDistRepCount = Compress.LOW_DIST_REP_COUNT - 1; + lowDistRepCount = PackDef.LOW_DIST_REP_COUNT - 1; Distance += prevLowDist; } else @@ -474,7 +474,7 @@ namespace SharpCompress.Compressors.Rar private void UnpWriteBuf() { int WrittenBorder = wrPtr; - int WriteSize = (unpPtr - WrittenBorder) & Compress.MAXWINMASK; + int WriteSize = (unpPtr - WrittenBorder) & PackDef.MAXWINMASK; for (int I = 0; I < prgStack.Count; I++) { UnpackFilter flt = prgStack[I]; @@ -489,17 +489,17 @@ namespace SharpCompress.Compressors.Rar } int BlockStart = flt.BlockStart; // ->BlockStart; int BlockLength = flt.BlockLength; // ->BlockLength; - if (((BlockStart - WrittenBorder) & Compress.MAXWINMASK) < WriteSize) + if (((BlockStart - WrittenBorder) & PackDef.MAXWINMASK) < WriteSize) { if (WrittenBorder != BlockStart) { UnpWriteArea(WrittenBorder, BlockStart); WrittenBorder = BlockStart; - WriteSize = (unpPtr - WrittenBorder) & Compress.MAXWINMASK; + WriteSize = (unpPtr - WrittenBorder) & PackDef.MAXWINMASK; } if (BlockLength <= WriteSize) { - int BlockEnd = (BlockStart + BlockLength) & Compress.MAXWINMASK; + int BlockEnd = (BlockStart + BlockLength) & PackDef.MAXWINMASK; if (BlockStart < BlockEnd || BlockEnd == 0) { // VM.SetMemory(0,Window+BlockStart,BlockLength); @@ -507,7 +507,7 @@ namespace SharpCompress.Compressors.Rar } else { - int FirstPartLength = Compress.MAXWINSIZE - BlockStart; + int FirstPartLength = PackDef.MAXWINSIZE - BlockStart; // VM.SetMemory(0,Window+BlockStart,FirstPartLength); rarVM.setMemory(0, window, BlockStart, FirstPartLength); @@ -641,7 +641,7 @@ namespace SharpCompress.Compressors.Rar writtenFileSize += FilteredDataSize; destUnpSize -= FilteredDataSize; WrittenBorder = BlockEnd; - WriteSize = (unpPtr - WrittenBorder) & Compress.MAXWINMASK; + WriteSize = (unpPtr - WrittenBorder) & PackDef.MAXWINMASK; } else { @@ -671,7 +671,7 @@ namespace SharpCompress.Compressors.Rar } if (endPtr < startPtr) { - UnpWriteData(window, startPtr, -startPtr & Compress.MAXWINMASK); + UnpWriteData(window, startPtr, -startPtr & PackDef.MAXWINMASK); UnpWriteData(window, 0, endPtr); unpAllBuf = true; } @@ -719,7 +719,7 @@ namespace SharpCompress.Compressors.Rar int destPtr = unpPtr - distance; // System.out.println(unpPtr+":"+distance); - if (destPtr >= 0 && destPtr < Compress.MAXWINSIZE - 260 && unpPtr < Compress.MAXWINSIZE - 260) + if (destPtr >= 0 && destPtr < PackDef.MAXWINSIZE - 260 && unpPtr < PackDef.MAXWINSIZE - 260) { window[unpPtr++] = window[destPtr++]; @@ -732,8 +732,8 @@ namespace SharpCompress.Compressors.Rar { while (length-- != 0) { - window[unpPtr] = window[destPtr++ & Compress.MAXWINMASK]; - unpPtr = (unpPtr + 1) & Compress.MAXWINMASK; + window[unpPtr] = window[destPtr++ & PackDef.MAXWINMASK]; + unpPtr = (unpPtr + 1) & PackDef.MAXWINMASK; } } } @@ -796,9 +796,9 @@ namespace SharpCompress.Compressors.Rar private bool readTables() { - byte[] bitLength = new byte[Compress.BC]; + byte[] bitLength = new byte[PackDef.BC]; - byte[] table = new byte[Compress.HUFF_TABLE_SIZE]; + byte[] table = new byte[PackDef.HUFF_TABLE_SIZE]; if (inAddr > readTop - 25) { if (!unpReadBuf()) @@ -824,7 +824,7 @@ namespace SharpCompress.Compressors.Rar } AddBits(2); - for (int i = 0; i < Compress.BC; i++) + for (int i = 0; i < PackDef.BC; i++) { int length = (Utility.URShift(GetBits(), 12)) & 0xFF; AddBits(4); @@ -852,9 +852,9 @@ namespace SharpCompress.Compressors.Rar } } - UnpackUtility.makeDecodeTables(bitLength, 0, BD, Compress.BC); + UnpackUtility.makeDecodeTables(bitLength, 0, BD, PackDef.BC); - int TableSize = Compress.HUFF_TABLE_SIZE; + int TableSize = PackDef.HUFF_TABLE_SIZE; for (int i = 0; i < TableSize;) { @@ -914,10 +914,10 @@ namespace SharpCompress.Compressors.Rar { return (false); } - UnpackUtility.makeDecodeTables(table, 0, LD, Compress.NC); - UnpackUtility.makeDecodeTables(table, Compress.NC, DD, Compress.DC); - UnpackUtility.makeDecodeTables(table, Compress.NC + Compress.DC, LDD, Compress.LDC); - UnpackUtility.makeDecodeTables(table, Compress.NC + Compress.DC + Compress.LDC, RD, Compress.RC); + UnpackUtility.makeDecodeTables(table, 0, LD, PackDef.NC); + UnpackUtility.makeDecodeTables(table, PackDef.NC, DD, PackDef.DC); + UnpackUtility.makeDecodeTables(table, PackDef.NC + PackDef.DC, LDD, PackDef.LDC); + UnpackUtility.makeDecodeTables(table, PackDef.NC + PackDef.DC + PackDef.LDC, RD, PackDef.RC); // memcpy(unpOldTable,table,sizeof(unpOldTable)); @@ -1077,7 +1077,7 @@ namespace SharpCompress.Compressors.Rar { BlockStart += 258; } - StackFilter.BlockStart = ((BlockStart + unpPtr) & Compress.MAXWINMASK); + StackFilter.BlockStart = ((BlockStart + unpPtr) & PackDef.MAXWINMASK); if ((firstByte & 0x20) != 0) { StackFilter.BlockLength = RarVM.ReadData(Inp); @@ -1086,7 +1086,7 @@ namespace SharpCompress.Compressors.Rar { StackFilter.BlockLength = FiltPos < oldFilterLengths.Count ? oldFilterLengths[FiltPos] : 0; } - StackFilter.NextWindow = (wrPtr != unpPtr) && ((wrPtr - unpPtr) & Compress.MAXWINMASK) <= BlockStart; + StackFilter.NextWindow = (wrPtr != unpPtr) && ((wrPtr - unpPtr) & PackDef.MAXWINMASK) <= BlockStart; // DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x // BlockStart=%08x",UnpPtr,WrPtr,BlockStart); diff --git a/src/SharpCompress/Compressors/Rar/Unpack15.cs b/src/SharpCompress/Compressors/Rar/Unpack15.cs index c7c1c877..cc762635 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack15.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack15.cs @@ -174,13 +174,13 @@ namespace SharpCompress.Compressors.Rar while (destUnpSize >= 0) { - unpPtr &= Compress.MAXWINMASK; + unpPtr &= PackDef.MAXWINMASK; if (inAddr > readTop - 30 && !unpReadBuf()) { break; } - if (((wrPtr - unpPtr) & Compress.MAXWINMASK) < 270 && wrPtr != unpPtr) + if (((wrPtr - unpPtr) & PackDef.MAXWINMASK) < 270 && wrPtr != unpPtr) { oldUnpWriteBuf(); if (suspended) @@ -702,8 +702,8 @@ namespace SharpCompress.Compressors.Rar destUnpSize -= Length; while ((Length--) != 0) { - window[unpPtr] = window[(unpPtr - Distance) & Compress.MAXWINMASK]; - unpPtr = (unpPtr + 1) & Compress.MAXWINMASK; + window[unpPtr] = window[(unpPtr - Distance) & PackDef.MAXWINMASK]; + unpPtr = (unpPtr + 1) & PackDef.MAXWINMASK; } } @@ -726,7 +726,7 @@ namespace SharpCompress.Compressors.Rar } if (unpPtr < wrPtr) { - writeStream.Write(window, wrPtr, -wrPtr & Compress.MAXWINMASK); + writeStream.Write(window, wrPtr, -wrPtr & PackDef.MAXWINMASK); writeStream.Write(window, 0, unpPtr); unpAllBuf = true; } diff --git a/src/SharpCompress/Compressors/Rar/Unpack20.cs b/src/SharpCompress/Compressors/Rar/Unpack20.cs index 608442c1..e9a8dbdd 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack20.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack20.cs @@ -37,7 +37,7 @@ namespace SharpCompress.Compressors.Rar private void InitBlock() { - UnpOldTable20 = new byte[Compress.MC20 * 4]; + UnpOldTable20 = new byte[PackDef.MC20 * 4]; } protected internal MultDecode[] MD = new MultDecode[4]; @@ -124,7 +124,7 @@ namespace SharpCompress.Compressors.Rar while (destUnpSize >= 0) { - unpPtr &= Compress.MAXWINMASK; + unpPtr &= PackDef.MAXWINMASK; if (inAddr > readTop - 30) { @@ -133,7 +133,7 @@ namespace SharpCompress.Compressors.Rar break; } } - if (((wrPtr - unpPtr) & Compress.MAXWINMASK) < 270 && wrPtr != unpPtr) + if (((wrPtr - unpPtr) & PackDef.MAXWINMASK) < 270 && wrPtr != unpPtr) { oldUnpWriteBuf(); if (suspended) @@ -258,7 +258,7 @@ namespace SharpCompress.Compressors.Rar destUnpSize -= Length; int DestPtr = unpPtr - Distance; - if (DestPtr < Compress.MAXWINSIZE - 300 && unpPtr < Compress.MAXWINSIZE - 300) + if (DestPtr < PackDef.MAXWINSIZE - 300 && unpPtr < PackDef.MAXWINSIZE - 300) { window[unpPtr++] = window[DestPtr++]; window[unpPtr++] = window[DestPtr++]; @@ -272,16 +272,16 @@ namespace SharpCompress.Compressors.Rar { while ((Length--) != 0) { - window[unpPtr] = window[DestPtr++ & Compress.MAXWINMASK]; - unpPtr = (unpPtr + 1) & Compress.MAXWINMASK; + window[unpPtr] = window[DestPtr++ & PackDef.MAXWINMASK]; + unpPtr = (unpPtr + 1) & PackDef.MAXWINMASK; } } } private bool ReadTables20() { - byte[] BitLength = new byte[Compress.BC20]; - byte[] Table = new byte[Compress.MC20 * 4]; + byte[] BitLength = new byte[PackDef.BC20]; + byte[] Table = new byte[PackDef.MC20 * 4]; int TableSize, N, I; if (inAddr > readTop - 25) { @@ -308,18 +308,18 @@ namespace SharpCompress.Compressors.Rar UnpCurChannel = 0; } AddBits(2); - TableSize = Compress.MC20 * UnpChannels; + TableSize = PackDef.MC20 * UnpChannels; } else { - TableSize = Compress.NC20 + Compress.DC20 + Compress.RC20; + TableSize = PackDef.NC20 + PackDef.DC20 + PackDef.RC20; } - for (I = 0; I < Compress.BC20; I++) + for (I = 0; I < PackDef.BC20; I++) { BitLength[I] = (byte)(Utility.URShift(GetBits(), 12)); AddBits(4); } - UnpackUtility.makeDecodeTables(BitLength, 0, BD, Compress.BC20); + UnpackUtility.makeDecodeTables(BitLength, 0, BD, PackDef.BC20); I = 0; while (I < TableSize) { @@ -372,14 +372,14 @@ namespace SharpCompress.Compressors.Rar { for (I = 0; I < UnpChannels; I++) { - UnpackUtility.makeDecodeTables(Table, I * Compress.MC20, MD[I], Compress.MC20); + UnpackUtility.makeDecodeTables(Table, I * PackDef.MC20, MD[I], PackDef.MC20); } } else { - UnpackUtility.makeDecodeTables(Table, 0, LD, Compress.NC20); - UnpackUtility.makeDecodeTables(Table, Compress.NC20, DD, Compress.DC20); - UnpackUtility.makeDecodeTables(Table, Compress.NC20 + Compress.DC20, RD, Compress.RC20); + UnpackUtility.makeDecodeTables(Table, 0, LD, PackDef.NC20); + UnpackUtility.makeDecodeTables(Table, PackDef.NC20, DD, PackDef.DC20); + UnpackUtility.makeDecodeTables(Table, PackDef.NC20 + PackDef.DC20, RD, PackDef.RC20); } // memcpy(UnpOldTable20,Table,sizeof(UnpOldTable20)); diff --git a/src/SharpCompress/Compressors/Rar/Unpack50.cs b/src/SharpCompress/Compressors/Rar/Unpack50.cs new file mode 100644 index 00000000..85614d9c --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/Unpack50.cs @@ -0,0 +1,696 @@ +//using System; +//using System.Collections.Generic; +//using System.Linq; +//using System.Text; +//using System.Threading.Tasks; +// +//namespace SharpCompress.Compressors.Rar +//{ +// internal sealed class Unpack50 +// { +// +// public void Unpack5(bool Solid) { +// FileExtracted=true; +// +// if (!Suspended) +// { +// UnpInitData(Solid); +// if (!UnpReadBuf()) +// return; +// +// // Check TablesRead5 to be sure that we read tables at least once +// // regardless of current block header TablePresent flag. +// // So we can safefly use these tables below. +// if (!ReadBlockHeader(Inp,BlockHeader) || +// !ReadTables(Inp,BlockHeader,BlockTables) || !TablesRead5) +// return; +// } +// +// while (true) +// { +// UnpPtr&=MaxWinMask; +// +// if (Inp.InAddr>=ReadBorder) +// { +// bool FileDone=false; +// +// // We use 'while', because for empty block containing only Huffman table, +// // we'll be on the block border once again just after reading the table. +// while (Inp.InAddr>BlockHeader.BlockStart+BlockHeader.BlockSize-1 || +// Inp.InAddr==BlockHeader.BlockStart+BlockHeader.BlockSize-1 && +// Inp.InBit>=BlockHeader.BlockBitSize) +// { +// if (BlockHeader.LastBlockInFile) +// { +// FileDone=true; +// break; +// } +// if (!ReadBlockHeader(Inp,BlockHeader) || !ReadTables(Inp,BlockHeader,BlockTables)) +// return; +// } +// if (FileDone || !UnpReadBuf()) +// break; +// } +// +// if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) +// return; +// if (Suspended) +// { +// FileExtracted=false; +// return; +// } +// } +// +// uint MainSlot=DecodeNumber(Inp,&BlockTables.LD); +// if (MainSlot<256) +// { +// if (Fragmented) +// FragWindow[UnpPtr++]=(byte)MainSlot; +// else +// Window[UnpPtr++]=(byte)MainSlot; +// continue; +// } +// if (MainSlot>=262) +// { +// uint Length=SlotToLength(Inp,MainSlot-262); +// +// uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD); +// if (DistSlot<4) +// { +// DBits=0; +// Distance+=DistSlot; +// } +// else +// { +// DBits=DistSlot/2 - 1; +// Distance+=(2 | (DistSlot & 1)) << DBits; +// } +// +// if (DBits>0) +// { +// if (DBits>=4) +// { +// if (DBits>4) +// { +// Distance+=((Inp.getbits32()>>(36-DBits))<<4); +// Inp.addbits(DBits-4); +// } +// uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); +// Distance+=LowDist; +// } +// else +// { +// Distance+=Inp.getbits32()>>(32-DBits); +// Inp.addbits(DBits); +// } +// } +// +// if (Distance>0x100) +// { +// Length++; +// if (Distance>0x2000) +// { +// Length++; +// if (Distance>0x40000) +// Length++; +// } +// } +// +// InsertOldDist(Distance); +// LastLength=Length; +// if (Fragmented) +// FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); +// else +// CopyString(Length,Distance); +// continue; +// } +// if (MainSlot==256) +// { +// UnpackFilter Filter; +// if (!ReadFilter(Inp,Filter) || !AddFilter(Filter)) +// break; +// continue; +// } +// if (MainSlot==257) +// { +// if (LastLength!=0) +// if (Fragmented) +// FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask); +// else +// CopyString(LastLength,OldDist[0]); +// continue; +// } +// if (MainSlot<262) +// { +// uint DistNum=MainSlot-258; +// uint Distance=OldDist[DistNum]; +// for (uint I=DistNum;I>0;I--) +// OldDist[I]=OldDist[I-1]; +// OldDist[0]=Distance; +// +// uint LengthSlot=DecodeNumber(Inp,&BlockTables.RD); +// uint Length=SlotToLength(Inp,LengthSlot); +// LastLength=Length; +// if (Fragmented) +// FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); +// else +// CopyString(Length,Distance); +// continue; +// } +// } +// UnpWriteBuf(); +//} +// +// +//uint ReadFilterData(BitInput &Inp) +//{ +// uint ByteCount=(Inp.fgetbits()>>14)+1; +// Inp.addbits(2); +// +// uint Data=0; +// for (uint I=0;I>8)<<(I*8); +// Inp.addbits(8); +// } +// return Data; +//} +// +// +//bool ReadFilter(BitInput &Inp,UnpackFilter &Filter) +//{ +// if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16) +// if (!UnpReadBuf()) +// return false; +// +// Filter.BlockStart=ReadFilterData(Inp); +// Filter.BlockLength=ReadFilterData(Inp); +// if (Filter.BlockLength>MAX_FILTER_BLOCK_SIZE) +// Filter.BlockLength=0; +// +// Filter.Type=Inp.fgetbits()>>13; +// Inp.faddbits(3); +// +// if (Filter.Type==FILTER_DELTA) +// { +// Filter.Channels=(Inp.fgetbits()>>11)+1; +// Inp.faddbits(5); +// } +// +// return true; +//} +// +// +//bool AddFilter(UnpackFilter &Filter) +//{ +// if (Filters.Size()>=MAX_UNPACK_FILTERS) +// { +// UnpWriteBuf(); // Write data, apply and flush filters. +// if (Filters.Size()>=MAX_UNPACK_FILTERS) +// InitFilters(); // Still too many filters, prevent excessive memory use. +// } +// +// // If distance to filter start is that large that due to circular dictionary +// // mode now it points to old not written yet data, then we set 'NextWindow' +// // flag and process this filter only after processing that older data. +// Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart; +// +// Filter.BlockStart=uint((Filter.BlockStart+UnpPtr)&MaxWinMask); +// Filters.Push(Filter); +// return true; +//} +// +// +//bool UnpReadBuf() +//{ +// int DataSize=ReadTop-Inp.InAddr; // Data left to process. +// if (DataSize<0) +// return false; +// BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart; +// if (Inp.InAddr>BitInput::MAX_SIZE/2) +// { +// // If we already processed more than half of buffer, let's move +// // remaining data into beginning to free more space for new data +// // and ensure that calling function does not cross the buffer border +// // even if we did not read anything here. Also it ensures that read size +// // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk +// // to make it zero. +// if (DataSize>0) +// memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); +// Inp.InAddr=0; +// ReadTop=DataSize; +// } +// else +// DataSize=ReadTop; +// int ReadCode=0; +// if (BitInput::MAX_SIZE!=DataSize) +// ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize); +// if (ReadCode>0) // Can be also -1. +// ReadTop+=ReadCode; +// ReadBorder=ReadTop-30; +// BlockHeader.BlockStart=Inp.InAddr; +// if (BlockHeader.BlockSize!=-1) // '-1' means not defined yet. +// { +// // We may need to quit from main extraction loop and read new block header +// // and trees earlier than data in input buffer ends. +// ReadBorder=Min(ReadBorder,BlockHeader.BlockStart+BlockHeader.BlockSize-1); +// } +// return ReadCode!=-1; +//} +// +// +//void UnpWriteBuf() +//{ +// size_t WrittenBorder=WrPtr; +// size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; +// size_t WriteSizeLeft=FullWriteSize; +// bool NotAllFiltersProcessed=false; +// for (size_t I=0;IType==FILTER_NONE) +// continue; +// if (flt->NextWindow) +// { +// // Here we skip filters which have block start in current data range +// // due to address wrap around in circular dictionary, but actually +// // belong to next dictionary block. If such filter start position +// // is included to current write range, then we reset 'NextWindow' flag. +// // In fact we can reset it even without such check, because current +// // implementation seems to guarantee 'NextWindow' flag reset after +// // buffer writing for all existing filters. But let's keep this check +// // just in case. Compressor guarantees that distance between +// // filter block start and filter storing position cannot exceed +// // the dictionary size. So if we covered the filter block start with +// // our write here, we can safely assume that filter is applicable +// // to next block on no further wrap arounds is possible. +// if (((flt->BlockStart-WrPtr)&MaxWinMask)<=FullWriteSize) +// flt->NextWindow=false; +// continue; +// } +// uint BlockStart=flt->BlockStart; +// uint BlockLength=flt->BlockLength; +// if (((BlockStart-WrittenBorder)&MaxWinMask)0) // We set it to 0 also for invalid filters. +// { +// uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; +// +// FilterSrcMemory.Alloc(BlockLength); +// byte *Mem=&FilterSrcMemory[0]; +// if (BlockStartUnpWrite(OutMem,BlockLength); +// +// UnpSomeRead=true; +// WrittenFileSize+=BlockLength; +// WrittenBorder=BlockEnd; +// WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask; +// } +// } +// else +// { +// // Current filter intersects the window write border, so we adjust +// // the window border to process this filter next time, not now. +// WrPtr=WrittenBorder; +// +// // Since Filter start position can only increase, we quit processing +// // all following filters for this data block and reset 'NextWindow' +// // flag for them. +// for (size_t J=I;JType!=FILTER_NONE) +// flt->NextWindow=false; +// } +// +// // Do not write data left after current filter now. +// NotAllFiltersProcessed=true; +// break; +// } +// } +// } +// +// // Remove processed filters from queue. +// size_t EmptyCount=0; +// for (size_t I=0;I0) +// Filters[I-EmptyCount]=Filters[I]; +// if (Filters[I].Type==FILTER_NONE) +// EmptyCount++; +// } +// if (EmptyCount>0) +// Filters.Alloc(Filters.Size()-EmptyCount); +// +// if (!NotAllFiltersProcessed) // Only if all filters are processed. +// { +// // Write data left after last filter. +// UnpWriteArea(WrittenBorder,UnpPtr); +// WrPtr=UnpPtr; +// } +// +// // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE +// // instead of potentially huge MaxWinSize blocks. It also allows us +// // to keep the size of Filters array reasonable. +// WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask; +// +// // Choose the nearest among WriteBorder and WrPtr actual written border. +// // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. +// if (WriteBorder==UnpPtr || +// WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask)) +// WriteBorder=WrPtr; +//} +// +// +//byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt) +//{ +// byte *SrcData=Data; +// switch(Flt->Type) +// { +// case FILTER_E8: +// case FILTER_E8E9: +// { +// uint FileOffset=(uint)WrittenFileSize; +// +// const uint FileSize=0x1000000; +// byte CmpByte2=Flt->Type==FILTER_E8E9 ? 0xe9:0xe8; +// // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4" +// // to avoid overflow for DataSize<4. +// for (uint CurPos=0;CurPos+4=0 +// RawPut4(Addr+FileSize,Data); +// } +// else +// if (((Addr-FileSize) & 0x80000000)!=0) // Addr>8); +// D[2]=(byte)(Offset>>16); +// } +// } +// } +// return SrcData; +// case FILTER_DELTA: +// { +// // Unlike RAR3, we do not need to reject excessive channel +// // values here, since RAR5 uses only 5 bits to store channel. +// uint Channels=Flt->Channels,SrcPos=0; +// +// FilterDstMemory.Alloc(DataSize); +// byte *DstData=&FilterDstMemory[0]; +// +// // Bytes from same channels are grouped to continual data blocks, +// // so we need to place them back to their interleaving positions. +// for (uint CurChannel=0;CurChannel0) +// { +// size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); +// UnpWriteData(&FragWindow[StartPtr],BlockSize); +// SizeToWrite-=BlockSize; +// StartPtr=(StartPtr+BlockSize) & MaxWinMask; +// } +// } +// else +// if (EndPtr=DestUnpSize) +// return; +// size_t WriteSize=Size; +// int64 LeftToWrite=DestUnpSize-WrittenFileSize; +// if ((int64)WriteSize>LeftToWrite) +// WriteSize=(size_t)LeftToWrite; +// UnpIO->UnpWrite(Data,WriteSize); +// WrittenFileSize+=Size; +//} +// +// +//void UnpInitData50(bool Solid) +//{ +// if (!Solid) +// TablesRead5=false; +//} +// +// +//bool ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header) +//{ +// Header.HeaderSize=0; +// +// if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7) +// if (!UnpReadBuf()) +// return false; +// Inp.faddbits((8-Inp.InBit)&7); +// +// byte BlockFlags=Inp.fgetbits()>>8; +// Inp.faddbits(8); +// uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count. +// +// if (ByteCount==4) +// return false; +// +// Header.HeaderSize=2+ByteCount; +// +// Header.BlockBitSize=(BlockFlags&7)+1; +// +// byte SavedCheckSum=Inp.fgetbits()>>8; +// Inp.faddbits(8); +// +// int BlockSize=0; +// for (uint I=0;I>8)<<(I*8); +// Inp.addbits(8); +// } +// +// Header.BlockSize=BlockSize; +// byte CheckSum=byte(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16)); +// if (CheckSum!=SavedCheckSum) +// return false; +// +// Header.BlockStart=Inp.InAddr; +// ReadBorder=Min(ReadBorder,Header.BlockStart+Header.BlockSize-1); +// +// Header.LastBlockInFile=(BlockFlags & 0x40)!=0; +// Header.TablePresent=(BlockFlags & 0x80)!=0; +// return true; +//} +// +// +//bool ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables) +//{ +// if (!Header.TablePresent) +// return true; +// +// if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25) +// if (!UnpReadBuf()) +// return false; +// +// byte BitLength[BC]; +// for (uint I=0;I> 12); +// Inp.faddbits(4); +// if (Length==15) +// { +// uint ZeroCount=(byte)(Inp.fgetbits() >> 12); +// Inp.faddbits(4); +// if (ZeroCount==0) +// BitLength[I]=15; +// else +// { +// ZeroCount+=2; +// while (ZeroCount-- > 0 && IReadTop-5) +// if (!UnpReadBuf()) +// return false; +// uint Number=DecodeNumber(Inp,&Tables.BD); +// if (Number<16) +// { +// Table[I]=Number; +// I++; +// } +// else +// if (Number<18) +// { +// uint N; +// if (Number==16) +// { +// N=(Inp.fgetbits() >> 13)+3; +// Inp.faddbits(3); +// } +// else +// { +// N=(Inp.fgetbits() >> 9)+11; +// Inp.faddbits(7); +// } +// if (I==0) +// { +// // We cannot have "repeat previous" code at the first position. +// // Multiple such codes would shift Inp position without changing I, +// // which can lead to reading beyond of Inp boundary in mutithreading +// // mode, where Inp.ExternalBuffer disables bounds check and we just +// // reserve a lot of buffer space to not need such check normally. +// return false; +// } +// else +// while (N-- > 0 && I> 13)+3; +// Inp.faddbits(3); +// } +// else +// { +// N=(Inp.fgetbits() >> 9)+11; +// Inp.faddbits(7); +// } +// while (N-- > 0 && IReadTop) +// return false; +// MakeDecodeTables(&Table[0],&Tables.LD,NC); +// MakeDecodeTables(&Table[NC],&Tables.DD,DC); +// MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC); +// MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC); +// return true; +//} +// +// +//void InitFilters() +//{ +// Filters.SoftReset(); +//} +// +// } +//} From 0e57537167504f03523c32b953e65ff965eade4c Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 11:36:33 -0500 Subject: [PATCH 036/124] rar5: unpack wip --- src/SharpCompress/Compressors/Rar/Decode/PackDef.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs b/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs index 15ecdf13..a9e77f12 100644 --- a/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs +++ b/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs @@ -1,12 +1,11 @@ namespace SharpCompress.Compressors.Rar.Decode { - internal class PackDef + internal static class PackDef { public const int CODEBUFSIZE = 0x4000; public const int MAXWINSIZE = 0x400000; - //UPGRADE_NOTE: Final was removed from the declaration of 'MAXWINMASK '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public static readonly int MAXWINMASK = (MAXWINSIZE - 1); + public const int MAXWINMASK = (MAXWINSIZE - 1); public const int LOW_DIST_REP_COUNT = 16; @@ -15,8 +14,7 @@ namespace SharpCompress.Compressors.Rar.Decode public const int LDC = 17; public const int RC = 28; - //UPGRADE_NOTE: Final was removed from the declaration of 'HUFF_TABLE_SIZE '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public static readonly int HUFF_TABLE_SIZE = (NC + DC + RC + LDC); + public const int HUFF_TABLE_SIZE = (NC + DC + RC + LDC); public const int BC = 20; public const int NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ From f4506080735688927b9c9de6d029ce27f803f65a Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 11:46:17 -0500 Subject: [PATCH 037/124] rar5: unpack use partial class instead of inheritance --- src/SharpCompress/Compressors/Rar/Unpack.cs | 5 +- src/SharpCompress/Compressors/Rar/Unpack15.cs | 21 +------- src/SharpCompress/Compressors/Rar/Unpack20.cs | 49 +++---------------- 3 files changed, 12 insertions(+), 63 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/Unpack.cs b/src/SharpCompress/Compressors/Rar/Unpack.cs index f0cd37b5..66e1e40c 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack.cs @@ -10,7 +10,8 @@ using SharpCompress.Compressors.Rar.VM; namespace SharpCompress.Compressors.Rar { - internal sealed class Unpack : Unpack20 + //!!! internal sealed class Unpack : Unpack20 + internal partial class Unpack : BitInput { public bool FileExtracted { // Duplicate method // private boolean ReadEndOfBlock() throws IOException, RarException @@ -738,7 +739,7 @@ namespace SharpCompress.Compressors.Rar } } - protected internal override void unpInitData(bool solid) + protected internal void unpInitData(bool solid) { if (!solid) { diff --git a/src/SharpCompress/Compressors/Rar/Unpack15.cs b/src/SharpCompress/Compressors/Rar/Unpack15.cs index cc762635..24f098c3 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack15.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack15.cs @@ -3,34 +3,17 @@ * Original author: Edmund Wagner * Creation date: 21.06.2007 * -* Source: $HeadURL$ -* Last changed: $LastChangedDate$ -* * the unrar licence applies to all junrar source and binary distributions * you are not allowed to use this source to re-create the RAR compression algorithm -* -* Here some html entities which can be used for escaping javadoc tags: -* "&": "&" or "&" -* "<": "<" or "<" -* ">": ">" or ">" -* "@": "@" */ using System; using System.IO; using SharpCompress.Compressors.Rar.Decode; -using SharpCompress.Compressors.Rar.VM; namespace SharpCompress.Compressors.Rar { - /// DOCUMENT ME - /// - /// - /// $LastChangedBy$ - /// - /// $LastChangedRevision$ - /// - internal abstract class Unpack15 : BitInput + internal partial class Unpack { protected internal int readBorder; @@ -142,8 +125,6 @@ namespace SharpCompress.Compressors.Rar , 0x9c, 0xb0 }; - protected internal abstract void unpInitData(bool solid); - protected void unpack15(bool solid) { if (suspended) diff --git a/src/SharpCompress/Compressors/Rar/Unpack20.cs b/src/SharpCompress/Compressors/Rar/Unpack20.cs index e9a8dbdd..57b7e721 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack20.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack20.cs @@ -3,17 +3,8 @@ * Original author: Edmund Wagner * Creation date: 21.06.2007 * -* Source: $HeadURL$ -* Last changed: $LastChangedDate$ -* * the unrar licence applies to all junrar source and binary distributions * you are not allowed to use this source to re-create the RAR compression algorithm -* -* Here some html entities which can be used for escaping javadoc tags: -* "&": "&" or "&" -* "<": "<" or "<" -* ">": ">" or ">" -* "@": "@" */ using System; @@ -21,59 +12,38 @@ using SharpCompress.Compressors.Rar.Decode; namespace SharpCompress.Compressors.Rar { - /// DOCUMENT ME - /// - /// - /// $LastChangedBy$ - /// - /// $LastChangedRevision$ - /// - internal abstract class Unpack20 : Unpack15 + internal partial class Unpack { - public Unpack20() - { - InitBlock(); - } + protected readonly internal MultDecode[] MD = new MultDecode[4]; - private void InitBlock() - { - UnpOldTable20 = new byte[PackDef.MC20 * 4]; - } - - protected internal MultDecode[] MD = new MultDecode[4]; - - //UPGRADE_NOTE: The initialization of 'UnpOldTable20' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'" - protected internal byte[] UnpOldTable20; + protected readonly internal byte[] UnpOldTable20 = new byte[PackDef.MC20 * 4]; protected internal int UnpAudioBlock, UnpChannels, UnpCurChannel, UnpChannelDelta; private readonly AudioVariables[] AudV = new AudioVariables[4]; - protected internal LitDecode LD = new LitDecode(); + protected readonly internal LitDecode LD = new LitDecode(); - protected internal DistDecode DD = new DistDecode(); + protected readonly internal DistDecode DD = new DistDecode(); - protected internal LowDistDecode LDD = new LowDistDecode(); + protected readonly internal LowDistDecode LDD = new LowDistDecode(); - protected internal RepDecode RD = new RepDecode(); + protected readonly internal RepDecode RD = new RepDecode(); - protected internal BitDecode BD = new BitDecode(); + protected readonly internal BitDecode BD = new BitDecode(); - //UPGRADE_NOTE: Final was removed from the declaration of 'LDecode'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" public static readonly int[] LDecode = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224 }; - //UPGRADE_NOTE: Final was removed from the declaration of 'LBits'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" public static readonly byte[] LBits = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; - //UPGRADE_NOTE: Final was removed from the declaration of 'DDecode'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" public static readonly int[] DDecode = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, @@ -83,7 +53,6 @@ namespace SharpCompress.Compressors.Rar 917504, 983040 }; - //UPGRADE_NOTE: Final was removed from the declaration of 'DBits'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" public static readonly int[] DBits = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, @@ -91,10 +60,8 @@ namespace SharpCompress.Compressors.Rar , 16, 16, 16, 16, 16, 16, 16 }; - //UPGRADE_NOTE: Final was removed from the declaration of 'SDDecode'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" public static readonly int[] SDDecode = {0, 4, 8, 16, 32, 64, 128, 192}; - //UPGRADE_NOTE: Final was removed from the declaration of 'SDBits'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" public static readonly int[] SDBits = {2, 2, 3, 4, 5, 6, 6, 6}; internal void unpack20(bool solid) From c5b8a444f29c38e8bf004000bfbea29518deddf8 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 11:55:33 -0500 Subject: [PATCH 038/124] rar5: unpack tighten up access modifiers --- src/SharpCompress/Compressors/Rar/Unpack.cs | 76 +++++++------------ src/SharpCompress/Compressors/Rar/Unpack15.cs | 54 ++++++------- src/SharpCompress/Compressors/Rar/Unpack20.cs | 32 ++++---- 3 files changed, 71 insertions(+), 91 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/Unpack.cs b/src/SharpCompress/Compressors/Rar/Unpack.cs index 66e1e40c..c6545290 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack.cs @@ -10,26 +10,26 @@ using SharpCompress.Compressors.Rar.VM; namespace SharpCompress.Compressors.Rar { - //!!! internal sealed class Unpack : Unpack20 - internal partial class Unpack : BitInput + internal sealed partial class Unpack : BitInput { - public bool FileExtracted { // Duplicate method - // private boolean ReadEndOfBlock() throws IOException, RarException - // { - // int BitField = getbits(); - // boolean NewTable, NewFile = false; - // if ((BitField & 0x8000) != 0) { - // NewTable = true; - // addbits(1); - // } else { - // NewFile = true; - // NewTable = (BitField & 0x4000) != 0; - // addbits(2); - // } - // tablesRead = !NewTable; - // return !(NewFile || NewTable && !readTables()); - // } - get; private set; } + // Duplicate method + // private boolean ReadEndOfBlock() throws IOException, RarException + // { + // int BitField = getbits(); + // boolean NewTable, NewFile = false; + // if ((BitField & 0x8000) != 0) { + // NewTable = true; + // addbits(1); + // } else { + // NewFile = true; + // NewTable = (BitField & 0x4000) != 0; + // addbits(2); + // } + // tablesRead = !NewTable; + // return !(NewFile || NewTable && !readTables()); + // } + + public bool FileExtracted { get; private set; } public long DestSize { @@ -57,32 +57,24 @@ namespace SharpCompress.Compressors.Rar public int PpmEscChar { get; set; } - //UPGRADE_NOTE: Final was removed from the declaration of 'ppm '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" private readonly ModelPPM ppm = new ModelPPM(); private readonly RarVM rarVM = new RarVM(); - /* Filters code, one entry per filter */ - //UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'" + // Filters code, one entry per filter private readonly List filters = new List(); - /* Filters stack, several entrances of same filter are possible */ - //UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'" + // Filters stack, several entrances of same filter are possible private readonly List prgStack = new List(); - /* - * lengths of preceding blocks, one length per filter. Used to reduce size - * required to write block length if lengths are repeating - */ - //UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'" + // lengths of preceding blocks, one length per filter. Used to reduce size + // required to write block length if lengths are repeating private readonly List oldFilterLengths = new List(); private int lastFilter; private bool tablesRead; - //UPGRADE_NOTE: The initialization of 'unpOldTable' was moved to method 'InitBlock'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'" - private readonly byte[] unpOldTable = new byte[PackDef.HUFF_TABLE_SIZE]; private BlockTypes unpBlockType; @@ -97,21 +89,11 @@ namespace SharpCompress.Compressors.Rar private int lowDistRepCount; - public static int[] DBitLengthCounts = {4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 14, 0, 12}; + private static readonly int[] DBitLengthCounts = {4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 14, 0, 12}; private FileHeader fileHeader; - public Unpack() - { - window = null; - - //externalWindow = false; - suspended = false; - unpAllBuf = false; - unpSomeRead = false; - } - - public void init(byte[] window) + private void init(byte[] window) { if (window == null) { @@ -739,7 +721,7 @@ namespace SharpCompress.Compressors.Rar } } - protected internal void unpInitData(bool solid) + private void unpInitData(bool solid) { if (!solid) { @@ -788,7 +770,7 @@ namespace SharpCompress.Compressors.Rar else { NewFile = true; - NewTable = (BitField & 0x4000) != 0 ? true : false; + NewTable = (BitField & 0x4000) != 0; AddBits(2); } tablesRead = !NewTable; @@ -942,7 +924,6 @@ namespace SharpCompress.Compressors.Rar AddBits(16); } - //UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'" List vmCode = new List(); for (int I = 0; I < Length; I++) { @@ -988,7 +969,6 @@ namespace SharpCompress.Compressors.Rar Length = B1 * 256 + B2; } - //UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'" List vmCode = new List(); for (int I = 0; I < Length; I++) { @@ -1241,7 +1221,7 @@ namespace SharpCompress.Compressors.Rar } } - public void cleanUp() + private void cleanUp() { if (ppm != null) { diff --git a/src/SharpCompress/Compressors/Rar/Unpack15.cs b/src/SharpCompress/Compressors/Rar/Unpack15.cs index 24f098c3..4f1d502b 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack15.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack15.cs @@ -15,50 +15,50 @@ namespace SharpCompress.Compressors.Rar { internal partial class Unpack { - protected internal int readBorder; + private int readBorder; - protected internal bool suspended; + private bool suspended; - protected internal bool unpAllBuf; + internal bool unpAllBuf; - //protected internal ComprDataIO unpIO; + //private ComprDataIO unpIO; - protected Stream readStream; - protected Stream writeStream; + private Stream readStream; + private Stream writeStream; - protected internal bool unpSomeRead; + internal bool unpSomeRead; - protected internal int readTop; + private int readTop; - protected internal long destUnpSize; + private long destUnpSize; - protected internal byte[] window; + private byte[] window; - protected internal int[] oldDist = new int[4]; + private readonly int[] oldDist = new int[4]; - protected internal int unpPtr, wrPtr; + private int unpPtr, wrPtr; - protected internal int oldDistPtr; + private int oldDistPtr; - protected internal int[] ChSet = new int[256], + private readonly int[] ChSet = new int[256], ChSetA = new int[256], ChSetB = new int[256], ChSetC = new int[256]; - protected internal int[] Place = new int[256], + private readonly int[] Place = new int[256], PlaceA = new int[256], PlaceB = new int[256], PlaceC = new int[256]; - protected internal int[] NToPl = new int[256], NToPlB = new int[256], NToPlC = new int[256]; + private readonly int[] NToPl = new int[256], NToPlB = new int[256], NToPlC = new int[256]; - protected internal int FlagBuf, AvrPlc, AvrPlcB, AvrLn1, AvrLn2, AvrLn3; + private int FlagBuf, AvrPlc, AvrPlcB, AvrLn1, AvrLn2, AvrLn3; - protected internal int Buf60, NumHuf, StMode, LCount, FlagsCnt; + private int Buf60, NumHuf, StMode, LCount, FlagsCnt; - protected internal int Nhfb, Nlzb, MaxDist3; + private int Nhfb, Nlzb, MaxDist3; - protected internal int lastDist, lastLength; + private int lastDist, lastLength; private const int STARTL1 = 2; @@ -109,23 +109,23 @@ namespace SharpCompress.Compressors.Rar private static readonly int[] PosHf4 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0}; - internal static int[] ShortLen1 = {1, 3, 4, 4, 5, 6, 7, 8, 8, 4, 4, 5, 6, 6, 4, 0}; + private static readonly int[] ShortLen1 = {1, 3, 4, 4, 5, 6, 7, 8, 8, 4, 4, 5, 6, 6, 4, 0}; - internal static int[] ShortXor1 = + private static readonly int[] ShortXor1 = { 0, 0xa0, 0xd0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0xc0, 0x80, 0x90, 0x98 , 0x9c, 0xb0 }; - internal static int[] ShortLen2 = {2, 3, 3, 3, 4, 4, 5, 6, 6, 4, 4, 5, 6, 6, 4, 0}; + private static readonly int[] ShortLen2 = {2, 3, 3, 3, 4, 4, 5, 6, 6, 4, 4, 5, 6, 6, 4, 0}; - internal static int[] ShortXor2 = + private static readonly int[] ShortXor2 = { 0, 0x40, 0x60, 0xa0, 0xd0, 0xe0, 0xf0, 0xf8, 0xfc, 0xc0, 0x80, 0x90, 0x98 , 0x9c, 0xb0 }; - protected void unpack15(bool solid) + private void unpack15(bool solid) { if (suspended) { @@ -223,7 +223,7 @@ namespace SharpCompress.Compressors.Rar oldUnpWriteBuf(); } - protected bool unpReadBuf() + private bool unpReadBuf() { int dataSize = readTop - inAddr; if (dataSize < 0) @@ -699,7 +699,7 @@ namespace SharpCompress.Compressors.Rar return ((Utility.URShift((Num - (I != 0 ? DecTab[I - 1] : 0)), (16 - StartPos))) + PosTab[StartPos]); } - protected void oldUnpWriteBuf() + private void oldUnpWriteBuf() { if (unpPtr != wrPtr) { diff --git a/src/SharpCompress/Compressors/Rar/Unpack20.cs b/src/SharpCompress/Compressors/Rar/Unpack20.cs index 57b7e721..f4c43af1 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack20.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack20.cs @@ -14,37 +14,37 @@ namespace SharpCompress.Compressors.Rar { internal partial class Unpack { - protected readonly internal MultDecode[] MD = new MultDecode[4]; + private readonly MultDecode[] MD = new MultDecode[4]; - protected readonly internal byte[] UnpOldTable20 = new byte[PackDef.MC20 * 4]; + private readonly byte[] UnpOldTable20 = new byte[PackDef.MC20 * 4]; - protected internal int UnpAudioBlock, UnpChannels, UnpCurChannel, UnpChannelDelta; + private int UnpAudioBlock, UnpChannels, UnpCurChannel, UnpChannelDelta; private readonly AudioVariables[] AudV = new AudioVariables[4]; - protected readonly internal LitDecode LD = new LitDecode(); + private readonly LitDecode LD = new LitDecode(); - protected readonly internal DistDecode DD = new DistDecode(); + private readonly DistDecode DD = new DistDecode(); - protected readonly internal LowDistDecode LDD = new LowDistDecode(); + private readonly LowDistDecode LDD = new LowDistDecode(); - protected readonly internal RepDecode RD = new RepDecode(); + private readonly RepDecode RD = new RepDecode(); - protected readonly internal BitDecode BD = new BitDecode(); + private readonly BitDecode BD = new BitDecode(); - public static readonly int[] LDecode = + private static readonly int[] LDecode = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224 }; - public static readonly byte[] LBits = + private static readonly byte[] LBits = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; - public static readonly int[] DDecode = + private static readonly int[] DDecode = { 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, @@ -53,18 +53,18 @@ namespace SharpCompress.Compressors.Rar 917504, 983040 }; - public static readonly int[] DBits = + private static readonly int[] DBits = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16 , 16, 16, 16, 16, 16, 16, 16 }; - public static readonly int[] SDDecode = {0, 4, 8, 16, 32, 64, 128, 192}; + private static readonly int[] SDDecode = {0, 4, 8, 16, 32, 64, 128, 192}; - public static readonly int[] SDBits = {2, 2, 3, 4, 5, 6, 6, 6}; + private static readonly int[] SDBits = {2, 2, 3, 4, 5, 6, 6, 6}; - internal void unpack20(bool solid) + private void unpack20(bool solid) { int Bits; @@ -357,7 +357,7 @@ namespace SharpCompress.Compressors.Rar return (true); } - protected void unpInitData20(bool Solid) + private void unpInitData20(bool Solid) { if (!Solid) { From a2369fb53120a7d70e306348c1586e8d8e60e0a3 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 12:08:09 -0500 Subject: [PATCH 039/124] rar5: uppercase method names --- .../Compressors/Rar/RarStream.cs | 4 +- src/SharpCompress/Compressors/Rar/Unpack.cs | 103 ++++++++---------- src/SharpCompress/Compressors/Rar/Unpack15.cs | 2 +- src/SharpCompress/Compressors/Rar/Unpack20.cs | 2 +- src/SharpCompress/Compressors/Rar/Unpack50.cs | 10 +- 5 files changed, 52 insertions(+), 69 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/RarStream.cs b/src/SharpCompress/Compressors/Rar/RarStream.cs index c36f261c..6fdd49cd 100644 --- a/src/SharpCompress/Compressors/Rar/RarStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarStream.cs @@ -28,7 +28,7 @@ namespace SharpCompress.Compressors.Rar this.fileHeader = fileHeader; this.readStream = readStream; fetch = true; - unpack.doUnpack(fileHeader, readStream, this); + unpack.DoUnpack(fileHeader, readStream, this); fetch = false; } @@ -76,7 +76,7 @@ namespace SharpCompress.Compressors.Rar outOffset = offset; outCount = count; fetch = true; - unpack.doUnpack(); + unpack.DoUnpack(); fetch = false; } return outTotal; diff --git a/src/SharpCompress/Compressors/Rar/Unpack.cs b/src/SharpCompress/Compressors/Rar/Unpack.cs index c6545290..dc1cb17c 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack.cs @@ -12,23 +12,6 @@ namespace SharpCompress.Compressors.Rar { internal sealed partial class Unpack : BitInput { - // Duplicate method - // private boolean ReadEndOfBlock() throws IOException, RarException - // { - // int BitField = getbits(); - // boolean NewTable, NewFile = false; - // if ((BitField & 0x8000) != 0) { - // NewTable = true; - // addbits(1); - // } else { - // NewFile = true; - // NewTable = (BitField & 0x4000) != 0; - // addbits(2); - // } - // tablesRead = !NewTable; - // return !(NewFile || NewTable && !readTables()); - // } - public bool FileExtracted { get; private set; } public long DestSize @@ -93,7 +76,7 @@ namespace SharpCompress.Compressors.Rar private FileHeader fileHeader; - private void init(byte[] window) + private void Init(byte[] window) { if (window == null) { @@ -106,10 +89,10 @@ namespace SharpCompress.Compressors.Rar //externalWindow = true; } inAddr = 0; - unpInitData(false); + UnpInitData(false); } - public void doUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) + public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) { destUnpSize = fileHeader.UncompressedSize; this.fileHeader = fileHeader; @@ -117,17 +100,17 @@ namespace SharpCompress.Compressors.Rar this.readStream = readStream; if (!fileHeader.IsSolid) { - init(null); + Init(null); } suspended = false; - doUnpack(); + DoUnpack(); } - public void doUnpack() + public void DoUnpack() { if (fileHeader.CompressionMethod == 0) { - unstoreFile(); + UnstoreFile(); return; } var solid = fileHeader.IsSolid; @@ -144,7 +127,7 @@ namespace SharpCompress.Compressors.Rar case 29: // rar 3.x compression case 36: // alternative hash - unpack29(solid); + Unpack29(solid); break; // case 50: // rar 5.x compression @@ -156,7 +139,7 @@ namespace SharpCompress.Compressors.Rar } } - private void unstoreFile() + private void UnstoreFile() { byte[] buffer = new byte[0x10000]; while (true) @@ -179,7 +162,7 @@ namespace SharpCompress.Compressors.Rar } } - private void unpack29(bool solid) + private void Unpack29(bool solid) { int[] DDecode = new int[PackDef.DC]; byte[] DBits = new byte[PackDef.DC]; @@ -204,12 +187,12 @@ namespace SharpCompress.Compressors.Rar if (!suspended) { - unpInitData(solid); + UnpInitData(solid); if (!unpReadBuf()) { return; } - if ((!solid || !tablesRead) && !readTables()) + if ((!solid || !tablesRead) && !ReadTables()) { return; } @@ -260,7 +243,7 @@ namespace SharpCompress.Compressors.Rar int NextCh = ppm.decodeChar(); if (NextCh == 0) { - if (!readTables()) + if (!ReadTables()) { break; } @@ -272,7 +255,7 @@ namespace SharpCompress.Compressors.Rar } if (NextCh == 3) { - if (!readVMCodePPM()) + if (!ReadVMCodePPM()) { break; } @@ -307,7 +290,7 @@ namespace SharpCompress.Compressors.Rar { break; } - copyString(Length + 32, Distance + 2); + CopyString(Length + 32, Distance + 2); continue; } if (NextCh == 5) @@ -317,7 +300,7 @@ namespace SharpCompress.Compressors.Rar { break; } - copyString(Length + 4, 1); + CopyString(Length + 4, 1); continue; } } @@ -387,15 +370,15 @@ namespace SharpCompress.Compressors.Rar } } - insertOldDist(Distance); - insertLastMatch(Length, Distance); + InsertOldDist(Distance); + InsertLastMatch(Length, Distance); - copyString(Length, Distance); + CopyString(Length, Distance); continue; } if (Number == 256) { - if (!readEndOfBlock()) + if (!ReadEndOfBlock()) { break; } @@ -403,7 +386,7 @@ namespace SharpCompress.Compressors.Rar } if (Number == 257) { - if (!readVMCode()) + if (!ReadVMCode()) { break; } @@ -413,7 +396,7 @@ namespace SharpCompress.Compressors.Rar { if (lastLength != 0) { - copyString(lastLength, lastDist); + CopyString(lastLength, lastDist); } continue; } @@ -434,8 +417,8 @@ namespace SharpCompress.Compressors.Rar Length += Utility.URShift(GetBits(), (16 - Bits)); AddBits(Bits); } - insertLastMatch(Length, Distance); - copyString(Length, Distance); + InsertLastMatch(Length, Distance); + CopyString(Length, Distance); continue; } if (Number < 272) @@ -446,9 +429,9 @@ namespace SharpCompress.Compressors.Rar Distance += Utility.URShift(GetBits(), (16 - Bits)); AddBits(Bits); } - insertOldDist(Distance); - insertLastMatch(2, Distance); - copyString(2, Distance); + InsertOldDist(Distance); + InsertLastMatch(2, Distance); + CopyString(2, Distance); } } UnpWriteBuf(); @@ -681,7 +664,7 @@ namespace SharpCompress.Compressors.Rar destUnpSize -= size; } - private void insertOldDist(int distance) + private void InsertOldDist(int distance) { oldDist[3] = oldDist[2]; oldDist[2] = oldDist[1]; @@ -689,13 +672,13 @@ namespace SharpCompress.Compressors.Rar oldDist[0] = distance; } - private void insertLastMatch(int length, int distance) + private void InsertLastMatch(int length, int distance) { lastDist = distance; lastLength = length; } - private void copyString(int length, int distance) + private void CopyString(int length, int distance) { // System.out.println("copyString(" + length + ", " + distance + ")"); @@ -721,7 +704,7 @@ namespace SharpCompress.Compressors.Rar } } - private void unpInitData(bool solid) + private void UnpInitData(bool solid) { if (!solid) { @@ -738,7 +721,7 @@ namespace SharpCompress.Compressors.Rar wrPtr = 0; PpmEscChar = 2; - initFilters(); + InitFilters(); } InitBitInput(); ppmError = false; @@ -748,7 +731,7 @@ namespace SharpCompress.Compressors.Rar unpInitData20(solid); } - private void initFilters() + private void InitFilters() { oldFilterLengths.Clear(); lastFilter = 0; @@ -758,7 +741,7 @@ namespace SharpCompress.Compressors.Rar prgStack.Clear(); } - private bool readEndOfBlock() + private bool ReadEndOfBlock() { int BitField = GetBits(); bool NewTable, NewFile = false; @@ -774,10 +757,10 @@ namespace SharpCompress.Compressors.Rar AddBits(2); } tablesRead = !NewTable; - return !(NewFile || NewTable && !readTables()); + return !(NewFile || NewTable && !ReadTables()); } - private bool readTables() + private bool ReadTables() { byte[] bitLength = new byte[PackDef.BC]; @@ -908,7 +891,7 @@ namespace SharpCompress.Compressors.Rar return (true); } - private bool readVMCode() + private bool ReadVMCode() { int FirstByte = GetBits() >> 8; AddBits(8); @@ -934,10 +917,10 @@ namespace SharpCompress.Compressors.Rar vmCode.Add((byte)(GetBits() >> 8)); AddBits(8); } - return (addVMCode(FirstByte, vmCode, Length)); + return (AddVMCode(FirstByte, vmCode, Length)); } - private bool readVMCodePPM() + private bool ReadVMCodePPM() { int FirstByte = ppm.decodeChar(); if (FirstByte == -1) @@ -979,10 +962,10 @@ namespace SharpCompress.Compressors.Rar } vmCode.Add((byte)Ch); // VMCode[I]=Ch; } - return (addVMCode(FirstByte, vmCode, Length)); + return (AddVMCode(FirstByte, vmCode, Length)); } - private bool addVMCode(int firstByte, List vmCode, int length) + private bool AddVMCode(int firstByte, List vmCode, int length) { BitInput Inp = new BitInput(); Inp.InitBitInput(); @@ -1000,7 +983,7 @@ namespace SharpCompress.Compressors.Rar FiltPos = RarVM.ReadData(Inp); if (FiltPos == 0) { - initFilters(); + InitFilters(); } else { @@ -1221,7 +1204,7 @@ namespace SharpCompress.Compressors.Rar } } - private void cleanUp() + private void CleanUp() { if (ppm != null) { diff --git a/src/SharpCompress/Compressors/Rar/Unpack15.cs b/src/SharpCompress/Compressors/Rar/Unpack15.cs index 4f1d502b..c0158746 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack15.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack15.cs @@ -133,7 +133,7 @@ namespace SharpCompress.Compressors.Rar } else { - unpInitData(solid); + UnpInitData(solid); oldUnpInitData(solid); unpReadBuf(); if (!solid) diff --git a/src/SharpCompress/Compressors/Rar/Unpack20.cs b/src/SharpCompress/Compressors/Rar/Unpack20.cs index f4c43af1..12eeeed2 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack20.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack20.cs @@ -74,7 +74,7 @@ namespace SharpCompress.Compressors.Rar } else { - unpInitData(solid); + UnpInitData(solid); if (!unpReadBuf()) { return; diff --git a/src/SharpCompress/Compressors/Rar/Unpack50.cs b/src/SharpCompress/Compressors/Rar/Unpack50.cs index 85614d9c..c7c55cc4 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack50.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack50.cs @@ -6,15 +6,15 @@ // //namespace SharpCompress.Compressors.Rar //{ -// internal sealed class Unpack50 +// internal partial class Unpack // { // // public void Unpack5(bool Solid) { -// FileExtracted=true; +// FileExtracted=true; // -// if (!Suspended) -// { -// UnpInitData(Solid); +// if (!Suspended) +// { +// unpInitData(Solid); // if (!UnpReadBuf()) // return; // From cc175da0d8b9ab159b24ad8d706424d1b5009815 Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 17:08:53 -0500 Subject: [PATCH 040/124] rar5: unpack wip --- .../Compressors/Rar/Decode/FilterType.cs | 16 +- .../Compressors/Rar/Decode/PackDef.cs | 42 +- src/SharpCompress/Compressors/Rar/Unpack.cs | 60 +- src/SharpCompress/Compressors/Rar/Unpack50.cs | 1498 +++++++++-------- .../Compressors/Rar/UnpackFilter.cs | 7 + .../Compressors/Rar/UnpackUtility.cs | 5 + .../Compressors/Rar/VM/BitInput.cs | 25 +- 7 files changed, 955 insertions(+), 698 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/Decode/FilterType.cs b/src/SharpCompress/Compressors/Rar/Decode/FilterType.cs index 24e2b0b5..cb64f783 100644 --- a/src/SharpCompress/Compressors/Rar/Decode/FilterType.cs +++ b/src/SharpCompress/Compressors/Rar/Decode/FilterType.cs @@ -1,16 +1,10 @@ namespace SharpCompress.Compressors.Rar.Decode { - internal enum FilterType + internal enum FilterType : byte { - FILTER_NONE, - FILTER_PPM, - FILTER_E8, - FILTER_E8E9, - FILTER_UPCASETOLOW, - FILTER_AUDIO, - FILTER_RGB, - FILTER_DELTA, - FILTER_ITANIUM, - FILTER_E8E9V2 + // These values must not be changed, because we use them directly + // in RAR5 compression and decompression code. + FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM, + FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs b/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs index a9e77f12..472e7e5b 100644 --- a/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs +++ b/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs @@ -2,25 +2,47 @@ namespace SharpCompress.Compressors.Rar.Decode { internal static class PackDef { - public const int CODEBUFSIZE = 0x4000; + // 20171217 NOTE: these contants are gone from unrar src code + // seems to be more dynamic public const int MAXWINSIZE = 0x400000; + public const int MAXWINMASK = MAXWINSIZE - 1; - public const int MAXWINMASK = (MAXWINSIZE - 1); - + public const uint MAX_LZ_MATCH = 0x1001; + public const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. public const int LOW_DIST_REP_COUNT = 16; public const int NC = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ public const int DC = 60; public const int LDC = 17; public const int RC = 28; +// 20171217: NOTE: these constants seem to have been updated in the unrar src code +// at some unknown point. updating causes decompression failure, not sure why. +// public const int NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ +// public const int DC = 64; +// public const int LDC = 16; +// public const int RC = 44; + public const int HUFF_TABLE_SIZE = NC + DC + RC + LDC; + public const int BC = 20; - public const int HUFF_TABLE_SIZE = (NC + DC + RC + LDC); - public const int BC = 20; + public const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ + public const uint DC30 = 60; + public const uint LDC30 = 17; + public const uint RC30 = 28; + public const uint BC30 = 20; + public const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30; - public const int NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ - public const int DC20 = 48; - public const int RC20 = 28; - public const int BC20 = 19; - public const int MC20 = 257; + public const int NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ + public const int DC20 = 48; + public const int RC20 = 28; + public const int BC20 = 19; + public const int MC20 = 257; + + // Largest alphabet size among all values listed above. + public const uint LARGEST_TABLE_SIZE = 306; + + //public enum { + // CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, + // CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA + //} } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Unpack.cs b/src/SharpCompress/Compressors/Rar/Unpack.cs index dc1cb17c..b5c9db78 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack.cs @@ -12,6 +12,14 @@ namespace SharpCompress.Compressors.Rar { internal sealed partial class Unpack : BitInput { + private readonly BitInput Inp; + + public Unpack() { + // to ease in porting Unpack50.cs + this.Inp = this; + } + + public bool FileExtracted { get; private set; } public long DestSize @@ -24,7 +32,10 @@ namespace SharpCompress.Compressors.Rar } } - public bool Suspended { set => suspended = value; } + public bool Suspended { + get => this.suspended; + set => this.suspended = value; + } public int Char { @@ -130,9 +141,9 @@ namespace SharpCompress.Compressors.Rar Unpack29(solid); break; -// case 50: // rar 5.x compression -// unpack50(solid); -// break; + case 50: // rar 5.x compression + Unpack5(solid); + break; default: throw new InvalidFormatException("unknown rar compression version " + this.fileHeader.CompressionAlgorithm); @@ -664,6 +675,11 @@ namespace SharpCompress.Compressors.Rar destUnpSize -= size; } + private void InsertOldDist(uint distance) { + // TODO uint + InsertOldDist((int)distance); + } + private void InsertOldDist(int distance) { oldDist[3] = oldDist[2]; @@ -678,6 +694,11 @@ namespace SharpCompress.Compressors.Rar lastLength = length; } + private void CopyString(uint length, uint distance) { + // TODO uint + CopyString((int)length, (int)distance) ; + } + private void CopyString(int length, int distance) { // System.out.println("copyString(" + length + ", " + distance + ")"); @@ -720,6 +741,7 @@ namespace SharpCompress.Compressors.Rar unpPtr = 0; wrPtr = 0; PpmEscChar = 2; +WriteBorder=Math.Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; InitFilters(); } @@ -731,6 +753,36 @@ namespace SharpCompress.Compressors.Rar unpInitData20(solid); } +//void Unpack::UnpInitData(bool Solid) +//{ +// if (!Solid) +// { +// memset(OldDist,0,sizeof(OldDist)); +// OldDistPtr=0; +// LastDist=LastLength=0; +//// memset(Window,0,MaxWinSize); +// memset(&BlockTables,0,sizeof(BlockTables)); +// UnpPtr=WrPtr=0; +// WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; +// } +// // Filters never share several solid files, so we can safely reset them +// // even in solid archive. +// InitFilters(); +// +// Inp.InitBitInput(); +// WrittenFileSize=0; +// ReadTop=0; +// ReadBorder=0; +// +// memset(&BlockHeader,0,sizeof(BlockHeader)); +// BlockHeader.BlockSize=-1; // '-1' means not defined yet. +//#ifndef SFX_MODULE +// UnpInitData20(Solid); +//#endif +// UnpInitData30(Solid); +// UnpInitData50(Solid); +//} + private void InitFilters() { oldFilterLengths.Clear(); diff --git a/src/SharpCompress/Compressors/Rar/Unpack50.cs b/src/SharpCompress/Compressors/Rar/Unpack50.cs index c7c55cc4..fe9829b3 100644 --- a/src/SharpCompress/Compressors/Rar/Unpack50.cs +++ b/src/SharpCompress/Compressors/Rar/Unpack50.cs @@ -1,696 +1,852 @@ -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using System.Text; -//using System.Threading.Tasks; +#if true +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using SharpCompress.Compressors.Filters; +using SharpCompress.Compressors.Rar.Decode; +using SharpCompress.Compressors.Rar.VM; +using static SharpCompress.Compressors.Rar.Decode.PackDef; + +using size_t=System.UInt32; +using UnpackBlockHeader = SharpCompress.Compressors.Rar.Unpack; + +namespace SharpCompress.Compressors.Rar +{ + internal partial class Unpack + { + + // Maximum allowed number of compressed bits processed in quick mode. + private const int MAX_QUICK_DECODE_BITS = 10; + + // Maximum number of filters per entire data block. Must be at least + // twice more than MAX_PACK_FILTERS to store filters from two data blocks. + private const int MAX_UNPACK_FILTERS = 8192; + + // Maximum number of filters per entire data block for RAR3 unpack. + // Must be at least twice more than v3_MAX_PACK_FILTERS to store filters + // from two data blocks. + private const int MAX3_UNPACK_FILTERS = 8192; + + // Limit maximum number of channels in RAR3 delta filter to some reasonable + // value to prevent too slow processing of corrupt archives with invalid + // channels number. Must be equal or larger than v3_MAX_FILTER_CHANNELS. + // No need to provide it for RAR5, which uses only 5 bits to store channels. + private const int MAX3_UNPACK_CHANNELS = 1024; + + // Maximum size of single filter block. We restrict it to limit memory + // allocation. Must be equal or larger than MAX_ANALYZE_SIZE. + private const int MAX_FILTER_BLOCK_SIZE = 0x400000; + + // Write data in 4 MB or smaller blocks. Must not exceed PACK_MAX_WRITE, + // so we keep number of buffered filter in unpacker reasonable. + private const int UNPACK_MAX_WRITE = 0x400000; + + // Decode compressed bit fields to alphabet numbers. +// struct DecodeTable +// { +// // Real size of DecodeNum table. +// public uint MaxNum; // -//namespace SharpCompress.Compressors.Rar -//{ -// internal partial class Unpack -// { +// // Left aligned start and upper limit codes defining code space +// // ranges for bit lengths. DecodeLen[BitLength-1] defines the start of +// // range for bit length and DecodeLen[BitLength] defines next code +// // after the end of range or in other words the upper limit code +// // for specified bit length. +// //uint DecodeLen[16]; +// public uint [] DecodeLen = new uint[16]; // -// public void Unpack5(bool Solid) { -// FileExtracted=true; +// // Every item of this array contains the sum of all preceding items. +// // So it contains the start position in code list for every bit length. +// public uint DecodePos[16]; // -// if (!Suspended) +// // Number of compressed bits processed in quick mode. +// // Must not exceed MAX_QUICK_DECODE_BITS. +// public uint QuickBits; +// +// // Translates compressed bits (up to QuickBits length) +// // to bit length in quick mode. +// public byte QuickLen[1< Filters { get { return this.filters; } } + +// TODO: make sure these aren't already somewhere else +public int BlockSize; +public int BlockBitSize; +public int BlockStart; +public int HeaderSize; +public bool LastBlockInFile; +public bool TablePresent; + + public void Unpack5(bool Solid) { + FileExtracted=true; + + if (!Suspended) + { + UnpInitData(Solid); + if (!UnpReadBuf()) + return; + + // Check TablesRead5 to be sure that we read tables at least once + // regardless of current block header TablePresent flag. + // So we can safefly use these tables below. + if (!ReadBlockHeader() || + !ReadTables() || !TablesRead5) + return; + } + + while (true) + { + UnpPtr &= MaxWinMask; + + if (Inp.InAddr>=ReadBorder) + { + bool FileDone=false; + + // We use 'while', because for empty block containing only Huffman table, + // we'll be on the block border once again just after reading the table. + while (Inp.InAddr>BlockHeader.BlockStart+BlockHeader.BlockSize-1 || + Inp.InAddr==BlockHeader.BlockStart+BlockHeader.BlockSize-1 && + Inp.InBit>=BlockHeader.BlockBitSize) + { + if (BlockHeader.LastBlockInFile) + { + FileDone=true; + break; + } + if (!ReadBlockHeader() || !ReadTables()) + return; + } + if (FileDone || !UnpReadBuf()) + break; + } + + if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) + return; + if (Suspended) + { + FileExtracted=false; + return; + } + } + + //uint MainSlot=DecodeNumber(Inp,LD); + uint MainSlot= this.DecodeNumber(LD); + if (MainSlot<256) + { +// if (Fragmented) +// FragWindow[UnpPtr++]=(byte)MainSlot; +// else + Window[UnpPtr++]=(byte)MainSlot; + continue; + } + if (MainSlot>=262) + { + uint Length=SlotToLength(MainSlot-262); + + //uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD); + int DBits; + uint Distance=1,DistSlot=this.DecodeNumber(DD); + if (DistSlot<4) + { + DBits=0; + Distance+=DistSlot; + } + else + { + //DBits=DistSlot/2 - 1; + DBits=(int)(DistSlot/2 - 1); + Distance+=(2 | (DistSlot & 1)) << DBits; + } + + if (DBits>0) + { + if (DBits>=4) + { + if (DBits>4) + { + Distance+=((Inp.getbits()>>(36-DBits))<<4); + Inp.AddBits(DBits-4); + } + //uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); + uint LowDist=this.DecodeNumber(LDD); + Distance+=LowDist; + } + else + { + Distance+=Inp.getbits()>>(32-DBits); + Inp.AddBits(DBits); + } + } + + if (Distance>0x100) + { + Length++; + if (Distance>0x2000) + { + Length++; + if (Distance>0x40000) + Length++; + } + } + + InsertOldDist(Distance); + LastLength=Length; +// if (Fragmented) +// FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); +// else + CopyString(Length,Distance); + continue; + } + if (MainSlot==256) + { + UnpackFilter Filter = new UnpackFilter(); + if (!ReadFilter(Filter) || !AddFilter(Filter)) + break; + continue; + } + if (MainSlot==257) + { + if (LastLength!=0) +// if (Fragmented) +// FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask); +// else + //CopyString(LastLength,OldDist[0]); + CopyString(LastLength,OldDistN(0)); + continue; + } + if (MainSlot<262) + { + //uint DistNum=MainSlot-258; + int DistNum=(int)(MainSlot-258); + //uint Distance=OldDist[DistNum]; + uint Distance=OldDistN(DistNum); + //for (uint I=DistNum;I>0;I--) + for (int I=DistNum;I>0;I--) + //OldDistN[I]=OldDistN(I-1); + SetOldDistN(I, OldDistN(I-1)); + //OldDistN[0]=Distance; + SetOldDistN(0, Distance); + + uint LengthSlot=this.DecodeNumber(RD); + uint Length=SlotToLength(LengthSlot); + LastLength=Length; +// if (Fragmented) +// FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); +// else + CopyString(Length,Distance); + continue; + } + } + UnpWriteBuf(); + } + + + uint ReadFilterData() + { + uint ByteCount=(Inp.fgetbits()>>14)+1; + Inp.AddBits(2); + + uint Data=0; + //for (uint I=0;I>8)<<(I*8); + Inp.AddBits(8); + } + return Data; + } + + + bool ReadFilter(UnpackFilter Filter) + { + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16) + if (!UnpReadBuf()) + return false; + + Filter.uBlockStart=ReadFilterData(); + Filter.uBlockLength=ReadFilterData(); + if (Filter.BlockLength>MAX_FILTER_BLOCK_SIZE) + Filter.BlockLength=0; + + //Filter.Type=Inp.fgetbits()>>13; + Filter.Type=(byte)(this.Inp.fgetbits()>>13); + Inp.faddbits(3); + + if (Filter.Type==(byte)FilterType.FILTER_DELTA) + { + //Filter.Channels=(Inp.fgetbits()>>11)+1; + Filter.Channels=(byte)((this.Inp.fgetbits()>>11)+1); + Inp.faddbits(5); + } + + return true; + } + + + bool AddFilter(UnpackFilter Filter) + { + if (Filters.Count>=MAX_UNPACK_FILTERS) + { + UnpWriteBuf(); // Write data, apply and flush filters. + if (Filters.Count>=MAX_UNPACK_FILTERS) + InitFilters(); // Still too many filters, prevent excessive memory use. + } + + // If distance to filter start is that large that due to circular dictionary + // mode now it points to old not written yet data, then we set 'NextWindow' + // flag and process this filter only after processing that older data. + Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart; + + Filter.uBlockStart=(uint)((Filter.BlockStart+UnpPtr)&MaxWinMask); + Filters.Add(Filter); + return true; + } + + + bool UnpReadBuf() + { + int DataSize=ReadTop-Inp.InAddr; // Data left to process. + if (DataSize<0) + return false; + BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart; + if (Inp.InAddr>BitInput.MAX_SIZE/2) + { + // If we already processed more than half of buffer, let's move + // remaining data into beginning to free more space for new data + // and ensure that calling function does not cross the buffer border + // even if we did not read anything here. Also it ensures that read size + // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk + // to make it zero. + if (DataSize>0) + //memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + Array.Copy(InBuf, inAddr, InBuf, 0, DataSize); + // TODO: perf + //Buffer.BlockCopy(InBuf, inAddr, InBuf, 0, DataSize); + + Inp.InAddr=0; + ReadTop=DataSize; + } + else + DataSize=ReadTop; + int ReadCode=0; + if (BitInput.MAX_SIZE!=DataSize) + //ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput.MAX_SIZE-DataSize); + ReadCode = readStream.Read(InBuf, DataSize, BitInput.MAX_SIZE-DataSize); + if (ReadCode>0) // Can be also -1. + ReadTop+=ReadCode; + ReadBorder=ReadTop-30; + BlockHeader.BlockStart=Inp.InAddr; + if (BlockHeader.BlockSize!=-1) // '-1' means not defined yet. + { + // We may need to quit from main extraction loop and read new block header + // and trees earlier than data in input buffer ends. + ReadBorder=Math.Min(ReadBorder,BlockHeader.BlockStart+BlockHeader.BlockSize-1); + } + return ReadCode!=-1; + } + +//? +// void UnpWriteBuf() +// { +// size_t WrittenBorder=WrPtr; +// size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; +// size_t WriteSizeLeft=FullWriteSize; +// bool NotAllFiltersProcessed=false; +// for (size_t I=0;IType==FilterType.FILTER_NONE) +// continue; +// if (flt->NextWindow) // { -// unpInitData(Solid); -// if (!UnpReadBuf()) -// return; -// -// // Check TablesRead5 to be sure that we read tables at least once -// // regardless of current block header TablePresent flag. -// // So we can safefly use these tables below. -// if (!ReadBlockHeader(Inp,BlockHeader) || -// !ReadTables(Inp,BlockHeader,BlockTables) || !TablesRead5) -// return; -// } -// -// while (true) -// { -// UnpPtr&=MaxWinMask; -// -// if (Inp.InAddr>=ReadBorder) -// { -// bool FileDone=false; -// -// // We use 'while', because for empty block containing only Huffman table, -// // we'll be on the block border once again just after reading the table. -// while (Inp.InAddr>BlockHeader.BlockStart+BlockHeader.BlockSize-1 || -// Inp.InAddr==BlockHeader.BlockStart+BlockHeader.BlockSize-1 && -// Inp.InBit>=BlockHeader.BlockBitSize) -// { -// if (BlockHeader.LastBlockInFile) -// { -// FileDone=true; -// break; -// } -// if (!ReadBlockHeader(Inp,BlockHeader) || !ReadTables(Inp,BlockHeader,BlockTables)) -// return; -// } -// if (FileDone || !UnpReadBuf()) -// break; -// } -// -// if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) -// return; -// if (Suspended) -// { -// FileExtracted=false; -// return; -// } -// } -// -// uint MainSlot=DecodeNumber(Inp,&BlockTables.LD); -// if (MainSlot<256) -// { -// if (Fragmented) -// FragWindow[UnpPtr++]=(byte)MainSlot; -// else -// Window[UnpPtr++]=(byte)MainSlot; -// continue; -// } -// if (MainSlot>=262) -// { -// uint Length=SlotToLength(Inp,MainSlot-262); -// -// uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD); -// if (DistSlot<4) -// { -// DBits=0; -// Distance+=DistSlot; -// } -// else -// { -// DBits=DistSlot/2 - 1; -// Distance+=(2 | (DistSlot & 1)) << DBits; -// } -// -// if (DBits>0) -// { -// if (DBits>=4) -// { -// if (DBits>4) -// { -// Distance+=((Inp.getbits32()>>(36-DBits))<<4); -// Inp.addbits(DBits-4); -// } -// uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); -// Distance+=LowDist; -// } -// else -// { -// Distance+=Inp.getbits32()>>(32-DBits); -// Inp.addbits(DBits); -// } -// } -// -// if (Distance>0x100) -// { -// Length++; -// if (Distance>0x2000) -// { -// Length++; -// if (Distance>0x40000) -// Length++; -// } -// } -// -// InsertOldDist(Distance); -// LastLength=Length; -// if (Fragmented) -// FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); -// else -// CopyString(Length,Distance); -// continue; -// } -// if (MainSlot==256) -// { -// UnpackFilter Filter; -// if (!ReadFilter(Inp,Filter) || !AddFilter(Filter)) -// break; -// continue; -// } -// if (MainSlot==257) -// { -// if (LastLength!=0) -// if (Fragmented) -// FragWindow.CopyString(LastLength,OldDist[0],UnpPtr,MaxWinMask); -// else -// CopyString(LastLength,OldDist[0]); -// continue; -// } -// if (MainSlot<262) -// { -// uint DistNum=MainSlot-258; -// uint Distance=OldDist[DistNum]; -// for (uint I=DistNum;I>0;I--) -// OldDist[I]=OldDist[I-1]; -// OldDist[0]=Distance; -// -// uint LengthSlot=DecodeNumber(Inp,&BlockTables.RD); -// uint Length=SlotToLength(Inp,LengthSlot); -// LastLength=Length; -// if (Fragmented) -// FragWindow.CopyString(Length,Distance,UnpPtr,MaxWinMask); -// else -// CopyString(Length,Distance); -// continue; -// } -// } -// UnpWriteBuf(); -//} -// -// -//uint ReadFilterData(BitInput &Inp) -//{ -// uint ByteCount=(Inp.fgetbits()>>14)+1; -// Inp.addbits(2); -// -// uint Data=0; -// for (uint I=0;I>8)<<(I*8); -// Inp.addbits(8); -// } -// return Data; -//} -// -// -//bool ReadFilter(BitInput &Inp,UnpackFilter &Filter) -//{ -// if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16) -// if (!UnpReadBuf()) -// return false; -// -// Filter.BlockStart=ReadFilterData(Inp); -// Filter.BlockLength=ReadFilterData(Inp); -// if (Filter.BlockLength>MAX_FILTER_BLOCK_SIZE) -// Filter.BlockLength=0; -// -// Filter.Type=Inp.fgetbits()>>13; -// Inp.faddbits(3); -// -// if (Filter.Type==FILTER_DELTA) -// { -// Filter.Channels=(Inp.fgetbits()>>11)+1; -// Inp.faddbits(5); -// } -// -// return true; -//} -// -// -//bool AddFilter(UnpackFilter &Filter) -//{ -// if (Filters.Size()>=MAX_UNPACK_FILTERS) -// { -// UnpWriteBuf(); // Write data, apply and flush filters. -// if (Filters.Size()>=MAX_UNPACK_FILTERS) -// InitFilters(); // Still too many filters, prevent excessive memory use. -// } -// -// // If distance to filter start is that large that due to circular dictionary -// // mode now it points to old not written yet data, then we set 'NextWindow' -// // flag and process this filter only after processing that older data. -// Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart; -// -// Filter.BlockStart=uint((Filter.BlockStart+UnpPtr)&MaxWinMask); -// Filters.Push(Filter); -// return true; -//} -// -// -//bool UnpReadBuf() -//{ -// int DataSize=ReadTop-Inp.InAddr; // Data left to process. -// if (DataSize<0) -// return false; -// BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart; -// if (Inp.InAddr>BitInput::MAX_SIZE/2) -// { -// // If we already processed more than half of buffer, let's move -// // remaining data into beginning to free more space for new data -// // and ensure that calling function does not cross the buffer border -// // even if we did not read anything here. Also it ensures that read size -// // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk -// // to make it zero. -// if (DataSize>0) -// memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); -// Inp.InAddr=0; -// ReadTop=DataSize; -// } -// else -// DataSize=ReadTop; -// int ReadCode=0; -// if (BitInput::MAX_SIZE!=DataSize) -// ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput::MAX_SIZE-DataSize); -// if (ReadCode>0) // Can be also -1. -// ReadTop+=ReadCode; -// ReadBorder=ReadTop-30; -// BlockHeader.BlockStart=Inp.InAddr; -// if (BlockHeader.BlockSize!=-1) // '-1' means not defined yet. -// { -// // We may need to quit from main extraction loop and read new block header -// // and trees earlier than data in input buffer ends. -// ReadBorder=Min(ReadBorder,BlockHeader.BlockStart+BlockHeader.BlockSize-1); -// } -// return ReadCode!=-1; -//} -// -// -//void UnpWriteBuf() -//{ -// size_t WrittenBorder=WrPtr; -// size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; -// size_t WriteSizeLeft=FullWriteSize; -// bool NotAllFiltersProcessed=false; -// for (size_t I=0;IType==FILTER_NONE) -// continue; -// if (flt->NextWindow) -// { -// // Here we skip filters which have block start in current data range -// // due to address wrap around in circular dictionary, but actually -// // belong to next dictionary block. If such filter start position -// // is included to current write range, then we reset 'NextWindow' flag. -// // In fact we can reset it even without such check, because current -// // implementation seems to guarantee 'NextWindow' flag reset after -// // buffer writing for all existing filters. But let's keep this check -// // just in case. Compressor guarantees that distance between -// // filter block start and filter storing position cannot exceed -// // the dictionary size. So if we covered the filter block start with -// // our write here, we can safely assume that filter is applicable -// // to next block on no further wrap arounds is possible. -// if (((flt->BlockStart-WrPtr)&MaxWinMask)<=FullWriteSize) -// flt->NextWindow=false; -// continue; -// } -// uint BlockStart=flt->BlockStart; -// uint BlockLength=flt->BlockLength; -// if (((BlockStart-WrittenBorder)&MaxWinMask)0) // We set it to 0 also for invalid filters. -// { -// uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; -// -// FilterSrcMemory.Alloc(BlockLength); -// byte *Mem=&FilterSrcMemory[0]; -// if (BlockStartBlockStart-WrPtr)&MaxWinMask)<=FullWriteSize) +// flt->NextWindow=false; +// continue; // } -// else +// uint BlockStart=flt->BlockStart; +// uint BlockLength=flt->BlockLength; +// if (((BlockStart-WrittenBorder)&MaxWinMask)0) // We set it to 0 also for invalid filters. +// { +// uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; +// +// FilterSrcMemory.Alloc(BlockLength); +// byte *Mem=&FilterSrcMemory[0]; +// if (BlockStartUnpWrite(OutMem,BlockLength); +// +// UnpSomeRead=true; +// WrittenFileSize+=BlockLength; +// WrittenBorder=BlockEnd; +// WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask; +// } +// } +// else +// { +// // Current filter intersects the window write border, so we adjust +// // the window border to process this filter next time, not now. +// WrPtr=WrittenBorder; +// +// // Since Filter start position can only increase, we quit processing +// // all following filters for this data block and reset 'NextWindow' +// // flag for them. +// for (size_t J=I;JType!=FilterType.FILTER_NONE) +// flt->NextWindow=false; +// } +// +// // Do not write data left after current filter now. +// NotAllFiltersProcessed=true; +// break; +// } // } // } // -// byte *OutMem=ApplyFilter(Mem,BlockLength,flt); -// -// Filters[I].Type=FILTER_NONE; -// -// if (OutMem!=NULL) -// UnpIO->UnpWrite(OutMem,BlockLength); -// -// UnpSomeRead=true; -// WrittenFileSize+=BlockLength; -// WrittenBorder=BlockEnd; -// WriteSizeLeft=(UnpPtr-WrittenBorder)&MaxWinMask; -// } -// } -// else -// { -// // Current filter intersects the window write border, so we adjust -// // the window border to process this filter next time, not now. -// WrPtr=WrittenBorder; -// -// // Since Filter start position can only increase, we quit processing -// // all following filters for this data block and reset 'NextWindow' -// // flag for them. -// for (size_t J=I;JType!=FILTER_NONE) -// flt->NextWindow=false; -// } -// -// // Do not write data left after current filter now. -// NotAllFiltersProcessed=true; -// break; -// } -// } -// } -// -// // Remove processed filters from queue. -// size_t EmptyCount=0; -// for (size_t I=0;I0) -// Filters[I-EmptyCount]=Filters[I]; -// if (Filters[I].Type==FILTER_NONE) -// EmptyCount++; -// } -// if (EmptyCount>0) -// Filters.Alloc(Filters.Size()-EmptyCount); -// -// if (!NotAllFiltersProcessed) // Only if all filters are processed. -// { -// // Write data left after last filter. -// UnpWriteArea(WrittenBorder,UnpPtr); -// WrPtr=UnpPtr; -// } -// -// // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE -// // instead of potentially huge MaxWinSize blocks. It also allows us -// // to keep the size of Filters array reasonable. -// WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask; -// -// // Choose the nearest among WriteBorder and WrPtr actual written border. -// // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. -// if (WriteBorder==UnpPtr || -// WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask)) -// WriteBorder=WrPtr; -//} -// -// -//byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt) -//{ -// byte *SrcData=Data; -// switch(Flt->Type) -// { -// case FILTER_E8: -// case FILTER_E8E9: -// { -// uint FileOffset=(uint)WrittenFileSize; -// -// const uint FileSize=0x1000000; -// byte CmpByte2=Flt->Type==FILTER_E8E9 ? 0xe9:0xe8; -// // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4" -// // to avoid overflow for DataSize<4. -// for (uint CurPos=0;CurPos+40) +// Filters[I-EmptyCount]=Filters[I]; +// if (Filters[I].Type==FilterType.FILTER_NONE) +// EmptyCount++; +// } +// if (EmptyCount>0) +// Filters.Alloc(Filters.Size()-EmptyCount); // -// // We check 0x80000000 bit instead of '< 0' comparison -// // not assuming int32 presence or uint size and endianness. -// if ((Addr & 0x80000000)!=0) // Addr<0 +// if (!NotAllFiltersProcessed) // Only if all filters are processed. +// { +// // Write data left after last filter. +// UnpWriteArea(WrittenBorder,UnpPtr); +// WrPtr=UnpPtr; +// } +// +// // We prefer to write data in blocks not exceeding UNPACK_MAX_WRITE +// // instead of potentially huge MaxWinSize blocks. It also allows us +// // to keep the size of Filters array reasonable. +// WriteBorder=(UnpPtr+Min(MaxWinSize,UNPACK_MAX_WRITE))&MaxWinMask; +// +// // Choose the nearest among WriteBorder and WrPtr actual written border. +// // If border is equal to UnpPtr, it means that we have MaxWinSize data ahead. +// if (WriteBorder==UnpPtr || +// WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<((WriteBorder-UnpPtr)&MaxWinMask)) +// WriteBorder=WrPtr; +// } + + +// unused + //x byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter *Flt) +// byte[] ApplyFilter(byte []Data, uint DataSize, UnpackFilter Flt) +// { +// //x byte *SrcData=Data; +// byte []SrcData=Data; +// switch(Flt.Type) +// { +// case (byte)FilterType.FILTER_E8: +// case (byte)FilterType.FILTER_E8E9: +// { +// uint FileOffset=(uint)WrittenFileSize; +// +// const uint FileSize=0x1000000; +// byte CmpByte2=Flt.Type==(byte)FilterType.FILTER_E8E9 ? (byte)0xe9 : (byte)0xe8; +// // DataSize is unsigned, so we use "CurPos+4" and not "DataSize-4" +// // to avoid overflow for DataSize<4. +// for (uint CurPos=0;CurPos+4=0 +// RawPut4(Addr+FileSize,Data); +// } +// else +// if (((Addr-FileSize) & 0x80000000)!=0) // Addr>8); +// D[2]=(byte)(Offset>>16); +// } +// } +// } +// return SrcData; +// case (byte)FilterType.FILTER_DELTA: +// { +// // Unlike RAR3, we do not need to reject excessive channel +// // values here, since RAR5 uses only 5 bits to store channel. +// uint Channels=Flt->Channels,SrcPos=0; +// +// FilterDstMemory.Alloc(DataSize); +// byte *DstData=&FilterDstMemory[0]; +// +// // Bytes from same channels are grouped to continual data blocks, +// // so we need to place them back to their interleaving positions. +// for (uint CurChannel=0;CurChannel0) +//// { +//// size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); +//// UnpWriteData(&FragWindow[StartPtr],BlockSize); +//// SizeToWrite-=BlockSize; +//// StartPtr=(StartPtr+BlockSize) & MaxWinMask; +//// } +//// } +//// else +// if (EndPtr=0 -// RawPut4(Addr+FileSize,Data); +// UnpWriteData(Window+StartPtr,MaxWinSize-StartPtr); +// UnpWriteData(Window,EndPtr); // } // else -// if (((Addr-FileSize) & 0x80000000)!=0) // Addr=DestUnpSize) +// return; +// size_t WriteSize=Size; +// long LeftToWrite=DestUnpSize-WrittenFileSize; +// if ((long)WriteSize>LeftToWrite) +// WriteSize=(size_t)LeftToWrite; +// UnpIO->UnpWrite(Data,WriteSize); +// WrittenFileSize+=Size; +// } + + + void UnpInitData50(bool Solid) + { + if (!Solid) + TablesRead5=false; + } + + + bool ReadBlockHeader() + { + Header.HeaderSize=0; + + if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7) + if (!UnpReadBuf()) + return false; + //Inp.faddbits((8-Inp.InBit)&7); + Inp.faddbits((uint)((8-this.Inp.InBit)&7)); + + byte BlockFlags=(byte)(Inp.fgetbits()>>8); + Inp.faddbits(8); + //uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count. + uint ByteCount=(uint)(((BlockFlags>>3)&3)+1); // Block size byte count. + + if (ByteCount==4) + return false; + + //Header.HeaderSize=2+ByteCount; + Header.HeaderSize=(int)(2+ByteCount); + + Header.BlockBitSize=(BlockFlags&7)+1; + + byte SavedCheckSum=(byte)(Inp.fgetbits()>>8); + Inp.faddbits(8); + + int BlockSize=0; + //for (uint I=0;I>8)<<(I*8); + BlockSize+=(int)(Inp.fgetbits()>>8)<<(I*8); + Inp.AddBits(8); + } + + Header.BlockSize=BlockSize; + byte CheckSum=(byte)(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16)); + if (CheckSum!=SavedCheckSum) + return false; + + Header.BlockStart=Inp.InAddr; + ReadBorder=Math.Min(ReadBorder,Header.BlockStart+Header.BlockSize-1); + + Header.LastBlockInFile=(BlockFlags & 0x40)!=0; + Header.TablePresent=(BlockFlags & 0x80)!=0; + return true; + } + +//? +// bool ReadTables(BitInput Inp, ref UnpackBlockHeader Header, ref UnpackBlockTables Tables) +// { +// if (!Header.TablePresent) +// return true; +// +// if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25) +// if (!UnpReadBuf()) +// return false; +// +// byte BitLength[BC]; +// for (uint I=0;I>8); -// D[2]=(byte)(Offset>>16); +// uint Length=(byte)(Inp.fgetbits() >> 12); +// Inp.faddbits(4); +// if (Length==15) +// { +// uint ZeroCount=(byte)(Inp.fgetbits() >> 12); +// Inp.faddbits(4); +// if (ZeroCount==0) +// BitLength[I]=15; +// else +// { +// ZeroCount+=2; +// while (ZeroCount-- > 0 && IChannels,SrcPos=0; // -// FilterDstMemory.Alloc(DataSize); -// byte *DstData=&FilterDstMemory[0]; +// MakeDecodeTables(BitLength,&Tables.BD,BC); // -// // Bytes from same channels are grouped to continual data blocks, -// // so we need to place them back to their interleaving positions. -// for (uint CurChannel=0;CurChannel0) -// { -// size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); -// UnpWriteData(&FragWindow[StartPtr],BlockSize); -// SizeToWrite-=BlockSize; -// StartPtr=(StartPtr+BlockSize) & MaxWinMask; -// } -// } -// else -// if (EndPtr=DestUnpSize) -// return; -// size_t WriteSize=Size; -// int64 LeftToWrite=DestUnpSize-WrittenFileSize; -// if ((int64)WriteSize>LeftToWrite) -// WriteSize=(size_t)LeftToWrite; -// UnpIO->UnpWrite(Data,WriteSize); -// WrittenFileSize+=Size; -//} -// -// -//void UnpInitData50(bool Solid) -//{ -// if (!Solid) -// TablesRead5=false; -//} -// -// -//bool ReadBlockHeader(BitInput &Inp,UnpackBlockHeader &Header) -//{ -// Header.HeaderSize=0; -// -// if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7) -// if (!UnpReadBuf()) -// return false; -// Inp.faddbits((8-Inp.InBit)&7); -// -// byte BlockFlags=Inp.fgetbits()>>8; -// Inp.faddbits(8); -// uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count. -// -// if (ByteCount==4) -// return false; -// -// Header.HeaderSize=2+ByteCount; -// -// Header.BlockBitSize=(BlockFlags&7)+1; -// -// byte SavedCheckSum=Inp.fgetbits()>>8; -// Inp.faddbits(8); -// -// int BlockSize=0; -// for (uint I=0;I>8)<<(I*8); -// Inp.addbits(8); -// } -// -// Header.BlockSize=BlockSize; -// byte CheckSum=byte(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16)); -// if (CheckSum!=SavedCheckSum) -// return false; -// -// Header.BlockStart=Inp.InAddr; -// ReadBorder=Min(ReadBorder,Header.BlockStart+Header.BlockSize-1); -// -// Header.LastBlockInFile=(BlockFlags & 0x40)!=0; -// Header.TablePresent=(BlockFlags & 0x80)!=0; -// return true; -//} -// -// -//bool ReadTables(BitInput &Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables) -//{ -// if (!Header.TablePresent) -// return true; -// -// if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-25) -// if (!UnpReadBuf()) -// return false; -// -// byte BitLength[BC]; -// for (uint I=0;I> 12); -// Inp.faddbits(4); -// if (Length==15) -// { -// uint ZeroCount=(byte)(Inp.fgetbits() >> 12); -// Inp.faddbits(4); -// if (ZeroCount==0) -// BitLength[I]=15; -// else -// { -// ZeroCount+=2; -// while (ZeroCount-- > 0 && IReadTop-5) -// if (!UnpReadBuf()) -// return false; -// uint Number=DecodeNumber(Inp,&Tables.BD); -// if (Number<16) -// { -// Table[I]=Number; -// I++; -// } -// else -// if (Number<18) -// { -// uint N; -// if (Number==16) -// { -// N=(Inp.fgetbits() >> 13)+3; -// Inp.faddbits(3); -// } -// else -// { -// N=(Inp.fgetbits() >> 9)+11; -// Inp.faddbits(7); -// } -// if (I==0) -// { -// // We cannot have "repeat previous" code at the first position. -// // Multiple such codes would shift Inp position without changing I, -// // which can lead to reading beyond of Inp boundary in mutithreading -// // mode, where Inp.ExternalBuffer disables bounds check and we just -// // reserve a lot of buffer space to not need such check normally. -// return false; -// } -// else -// while (N-- > 0 && IReadTop-5) +// if (!UnpReadBuf()) +// return false; +// uint Number=DecodeNumber(Inp,&Tables.BD); +// if (Number<16) +// { +// Table[I]=Number; +// I++; +// } +// else +// if (Number<18) +// { +// uint N; +// if (Number==16) +// { +// N=(Inp.fgetbits() >> 13)+3; +// Inp.faddbits(3); +// } +// else +// { +// N=(Inp.fgetbits() >> 9)+11; +// Inp.faddbits(7); +// } +// if (I==0) +// { +// // We cannot have "repeat previous" code at the first position. +// // Multiple such codes would shift Inp position without changing I, +// // which can lead to reading beyond of Inp boundary in mutithreading +// // mode, where Inp.ExternalBuffer disables bounds check and we just +// // reserve a lot of buffer space to not need such check normally. +// return false; +// } +// else +// while (N-- > 0 && I> 13)+3; +// Inp.faddbits(3); +// } +// else +// { +// N=(Inp.fgetbits() >> 9)+11; +// Inp.faddbits(7); +// } +// while (N-- > 0 && I> 13)+3; -// Inp.faddbits(3); +// TablesRead5=true; +// if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop) +// return false; +// MakeDecodeTables(&Table[0],&Tables.LD,NC); +// MakeDecodeTables(&Table[NC],&Tables.DD,DC); +// MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC); +// MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC); +// return true; // } -// else + +//? +// void InitFilters() // { -// N=(Inp.fgetbits() >> 9)+11; -// Inp.faddbits(7); +// Filters.SoftReset(); // } -// while (N-- > 0 && IReadTop) -// return false; -// MakeDecodeTables(&Table[0],&Tables.LD,NC); -// MakeDecodeTables(&Table[NC],&Tables.DD,DC); -// MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC); -// MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC); -// return true; -//} -// -// -//void InitFilters() -//{ -// Filters.SoftReset(); -//} -// -// } -//} + + } +} +#endif \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackFilter.cs b/src/SharpCompress/Compressors/Rar/UnpackFilter.cs index d199a7e6..ddf103ed 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackFilter.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackFilter.cs @@ -4,11 +4,18 @@ namespace SharpCompress.Compressors.Rar { internal class UnpackFilter { + public byte Type; + public byte Channels; + internal UnpackFilter() { Program = new VMPreparedProgram(); } + // TODO uint + internal uint uBlockStart { get { return (uint)BlockStart; } set { BlockStart = (int)value; } } + internal uint uBlockLength { get { return (uint)BlockLength; } set { BlockLength = (int)value; } } + internal int BlockStart { get; set; } internal int BlockLength { get; set; } diff --git a/src/SharpCompress/Compressors/Rar/UnpackUtility.cs b/src/SharpCompress/Compressors/Rar/UnpackUtility.cs index 8f9cfada..3c7a0692 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackUtility.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackUtility.cs @@ -4,6 +4,11 @@ namespace SharpCompress.Compressors.Rar { internal static class UnpackUtility { +//!!! TODO rename methods + internal static uint DecodeNumber(this BitInput input, Decode.Decode dec) { + return (uint)input.decodeNumber(dec); + } + internal static int decodeNumber(this BitInput input, Decode.Decode dec) { int bits; diff --git a/src/SharpCompress/Compressors/Rar/VM/BitInput.cs b/src/SharpCompress/Compressors/Rar/VM/BitInput.cs index 74c525b2..3d1fd979 100644 --- a/src/SharpCompress/Compressors/Rar/VM/BitInput.cs +++ b/src/SharpCompress/Compressors/Rar/VM/BitInput.cs @@ -5,8 +5,14 @@ namespace SharpCompress.Compressors.Rar.VM /// the max size of the input internal const int MAX_SIZE = 0x8000; - protected int inAddr; - protected int inBit; + public int inAddr; + public int inBit; + +// TODO: rename var + public int InAddr { get { return this.inAddr; } set { this.inAddr = value; } } + public int InBit { get { return this.inBit; } set { this.inBit = value; } } + public bool ExternalBuffer; + /// internal BitInput() @@ -22,6 +28,11 @@ namespace SharpCompress.Compressors.Rar.VM inBit = 0; } + internal void faddbits(uint bits) { + // TODO uint + AddBits((int)bits); + } + /// /// also named faddbits /// @@ -33,6 +44,16 @@ namespace SharpCompress.Compressors.Rar.VM inBit = bits & 7; } + internal uint fgetbits() { + // TODO uint + return (uint)GetBits(); + } + + internal uint getbits() { + // TODO uint + return (uint)GetBits(); + } + /// /// (also named fgetbits) /// From e13795651dceb14a40f2035eb54aebd629b2d57c Mon Sep 17 00:00:00 2001 From: coderb Date: Sun, 17 Dec 2017 17:52:00 -0500 Subject: [PATCH 041/124] rar: import unrarsrc-5.5.8 for reference this will allow us to diff against newer version of unrarsrc if we wish to update our port --- reference/unrar/UnRAR.vcxproj | 279 ++++++ reference/unrar/UnRARDll.vcxproj | 420 +++++++++ reference/unrar/acknow.txt | 92 ++ reference/unrar/arccmt.cpp | 166 ++++ reference/unrar/archive.cpp | 332 +++++++ reference/unrar/archive.hpp | 151 ++++ reference/unrar/arcread.cpp | 1437 ++++++++++++++++++++++++++++++ reference/unrar/array.hpp | 191 ++++ reference/unrar/blake2s.cpp | 183 ++++ reference/unrar/blake2s.hpp | 101 +++ reference/unrar/blake2s_sse.cpp | 129 +++ reference/unrar/blake2sp.cpp | 153 ++++ reference/unrar/cmddata.cpp | 1400 +++++++++++++++++++++++++++++ reference/unrar/cmddata.hpp | 64 ++ reference/unrar/coder.cpp | 48 + reference/unrar/coder.hpp | 23 + reference/unrar/compress.hpp | 50 ++ reference/unrar/consio.cpp | 352 ++++++++ reference/unrar/consio.hpp | 26 + reference/unrar/crc.cpp | 102 +++ reference/unrar/crc.hpp | 15 + reference/unrar/crypt.cpp | 134 +++ reference/unrar/crypt.hpp | 101 +++ reference/unrar/crypt1.cpp | 79 ++ reference/unrar/crypt2.cpp | 133 +++ reference/unrar/crypt3.cpp | 68 ++ reference/unrar/crypt5.cpp | 233 +++++ reference/unrar/dll.cpp | 474 ++++++++++ reference/unrar/dll.def | 12 + reference/unrar/dll.hpp | 185 ++++ reference/unrar/dll.rc | 28 + reference/unrar/encname.cpp | 69 ++ reference/unrar/encname.hpp | 20 + reference/unrar/errhnd.cpp | 391 ++++++++ reference/unrar/errhnd.hpp | 70 ++ reference/unrar/extinfo.cpp | 178 ++++ reference/unrar/extinfo.hpp | 23 + reference/unrar/extract.cpp | 1167 ++++++++++++++++++++++++ reference/unrar/extract.hpp | 62 ++ reference/unrar/filcreat.cpp | 163 ++++ reference/unrar/filcreat.hpp | 14 + reference/unrar/file.cpp | 729 +++++++++++++++ reference/unrar/file.hpp | 126 +++ reference/unrar/filefn.cpp | 510 +++++++++++ reference/unrar/filefn.hpp | 50 ++ reference/unrar/filestr.cpp | 162 ++++ reference/unrar/filestr.hpp | 15 + reference/unrar/find.cpp | 218 +++++ reference/unrar/find.hpp | 49 + reference/unrar/getbits.cpp | 52 ++ reference/unrar/getbits.hpp | 68 ++ reference/unrar/global.cpp | 7 + reference/unrar/global.hpp | 14 + reference/unrar/hardlinks.cpp | 34 + reference/unrar/hash.cpp | 135 +++ reference/unrar/hash.hpp | 52 ++ reference/unrar/headers.cpp | 61 ++ reference/unrar/headers.hpp | 380 ++++++++ reference/unrar/headers5.hpp | 100 +++ reference/unrar/isnt.cpp | 24 + reference/unrar/isnt.hpp | 13 + reference/unrar/license.txt | 42 + reference/unrar/list.cpp | 472 ++++++++++ reference/unrar/list.hpp | 6 + reference/unrar/loclang.hpp | 381 ++++++++ reference/unrar/log.cpp | 37 + reference/unrar/log.hpp | 12 + reference/unrar/makefile | 173 ++++ reference/unrar/match.cpp | 145 +++ reference/unrar/match.hpp | 34 + reference/unrar/model.cpp | 617 +++++++++++++ reference/unrar/model.hpp | 122 +++ reference/unrar/options.cpp | 35 + reference/unrar/options.hpp | 191 ++++ reference/unrar/os.hpp | 253 ++++++ reference/unrar/pathfn.cpp | 983 ++++++++++++++++++++ reference/unrar/pathfn.hpp | 76 ++ reference/unrar/qopen.cpp | 272 ++++++ reference/unrar/qopen.hpp | 61 ++ reference/unrar/rar.cpp | 105 +++ reference/unrar/rar.hpp | 94 ++ reference/unrar/rardefs.hpp | 31 + reference/unrar/rarlang.hpp | 10 + reference/unrar/raros.hpp | 36 + reference/unrar/rarpch.cpp | 2 + reference/unrar/rartypes.hpp | 32 + reference/unrar/rarvm.cpp | 364 ++++++++ reference/unrar/rarvm.hpp | 44 + reference/unrar/rawint.hpp | 122 +++ reference/unrar/rawread.cpp | 197 ++++ reference/unrar/rawread.hpp | 41 + reference/unrar/rdwrfn.cpp | 319 +++++++ reference/unrar/rdwrfn.hpp | 97 ++ reference/unrar/readme.txt | 50 ++ reference/unrar/recvol.cpp | 111 +++ reference/unrar/recvol.hpp | 87 ++ reference/unrar/recvol3.cpp | 543 +++++++++++ reference/unrar/recvol5.cpp | 517 +++++++++++ reference/unrar/resource.cpp | 11 + reference/unrar/resource.hpp | 11 + reference/unrar/rijndael.cpp | 506 +++++++++++ reference/unrar/rijndael.hpp | 44 + reference/unrar/rs.cpp | 160 ++++ reference/unrar/rs.hpp | 32 + reference/unrar/rs16.cpp | 419 +++++++++ reference/unrar/rs16.hpp | 44 + reference/unrar/savepos.hpp | 21 + reference/unrar/scantree.cpp | 486 ++++++++++ reference/unrar/scantree.hpp | 78 ++ reference/unrar/secpassword.cpp | 216 +++++ reference/unrar/secpassword.hpp | 35 + reference/unrar/sha1.cpp | 204 +++++ reference/unrar/sha1.hpp | 15 + reference/unrar/sha256.cpp | 148 +++ reference/unrar/sha256.hpp | 17 + reference/unrar/smallfn.cpp | 19 + reference/unrar/smallfn.hpp | 8 + reference/unrar/strfn.cpp | 464 ++++++++++ reference/unrar/strfn.hpp | 50 ++ reference/unrar/strlist.cpp | 151 ++++ reference/unrar/strlist.hpp | 31 + reference/unrar/suballoc.cpp | 295 ++++++ reference/unrar/suballoc.hpp | 86 ++ reference/unrar/system.cpp | 175 ++++ reference/unrar/system.hpp | 39 + reference/unrar/threadmisc.cpp | 206 +++++ reference/unrar/threadpool.cpp | 214 +++++ reference/unrar/threadpool.hpp | 107 +++ reference/unrar/timefn.cpp | 340 +++++++ reference/unrar/timefn.hpp | 65 ++ reference/unrar/ui.cpp | 14 + reference/unrar/ui.hpp | 163 ++++ reference/unrar/uicommon.cpp | 65 ++ reference/unrar/uiconsole.cpp | 398 +++++++++ reference/unrar/uisilent.cpp | 63 ++ reference/unrar/ulinks.cpp | 102 +++ reference/unrar/unicode.cpp | 612 +++++++++++++ reference/unrar/unicode.hpp | 65 ++ reference/unrar/unpack.cpp | 354 ++++++++ reference/unrar/unpack.hpp | 403 +++++++++ reference/unrar/unpack15.cpp | 489 ++++++++++ reference/unrar/unpack20.cpp | 378 ++++++++ reference/unrar/unpack30.cpp | 765 ++++++++++++++++ reference/unrar/unpack50.cpp | 683 ++++++++++++++ reference/unrar/unpack50frag.cpp | 103 +++ reference/unrar/unpack50mt.cpp | 655 ++++++++++++++ reference/unrar/unpackinline.cpp | 147 +++ reference/unrar/uowners.cpp | 141 +++ reference/unrar/version.hpp | 6 + reference/unrar/volume.cpp | 288 ++++++ reference/unrar/volume.hpp | 10 + reference/unrar/win32acl.cpp | 129 +++ reference/unrar/win32lnk.cpp | 174 ++++ reference/unrar/win32stm.cpp | 147 +++ 154 files changed, 29882 insertions(+) create mode 100644 reference/unrar/UnRAR.vcxproj create mode 100644 reference/unrar/UnRARDll.vcxproj create mode 100644 reference/unrar/acknow.txt create mode 100644 reference/unrar/arccmt.cpp create mode 100644 reference/unrar/archive.cpp create mode 100644 reference/unrar/archive.hpp create mode 100644 reference/unrar/arcread.cpp create mode 100644 reference/unrar/array.hpp create mode 100644 reference/unrar/blake2s.cpp create mode 100644 reference/unrar/blake2s.hpp create mode 100644 reference/unrar/blake2s_sse.cpp create mode 100644 reference/unrar/blake2sp.cpp create mode 100644 reference/unrar/cmddata.cpp create mode 100644 reference/unrar/cmddata.hpp create mode 100644 reference/unrar/coder.cpp create mode 100644 reference/unrar/coder.hpp create mode 100644 reference/unrar/compress.hpp create mode 100644 reference/unrar/consio.cpp create mode 100644 reference/unrar/consio.hpp create mode 100644 reference/unrar/crc.cpp create mode 100644 reference/unrar/crc.hpp create mode 100644 reference/unrar/crypt.cpp create mode 100644 reference/unrar/crypt.hpp create mode 100644 reference/unrar/crypt1.cpp create mode 100644 reference/unrar/crypt2.cpp create mode 100644 reference/unrar/crypt3.cpp create mode 100644 reference/unrar/crypt5.cpp create mode 100644 reference/unrar/dll.cpp create mode 100644 reference/unrar/dll.def create mode 100644 reference/unrar/dll.hpp create mode 100644 reference/unrar/dll.rc create mode 100644 reference/unrar/encname.cpp create mode 100644 reference/unrar/encname.hpp create mode 100644 reference/unrar/errhnd.cpp create mode 100644 reference/unrar/errhnd.hpp create mode 100644 reference/unrar/extinfo.cpp create mode 100644 reference/unrar/extinfo.hpp create mode 100644 reference/unrar/extract.cpp create mode 100644 reference/unrar/extract.hpp create mode 100644 reference/unrar/filcreat.cpp create mode 100644 reference/unrar/filcreat.hpp create mode 100644 reference/unrar/file.cpp create mode 100644 reference/unrar/file.hpp create mode 100644 reference/unrar/filefn.cpp create mode 100644 reference/unrar/filefn.hpp create mode 100644 reference/unrar/filestr.cpp create mode 100644 reference/unrar/filestr.hpp create mode 100644 reference/unrar/find.cpp create mode 100644 reference/unrar/find.hpp create mode 100644 reference/unrar/getbits.cpp create mode 100644 reference/unrar/getbits.hpp create mode 100644 reference/unrar/global.cpp create mode 100644 reference/unrar/global.hpp create mode 100644 reference/unrar/hardlinks.cpp create mode 100644 reference/unrar/hash.cpp create mode 100644 reference/unrar/hash.hpp create mode 100644 reference/unrar/headers.cpp create mode 100644 reference/unrar/headers.hpp create mode 100644 reference/unrar/headers5.hpp create mode 100644 reference/unrar/isnt.cpp create mode 100644 reference/unrar/isnt.hpp create mode 100644 reference/unrar/license.txt create mode 100644 reference/unrar/list.cpp create mode 100644 reference/unrar/list.hpp create mode 100644 reference/unrar/loclang.hpp create mode 100644 reference/unrar/log.cpp create mode 100644 reference/unrar/log.hpp create mode 100644 reference/unrar/makefile create mode 100644 reference/unrar/match.cpp create mode 100644 reference/unrar/match.hpp create mode 100644 reference/unrar/model.cpp create mode 100644 reference/unrar/model.hpp create mode 100644 reference/unrar/options.cpp create mode 100644 reference/unrar/options.hpp create mode 100644 reference/unrar/os.hpp create mode 100644 reference/unrar/pathfn.cpp create mode 100644 reference/unrar/pathfn.hpp create mode 100644 reference/unrar/qopen.cpp create mode 100644 reference/unrar/qopen.hpp create mode 100644 reference/unrar/rar.cpp create mode 100644 reference/unrar/rar.hpp create mode 100644 reference/unrar/rardefs.hpp create mode 100644 reference/unrar/rarlang.hpp create mode 100644 reference/unrar/raros.hpp create mode 100644 reference/unrar/rarpch.cpp create mode 100644 reference/unrar/rartypes.hpp create mode 100644 reference/unrar/rarvm.cpp create mode 100644 reference/unrar/rarvm.hpp create mode 100644 reference/unrar/rawint.hpp create mode 100644 reference/unrar/rawread.cpp create mode 100644 reference/unrar/rawread.hpp create mode 100644 reference/unrar/rdwrfn.cpp create mode 100644 reference/unrar/rdwrfn.hpp create mode 100644 reference/unrar/readme.txt create mode 100644 reference/unrar/recvol.cpp create mode 100644 reference/unrar/recvol.hpp create mode 100644 reference/unrar/recvol3.cpp create mode 100644 reference/unrar/recvol5.cpp create mode 100644 reference/unrar/resource.cpp create mode 100644 reference/unrar/resource.hpp create mode 100644 reference/unrar/rijndael.cpp create mode 100644 reference/unrar/rijndael.hpp create mode 100644 reference/unrar/rs.cpp create mode 100644 reference/unrar/rs.hpp create mode 100644 reference/unrar/rs16.cpp create mode 100644 reference/unrar/rs16.hpp create mode 100644 reference/unrar/savepos.hpp create mode 100644 reference/unrar/scantree.cpp create mode 100644 reference/unrar/scantree.hpp create mode 100644 reference/unrar/secpassword.cpp create mode 100644 reference/unrar/secpassword.hpp create mode 100644 reference/unrar/sha1.cpp create mode 100644 reference/unrar/sha1.hpp create mode 100644 reference/unrar/sha256.cpp create mode 100644 reference/unrar/sha256.hpp create mode 100644 reference/unrar/smallfn.cpp create mode 100644 reference/unrar/smallfn.hpp create mode 100644 reference/unrar/strfn.cpp create mode 100644 reference/unrar/strfn.hpp create mode 100644 reference/unrar/strlist.cpp create mode 100644 reference/unrar/strlist.hpp create mode 100644 reference/unrar/suballoc.cpp create mode 100644 reference/unrar/suballoc.hpp create mode 100644 reference/unrar/system.cpp create mode 100644 reference/unrar/system.hpp create mode 100644 reference/unrar/threadmisc.cpp create mode 100644 reference/unrar/threadpool.cpp create mode 100644 reference/unrar/threadpool.hpp create mode 100644 reference/unrar/timefn.cpp create mode 100644 reference/unrar/timefn.hpp create mode 100644 reference/unrar/ui.cpp create mode 100644 reference/unrar/ui.hpp create mode 100644 reference/unrar/uicommon.cpp create mode 100644 reference/unrar/uiconsole.cpp create mode 100644 reference/unrar/uisilent.cpp create mode 100644 reference/unrar/ulinks.cpp create mode 100644 reference/unrar/unicode.cpp create mode 100644 reference/unrar/unicode.hpp create mode 100644 reference/unrar/unpack.cpp create mode 100644 reference/unrar/unpack.hpp create mode 100644 reference/unrar/unpack15.cpp create mode 100644 reference/unrar/unpack20.cpp create mode 100644 reference/unrar/unpack30.cpp create mode 100644 reference/unrar/unpack50.cpp create mode 100644 reference/unrar/unpack50frag.cpp create mode 100644 reference/unrar/unpack50mt.cpp create mode 100644 reference/unrar/unpackinline.cpp create mode 100644 reference/unrar/uowners.cpp create mode 100644 reference/unrar/version.hpp create mode 100644 reference/unrar/volume.cpp create mode 100644 reference/unrar/volume.hpp create mode 100644 reference/unrar/win32acl.cpp create mode 100644 reference/unrar/win32lnk.cpp create mode 100644 reference/unrar/win32stm.cpp diff --git a/reference/unrar/UnRAR.vcxproj b/reference/unrar/UnRAR.vcxproj new file mode 100644 index 00000000..b55873e3 --- /dev/null +++ b/reference/unrar/UnRAR.vcxproj @@ -0,0 +1,279 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {95CC809B-03FC-4EDB-BB20-FD07A698C05F} + UnRAR + Win32Proj + 8.1 + + + + Application + v140_xp + MultiByte + true + + + Application + v140_xp + MultiByte + + + Application + v140_xp + MultiByte + false + + + Application + v140_xp + MultiByte + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + build\unrar32\$(Configuration)\ + build\unrar32\$(Configuration)\obj\ + true + false + + + build\unrar64\$(Configuration)\ + build\unrar64\$(Configuration)\obj\ + true + false + + + build\unrar32\$(Configuration)\ + build\unrar32\$(Configuration)\obj\ + false + false + + + build\unrar64\$(Configuration)\ + build\unrar64\$(Configuration)\obj\ + false + false + + + + /MP %(AdditionalOptions) + Disabled + UNRAR;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + false + Use + rar.hpp + Level3 + ProgramDatabase + StdCall + 4007;4996;%(DisableSpecificWarnings) + NoExtensions + + + true + Console + MachineX86 + + + + + X64 + + + /MP %(AdditionalOptions) + Disabled + UNRAR;%(PreprocessorDefinitions) + false + EnableFastChecks + MultiThreadedDebug + false + Use + rar.hpp + Level3 + ProgramDatabase + StdCall + 4007;4996;%(DisableSpecificWarnings) + NotSet + + + true + Console + MachineX64 + + + + + /MP %(AdditionalOptions) + MaxSpeed + true + Neither + true + false + UNRAR;%(PreprocessorDefinitions) + false + MultiThreaded + Default + true + true + NoExtensions + Precise + false + Use + rar.hpp + Level3 + ProgramDatabase + StdCall + 4007;4996;%(DisableSpecificWarnings) + + + true + Console + true + true + + MachineX86 + + + + + X64 + + + /MP %(AdditionalOptions) + MinSpace + true + Neither + true + false + UNRAR;%(PreprocessorDefinitions) + false + false + MultiThreaded + true + true + false + Use + rar.hpp + Level3 + ProgramDatabase + StdCall + 4007;4996;%(DisableSpecificWarnings) + NotSet + + + true + Console + true + true + + MachineX64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/reference/unrar/UnRARDll.vcxproj b/reference/unrar/UnRARDll.vcxproj new file mode 100644 index 00000000..ecac0db0 --- /dev/null +++ b/reference/unrar/UnRARDll.vcxproj @@ -0,0 +1,420 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + release_nocrypt + Win32 + + + release_nocrypt + x64 + + + Release + Win32 + + + Release + x64 + + + + UnRAR + {E815C46C-36C4-499F-BBC2-E772C6B17971} + UnRAR + Win32Proj + 8.1 + + + + DynamicLibrary + v140_xp + MultiByte + true + + + DynamicLibrary + v140_xp + MultiByte + true + + + DynamicLibrary + v140_xp + MultiByte + + + DynamicLibrary + v140_xp + MultiByte + false + + + DynamicLibrary + v140_xp + MultiByte + false + + + DynamicLibrary + v140_xp + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.24720.0 + + + build\unrardll32\$(Configuration)\ + build\unrardll32\$(Configuration)\obj\ + true + true + + + build\unrardll64\$(Configuration)\ + build\unrardll64\$(Configuration)\obj\ + true + true + + + build\unrardll32\$(Configuration)\ + build\unrardll32\$(Configuration)\obj\ + false + true + + + build\unrardll64\$(Configuration)\ + build\unrardll64\$(Configuration)\obj\ + false + true + + + build\unrardll32\$(Configuration)\ + build\unrardll32\$(Configuration)\obj\ + false + true + + + build\unrardll64\$(Configuration)\ + build\unrardll64\$(Configuration)\obj\ + false + true + + + + /MP %(AdditionalOptions) + Disabled + RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) + false + Sync + EnableFastChecks + MultiThreadedDebug + 4Bytes + false + Use + rar.hpp + Level3 + ProgramDatabase + Cdecl + 4007;4996;%(DisableSpecificWarnings) + NoExtensions + + + $(OutDir)unrar.dll + dll.def + true + Console + MachineX86 + + + + + X64 + + + /MP %(AdditionalOptions) + Disabled + RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) + false + Sync + EnableFastChecks + MultiThreadedDebug + 4Bytes + false + Use + rar.hpp + Level3 + ProgramDatabase + Cdecl + 4007;4996;%(DisableSpecificWarnings) + NotSet + + + $(OutDir)unrar.dll + dll.def + true + Console + MachineX64 + + + + + /MP %(AdditionalOptions) + MaxSpeed + true + Neither + true + false + RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) + false + Sync + MultiThreaded + 4Bytes + true + true + NoExtensions + Precise + false + Use + rar.hpp + Level3 + ProgramDatabase + Cdecl + 4007;4996;%(DisableSpecificWarnings) + + + /SAFESEH %(AdditionalOptions) + $(OutDir)unrar.dll + dll.def + true + Console + true + true + + MachineX86 + + + + + X64 + + + /MP %(AdditionalOptions) + MaxSpeed + true + Neither + true + false + RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions) + false + false + Sync + MultiThreaded + 4Bytes + true + true + false + Use + rar.hpp + Level3 + ProgramDatabase + Cdecl + 4007;4996;%(DisableSpecificWarnings) + NotSet + + + $(OutDir)unrar.dll + dll.def + true + Console + true + true + + MachineX64 + + + + + /MP %(AdditionalOptions) + MaxSpeed + true + Neither + true + false + RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions) + false + Sync + MultiThreaded + 4Bytes + true + true + NoExtensions + Precise + false + Use + rar.hpp + Level3 + ProgramDatabase + Cdecl + 4007;4996;%(DisableSpecificWarnings) + + + /SAFESEH %(AdditionalOptions) + $(OutDir)unrar.dll + dll_nocrypt.def + true + Console + true + true + + MachineX86 + + + + + X64 + + + /MP %(AdditionalOptions) + MaxSpeed + true + Neither + true + false + RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions) + false + false + Sync + MultiThreaded + 4Bytes + true + true + false + Use + rar.hpp + Level3 + ProgramDatabase + StdCall + 4007;4996;%(DisableSpecificWarnings) + NotSet + + + $(OutDir)unrar.dll + dll_nocrypt.def + true + Console + true + true + + MachineX64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/reference/unrar/acknow.txt b/reference/unrar/acknow.txt new file mode 100644 index 00000000..b982ecee --- /dev/null +++ b/reference/unrar/acknow.txt @@ -0,0 +1,92 @@ + ACKNOWLEDGMENTS + +* We used "Screaming Fast Galois Field Arithmetic Using Intel + SIMD Instructions" paper by James S. Plank, Kevin M. Greenan + and Ethan L. Miller to improve Reed-Solomon coding performance. + Also we are grateful to Artem Drobanov and Bulat Ziganshin + for samples and ideas allowed to make Reed-Solomon coding + more efficient. + +* RAR text compression algorithm is based on Dmitry Shkarin PPMII + and Dmitry Subbotin carryless rangecoder public domain source code. + You may find it in ftp.elf.stuba.sk/pub/pc/pack. + +* RAR encryption includes parts of code from Szymon Stefanek + and Brian Gladman AES implementations also as Steve Reid SHA-1 source. + + --------------------------------------------------------------------------- + Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK. + All rights reserved. + + LICENSE TERMS + + The free distribution and use of this software in both source and binary + form is allowed (with or without changes) provided that: + + 1. distributions of this source code include the above copyright + notice, this list of conditions and the following disclaimer; + + 2. distributions in binary form include the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other associated materials; + + 3. the copyright holder's name is not used to endorse products + built using this software without specific written permission. + + ALTERNATIVELY, provided that this notice is retained in full, this product + may be distributed under the terms of the GNU General Public License (GPL), + in which case the provisions of the GPL apply INSTEAD OF those given above. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + + Source code of this package also as other cryptographic technology + and computing project related links are available on Brian Gladman's + web site: http://www.gladman.me.uk + +* RAR uses CRC32 function based on Intel Slicing-by-8 algorithm. + Original Intel Slicing-by-8 code is available here: + + http://sourceforge.net/projects/slicing-by-8/ + + Original Intel Slicing-by-8 code is licensed under BSD License + available at http://www.opensource.org/licenses/bsd-license.html + + Copyright (c) 2004-2006 Intel Corporation. + All Rights Reserved + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with + the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +* RAR archives may optionally include BLAKE2sp hash ( https://blake2.net ), + designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn + and Christian Winnerlein. + +* Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed + to significantly improve RAR compression and speed. diff --git a/reference/unrar/arccmt.cpp b/reference/unrar/arccmt.cpp new file mode 100644 index 00000000..79963b27 --- /dev/null +++ b/reference/unrar/arccmt.cpp @@ -0,0 +1,166 @@ +static bool IsAnsiEscComment(const wchar *Data,size_t Size); + +bool Archive::GetComment(Array *CmtData) +{ + if (!MainComment) + return false; + SaveFilePos SavePos(*this); + +#ifndef SFX_MODULE + uint CmtLength; + if (Format==RARFMT14) + { + Seek(SFXSize+SIZEOF_MAINHEAD14,SEEK_SET); + CmtLength=GetByte(); + CmtLength+=(GetByte()<<8); + } + else +#endif + { + if (MainHead.CommentInHeader) + { + // Old style (RAR 2.9) archive comment embedded into the main + // archive header. + Seek(SFXSize+SIZEOF_MARKHEAD3+SIZEOF_MAINHEAD3,SEEK_SET); + ReadHeader(); + } + else + { + // Current (RAR 3.0+) version of archive comment. + Seek(GetStartPos(),SEEK_SET); + return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData); + } +#ifndef SFX_MODULE + // Old style (RAR 2.9) comment header embedded into the main + // archive header. + if (BrokenHeader) + { + uiMsg(UIERROR_CMTBROKEN,FileName); + return false; + } + CmtLength=CommHead.HeadSize-SIZEOF_COMMHEAD; +#endif + } +#ifndef SFX_MODULE + if (Format==RARFMT14 && MainHead.PackComment || Format!=RARFMT14 && CommHead.Method!=0x30) + { + if (Format!=RARFMT14 && (CommHead.UnpVer < 15 || CommHead.UnpVer > VER_UNPACK || CommHead.Method > 0x35)) + return false; + ComprDataIO DataIO; + DataIO.SetTestMode(true); + uint UnpCmtLength; + if (Format==RARFMT14) + { +#ifdef RAR_NOCRYPT + return false; +#else + UnpCmtLength=GetByte(); + UnpCmtLength+=(GetByte()<<8); + CmtLength-=2; + DataIO.SetCmt13Encryption(); + CommHead.UnpVer=15; +#endif + } + else + UnpCmtLength=CommHead.UnpSize; + DataIO.SetFiles(this,NULL); + DataIO.EnableShowProgress(false); + DataIO.SetPackedSizeToRead(CmtLength); + DataIO.UnpHash.Init(HASH_CRC32,1); + + Unpack CmtUnpack(&DataIO); + CmtUnpack.Init(0x10000,false); + CmtUnpack.SetDestSize(UnpCmtLength); + CmtUnpack.DoUnpack(CommHead.UnpVer,false); + + if (Format!=RARFMT14 && (DataIO.UnpHash.GetCRC32()&0xffff)!=CommHead.CommCRC) + { + uiMsg(UIERROR_CMTBROKEN,FileName); + return false; + } + else + { + byte *UnpData; + size_t UnpDataSize; + DataIO.GetUnpackedData(&UnpData,&UnpDataSize); +#ifdef _WIN_ALL + // If we ever decide to extend it to Android, we'll need to alloc + // 4x memory for OEM to UTF-8 output here. + OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize); +#endif + CmtData->Alloc(UnpDataSize+1); + memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar)); + CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size()); + CmtData->Alloc(wcslen(CmtData->Addr(0))); + } + } + else + { + if (CmtLength==0) + return false; + Array CmtRaw(CmtLength); + Read(&CmtRaw[0],CmtLength); + + if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff)) + { + uiMsg(UIERROR_CMTBROKEN,FileName); + return false; + } + CmtData->Alloc(CmtLength+1); + CmtRaw.Push(0); +#ifdef _WIN_ALL + // If we ever decide to extend it to Android, we'll need to alloc + // 4x memory for OEM to UTF-8 output here. + OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]); +#endif + CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtLength); + CmtData->Alloc(wcslen(CmtData->Addr(0))); + } +#endif + return CmtData->Size() > 0; +} + + +bool Archive::ReadCommentData(Array *CmtData) +{ + Array CmtRaw; + if (!ReadSubData(&CmtRaw,NULL)) + return false; + size_t CmtSize=CmtRaw.Size(); + CmtRaw.Push(0); + CmtData->Alloc(CmtSize+1); + if (Format==RARFMT50) + UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); + else + if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0) + { + RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2); + (*CmtData)[CmtSize/2]=0; + + } + else + { + CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size()); + } + CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length. + return true; +} + + +void Archive::ViewComment() +{ + if (Cmd->DisableComment) + return; + Array CmtBuf; + if (GetComment(&CmtBuf)) // In GUI too, so "Test" command detects broken comments. + { + size_t CmtSize=CmtBuf.Size(); + wchar *ChPtr=wcschr(&CmtBuf[0],0x1A); + if (ChPtr!=NULL) + CmtSize=ChPtr-&CmtBuf[0]; + mprintf(L"\n"); + OutComment(&CmtBuf[0],CmtSize); + } +} + + diff --git a/reference/unrar/archive.cpp b/reference/unrar/archive.cpp new file mode 100644 index 00000000..e66bddb9 --- /dev/null +++ b/reference/unrar/archive.cpp @@ -0,0 +1,332 @@ +#include "rar.hpp" + +#include "arccmt.cpp" + + +Archive::Archive(RAROptions *InitCmd) +{ + Cmd=NULL; // Just in case we'll have an exception in 'new' below. + + DummyCmd=(InitCmd==NULL); + Cmd=DummyCmd ? (new RAROptions):InitCmd; + + OpenShared=Cmd->OpenShared; + Format=RARFMT15; + Solid=false; + Volume=false; + MainComment=false; + Locked=false; + Signed=false; + FirstVolume=false; + NewNumbering=false; + SFXSize=0; + LatestTime.Reset(); + Protected=false; + Encrypted=false; + FailedHeaderDecryption=false; + BrokenHeader=false; + LastReadBlock=0; + + CurBlockPos=0; + NextBlockPos=0; + + RecoverySize=-1; + RecoveryPercent=-1; + + memset(&MainHead,0,sizeof(MainHead)); + memset(&CryptHead,0,sizeof(CryptHead)); + memset(&EndArcHead,0,sizeof(EndArcHead)); + + VolNumber=0; + VolWrite=0; + AddingFilesSize=0; + AddingHeadersSize=0; + *FirstVolumeName=0; + + Splitting=false; + NewArchive=false; + + SilentOpen=false; + +} + + +Archive::~Archive() +{ + if (DummyCmd) + delete Cmd; +} + + +void Archive::CheckArc(bool EnableBroken) +{ + if (!IsArchive(EnableBroken)) + { + // If FailedHeaderDecryption is set, we already reported that archive + // password is incorrect. + if (!FailedHeaderDecryption) + uiMsg(UIERROR_BADARCHIVE,FileName); + ErrHandler.Exit(RARX_FATAL); + } +} + + +#if !defined(SFX_MODULE) +void Archive::CheckOpen(const wchar *Name) +{ + TOpen(Name); + CheckArc(false); +} +#endif + + +bool Archive::WCheckOpen(const wchar *Name) +{ + if (!WOpen(Name)) + return false; + if (!IsArchive(false)) + { + uiMsg(UIERROR_BADARCHIVE,FileName); + Close(); + return false; + } + return true; +} + + +RARFORMAT Archive::IsSignature(const byte *D,size_t Size) +{ + RARFORMAT Type=RARFMT_NONE; + if (Size>=1 && D[0]==0x52) +#ifndef SFX_MODULE + if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e) + Type=RARFMT14; + else +#endif + if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07) + { + // We check the last signature byte, so we can return a sensible + // warning in case we'll want to change the archive format + // sometimes in the future. + if (D[6]==0) + Type=RARFMT15; + else + if (D[6]==1) + Type=RARFMT50; + else + if (D[6]==2) + Type=RARFMT_FUTURE; + } + return Type; +} + + +bool Archive::IsArchive(bool EnableBroken) +{ + Encrypted=false; + BrokenHeader=false; // Might be left from previous volume. + +#ifndef SFX_MODULE + if (IsDevice()) + { + uiMsg(UIERROR_INVALIDNAME,FileName,FileName); + return false; + } +#endif + if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3) + return false; + SFXSize=0; + + RARFORMAT Type; + if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE) + { + Format=Type; + if (Format==RARFMT14) + Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET); + } + else + { + Array Buffer(MAXSFXSIZE); + long CurPos=(long)Tell(); + int ReadSize=Read(&Buffer[0],Buffer.Size()-16); + for (int I=0;I0 && CurPos<28 && ReadSize>31) + { + char *D=&Buffer[28-CurPos]; + if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58) + continue; + } + SFXSize=CurPos+I; + Seek(SFXSize,SEEK_SET); + if (Format==RARFMT15 || Format==RARFMT50) + Read(MarkHead.Mark,SIZEOF_MARKHEAD3); + break; + } + if (SFXSize==0) + return false; + } + if (Format==RARFMT_FUTURE) + { + uiMsg(UIERROR_NEWRARFORMAT,FileName); + return false; + } + if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer. + { + Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1); + if (MarkHead.Mark[SIZEOF_MARKHEAD3]!=0) + return false; + MarkHead.HeadSize=SIZEOF_MARKHEAD5; + } + else + MarkHead.HeadSize=SIZEOF_MARKHEAD3; + +#ifdef RARDLL + // If callback function is not set, we cannot get the password, + // so we skip the initial header processing for encrypted header archive. + // It leads to skipped archive comment, but the rest of archive data + // is processed correctly. + if (Cmd->Callback==NULL) + SilentOpen=true; +#endif + + // Skip the archive encryption header if any and read the main header. + while (ReadHeader()!=0) + { + HEADER_TYPE Type=GetHeaderType(); + // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to + // avoid the password prompt. + if (Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT) + break; + SeekToNext(); + } + + // This check allows to make RS based recovery even if password is incorrect. + // But we should not do it for EnableBroken or we'll get 'not RAR archive' + // messages when extracting encrypted archives with wrong password. + if (FailedHeaderDecryption && !EnableBroken) + return false; + + SeekToNext(); + if (BrokenHeader) // Main archive header is corrupt. + { + uiMsg(UIERROR_MHEADERBROKEN,FileName); + if (!EnableBroken) + return false; + } + + MainComment=MainHead.CommentInHeader; + + // If we process non-encrypted archive or can request a password, + // we set 'first volume' flag based on file attributes below. + // It is necessary for RAR 2.x archives, which did not have 'first volume' + // flag in main header. Also for all RAR formats we need to scan until + // first file header to set "comment" flag when reading service header. + // Unless we are in silent mode, we need to know about presence of comment + // immediately after IsArchive call. + if (!SilentOpen || !Encrypted) + { + SaveFilePos SavePos(*this); + int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; + HEADER_TYPE SaveCurHeaderType=CurHeaderType; + + while (ReadHeader()!=0) + { + HEADER_TYPE HeaderType=GetHeaderType(); + if (HeaderType==HEAD_SERVICE) + { + // If we have a split service headers, it surely indicates non-first + // volume. But not split service header does not guarantee the first + // volume, because we can have split file after non-split archive + // comment. So we do not quit from loop here. + FirstVolume=Volume && !SubHead.SplitBefore; + } + else + if (HeaderType==HEAD_FILE) + { + FirstVolume=Volume && !FileHead.SplitBefore; + break; + } + else + if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header. + break; + SeekToNext(); + } + CurBlockPos=SaveCurBlockPos; + NextBlockPos=SaveNextBlockPos; + CurHeaderType=SaveCurHeaderType; + } + if (!Volume || FirstVolume) + wcscpy(FirstVolumeName,FileName); + + return true; +} + + + + +void Archive::SeekToNext() +{ + Seek(NextBlockPos,SEEK_SET); +} + + + + + + +// Calculate the block size including encryption fields and padding if any. +uint Archive::FullHeaderSize(size_t Size) +{ + if (Encrypted) + { + Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size. + if (Format == RARFMT50) + Size += SIZE_INITV; + else + Size += SIZE_SALT30; + } + return uint(Size); +} + + + + +#ifdef USE_QOPEN +bool Archive::Open(const wchar *Name,uint Mode) +{ + // Important if we reuse Archive object and it has virtual QOpen + // file position not matching real. For example, for 'l -v volname'. + QOpen.Unload(); + + return File::Open(Name,Mode); +} + + +int Archive::Read(void *Data,size_t Size) +{ + size_t Result; + if (QOpen.Read(Data,Size,Result)) + return (int)Result; + return File::Read(Data,Size); +} + + +void Archive::Seek(int64 Offset,int Method) +{ + if (!QOpen.Seek(Offset,Method)) + File::Seek(Offset,Method); +} + + +int64 Archive::Tell() +{ + int64 QPos; + if (QOpen.Tell(&QPos)) + return QPos; + return File::Tell(); +} +#endif + diff --git a/reference/unrar/archive.hpp b/reference/unrar/archive.hpp new file mode 100644 index 00000000..6e85b9ff --- /dev/null +++ b/reference/unrar/archive.hpp @@ -0,0 +1,151 @@ +#ifndef _RAR_ARCHIVE_ +#define _RAR_ARCHIVE_ + +class PPack; +class RawRead; +class RawWrite; + +enum NOMODIFY_FLAGS +{ + NMDF_ALLOWLOCK=1,NMDF_ALLOWANYVOLUME=2,NMDF_ALLOWFIRSTVOLUME=4 +}; + +enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE}; + +enum ADDSUBDATA_FLAGS +{ + ASDF_SPLIT = 1, // Allow to split archive just before header if necessary. + ASDF_COMPRESS = 2, // Allow to compress data following subheader. + ASDF_CRYPT = 4, // Encrypt data after subheader if password is set. + ASDF_CRYPTIFHEADERS = 8 // Encrypt data after subheader only in -hp mode. +}; + +class Archive:public File +{ + private: + void UpdateLatestTime(FileHeader *CurBlock); + void ConvertNameCase(wchar *Name); + void ConvertFileHeader(FileHeader *hd); + void WriteBlock50(HEADER_TYPE HeaderType,BaseBlock *wb,bool OnlySetSize,bool NonFinalWrite); + size_t ReadHeader14(); + size_t ReadHeader15(); + size_t ReadHeader50(); + void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb); + void RequestArcPassword(); + void UnexpEndArcMsg(); + void BrokenHeaderMsg(); + void UnkEncVerMsg(const wchar *Name); + void UnkEncVerMsg(); + bool ReadCommentData(Array *CmtData); + +#if !defined(RAR_NOCRYPT) + CryptData HeadersCrypt; +#endif + ComprDataIO SubDataIO; + bool DummyCmd; + RAROptions *Cmd; + + int64 RecoverySize; + int RecoveryPercent; + + RarTime LatestTime; + int LastReadBlock; + HEADER_TYPE CurHeaderType; + + bool SilentOpen; +#ifdef USE_QOPEN + QuickOpen QOpen; +#endif + public: + Archive(RAROptions *InitCmd=NULL); + ~Archive(); + static RARFORMAT IsSignature(const byte *D,size_t Size); + bool IsArchive(bool EnableBroken); + size_t SearchBlock(HEADER_TYPE HeaderType); + size_t SearchSubBlock(const wchar *Type); + size_t SearchRR(); + void WriteBlock(HEADER_TYPE HeaderType,BaseBlock *wb=NULL,bool OnlySetSize=false,bool NonFinalWrite=false); + void SetBlockSize(HEADER_TYPE HeaderType,BaseBlock *wb=NULL) {WriteBlock(HeaderType,wb,true);} + size_t ReadHeader(); + void CheckArc(bool EnableBroken); + void CheckOpen(const wchar *Name); + bool WCheckOpen(const wchar *Name); + bool GetComment(Array *CmtData); + void ViewComment(); + void SetLatestTime(RarTime *NewTime); + void SeekToNext(); + bool CheckAccess(); + bool IsArcDir(); + void ConvertAttributes(); + void VolSubtractHeaderSize(size_t SubSize); + uint FullHeaderSize(size_t Size); + int64 GetStartPos(); + void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile, + const wchar *Name,uint Flags); + bool ReadSubData(Array *UnpData,File *DestFile); + HEADER_TYPE GetHeaderType() {return CurHeaderType;}; + RAROptions* GetRAROptions() {return Cmd;} + void SetSilentOpen(bool Mode) {SilentOpen=Mode;} +#if 0 + void GetRecoveryInfo(bool Required,int64 *Size,int *Percent); +#endif +#ifdef USE_QOPEN + bool Open(const wchar *Name,uint Mode=FMF_READ); + int Read(void *Data,size_t Size); + void Seek(int64 Offset,int Method); + int64 Tell(); + void QOpenUnload() {QOpen.Unload();} +#endif + + BaseBlock ShortBlock; + MarkHeader MarkHead; + MainHeader MainHead; + CryptHeader CryptHead; + FileHeader FileHead; + EndArcHeader EndArcHead; + SubBlockHeader SubBlockHead; + FileHeader SubHead; + CommentHeader CommHead; + ProtectHeader ProtectHead; + AVHeader AVHead; + SignHeader SignHead; + UnixOwnersHeader UOHead; + MacFInfoHeader MACHead; + EAHeader EAHead; + StreamHeader StreamHead; + + int64 CurBlockPos; + int64 NextBlockPos; + + RARFORMAT Format; + bool Solid; + bool Volume; + bool MainComment; + bool Locked; + bool Signed; + bool FirstVolume; + bool NewNumbering; + bool Protected; + bool Encrypted; + size_t SFXSize; + bool BrokenHeader; + bool FailedHeaderDecryption; + +#if !defined(RAR_NOCRYPT) + byte ArcSalt[SIZE_SALT50]; +#endif + + bool Splitting; + + uint VolNumber; + int64 VolWrite; + uint64 AddingFilesSize; + uint64 AddingHeadersSize; + + bool NewArchive; + + wchar FirstVolumeName[NM]; +}; + + +#endif diff --git a/reference/unrar/arcread.cpp b/reference/unrar/arcread.cpp new file mode 100644 index 00000000..1120f682 --- /dev/null +++ b/reference/unrar/arcread.cpp @@ -0,0 +1,1437 @@ +#include "rar.hpp" + +size_t Archive::ReadHeader() +{ + // Once we failed to decrypt an encrypted block, there is no reason to + // attempt to do it further. We'll never be successful and only generate + // endless errors. + if (FailedHeaderDecryption) + return 0; + + CurBlockPos=Tell(); + + size_t ReadSize; + switch(Format) + { +#ifndef SFX_MODULE + case RARFMT14: + ReadSize=ReadHeader14(); + break; +#endif + case RARFMT15: + ReadSize=ReadHeader15(); + break; + case RARFMT50: + ReadSize=ReadHeader50(); + break; + } + + if (ReadSize>0 && NextBlockPos<=CurBlockPos) + { + BrokenHeaderMsg(); + return 0; + } + return ReadSize; +} + + +size_t Archive::SearchBlock(HEADER_TYPE HeaderType) +{ + size_t Size,Count=0; + while ((Size=ReadHeader())!=0 && + (HeaderType==HEAD_ENDARC || GetHeaderType()!=HEAD_ENDARC)) + { + if ((++Count & 127)==0) + Wait(); + if (GetHeaderType()==HeaderType) + return Size; + SeekToNext(); + } + return 0; +} + + +size_t Archive::SearchSubBlock(const wchar *Type) +{ + size_t Size,Count=0; + while ((Size=ReadHeader())!=0 && GetHeaderType()!=HEAD_ENDARC) + { + if ((++Count & 127)==0) + Wait(); + if (GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(Type)) + return Size; + SeekToNext(); + } + return 0; +} + + +size_t Archive::SearchRR() +{ + // If locator extra field is available for recovery record, let's utilize it. + if (MainHead.Locator && MainHead.RROffset!=0) + { + uint64 CurPos=Tell(); + Seek(MainHead.RROffset,SEEK_SET); + size_t Size=ReadHeader(); + if (Size!=0 && !BrokenHeader && GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(SUBHEAD_TYPE_RR)) + return Size; + Seek(CurPos,SEEK_SET); + } + // Otherwise scan the entire archive to find the recovery record. + return SearchSubBlock(SUBHEAD_TYPE_RR); +} + + +void Archive::UnexpEndArcMsg() +{ + int64 ArcSize=FileLength(); + + // If block positions are equal to file size, this is not an error. + // It can happen when we reached the end of older RAR 1.5 archive, + // which did not have the end of archive block. + if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize) + { + uiMsg(UIERROR_UNEXPEOF,FileName); + ErrHandler.SetErrorCode(RARX_WARNING); + } +} + + +void Archive::BrokenHeaderMsg() +{ + uiMsg(UIERROR_HEADERBROKEN,FileName); + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_CRC); +} + + +void Archive::UnkEncVerMsg(const wchar *Name) +{ + uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name); + ErrHandler.SetErrorCode(RARX_WARNING); +} + + +size_t Archive::ReadHeader15() +{ + RawRead Raw(this); + + bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3; + + if (Decrypt) + { +#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll. + return 0; +#else + RequestArcPassword(); + + byte Salt[SIZE_SALT30]; + if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30) + { + UnexpEndArcMsg(); + return 0; + } + HeadersCrypt.SetCryptKeys(false,CRYPT_RAR30,&Cmd->Password,Salt,NULL,0,NULL,NULL); + Raw.SetCrypt(&HeadersCrypt); +#endif + } + + Raw.Read(SIZEOF_SHORTBLOCKHEAD); + if (Raw.Size()==0) + { + UnexpEndArcMsg(); + return 0; + } + + ShortBlock.HeadCRC=Raw.Get2(); + + ShortBlock.Reset(); + + uint HeaderType=Raw.Get1(); + ShortBlock.Flags=Raw.Get2(); + ShortBlock.SkipIfUnknown=(ShortBlock.Flags & SKIP_IF_UNKNOWN)!=0; + ShortBlock.HeadSize=Raw.Get2(); + + ShortBlock.HeaderType=(HEADER_TYPE)HeaderType; + if (ShortBlock.HeadSizeReset(); + + *(BaseBlock *)hd=ShortBlock; + + hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0; + hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0; + hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0; + hd->SaltSet=(hd->Flags & LHD_SALT)!=0; + hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0; + hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0; + hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY; + hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5); + hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0; + hd->Version=(hd->Flags & LHD_VERSION)!=0; + + hd->DataSize=Raw.Get4(); + uint LowUnpSize=Raw.Get4(); + hd->HostOS=Raw.Get1(); + + hd->FileHash.Type=HASH_CRC32; + hd->FileHash.CRC32=Raw.Get4(); + + uint FileTime=Raw.Get4(); + hd->UnpVer=Raw.Get1(); + hd->Method=Raw.Get1()-0x30; + size_t NameSize=Raw.Get2(); + hd->FileAttr=Raw.Get4(); + + hd->CryptMethod=CRYPT_NONE; + if (hd->Encrypted) + switch(hd->UnpVer) + { + case 13: hd->CryptMethod=CRYPT_RAR13; break; + case 15: hd->CryptMethod=CRYPT_RAR15; break; + case 20: + case 26: hd->CryptMethod=CRYPT_RAR20; break; + default: hd->CryptMethod=CRYPT_RAR30; break; + } + + hd->HSType=HSYS_UNKNOWN; + if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS) + hd->HSType=HSYS_UNIX; + else + if (hd->HostOSHSType=HSYS_WINDOWS; + + hd->RedirType=FSREDIR_NONE; + + // RAR 4.x Unix symlink. + if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000) + { + hd->RedirType=FSREDIR_UNIXSYMLINK; + *hd->RedirName=0; + } + + hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0; + + hd->LargeFile=(hd->Flags & LHD_LARGE)!=0; + + uint HighPackSize,HighUnpSize; + if (hd->LargeFile) + { + HighPackSize=Raw.Get4(); + HighUnpSize=Raw.Get4(); + hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff); + } + else + { + HighPackSize=HighUnpSize=0; + // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates + // that we do not know the unpacked file size and must unpack it + // until we find the end of file marker in compressed data. + hd->UnknownUnpSize=(LowUnpSize==0xffffffff); + } + hd->PackSize=INT32TO64(HighPackSize,hd->DataSize); + hd->UnpSize=INT32TO64(HighUnpSize,LowUnpSize); + if (hd->UnknownUnpSize) + hd->UnpSize=INT64NDF; + + char FileName[NM*4]; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; + + if (FileBlock) + { + *hd->FileName=0; + if ((hd->Flags & LHD_UNICODE)!=0) + { + EncodeFileName NameCoder; + size_t Length=strlen(FileName); + Length++; + if (ReadNameSize>Length) + NameCoder.Decode(FileName,(byte *)FileName+Length, + ReadNameSize-Length,hd->FileName, + ASIZE(hd->FileName)); + } + + if (*hd->FileName==0) + ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM); + +#ifndef SFX_MODULE + ConvertNameCase(hd->FileName); +#endif + ConvertFileHeader(hd); + } + else + { + CharToWide(FileName,hd->FileName,ASIZE(hd->FileName)); + + // Calculate the size of optional data. + int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3); + if ((hd->Flags & LHD_SALT)!=0) + DataSize-=SIZE_SALT30; + + if (DataSize>0) + { + // Here we read optional additional fields for subheaders. + // They are stored after the file name and before salt. + hd->SubData.Alloc(DataSize); + Raw.GetB(&hd->SubData[0],DataSize); + if (hd->CmpName(SUBHEAD_TYPE_RR)) + { + byte *D=&hd->SubData[8]; + RecoverySize=D[0]+((uint)D[1]<<8)+((uint)D[2]<<16)+((uint)D[3]<<24); + RecoverySize*=512; // Sectors to size. + int64 CurPos=Tell(); + RecoveryPercent=ToPercent(RecoverySize,CurPos); + // Round fractional percent exceeding .5 to upper value. + if (ToPercent(RecoverySize+CurPos/200,CurPos)>RecoveryPercent) + RecoveryPercent++; + } + } + + if (hd->CmpName(SUBHEAD_TYPE_CMT)) + MainComment=true; + } + if ((hd->Flags & LHD_SALT)!=0) + Raw.GetB(hd->Salt,SIZE_SALT30); + hd->mtime.SetDos(FileTime); + if ((hd->Flags & LHD_EXTTIME)!=0) + { + ushort Flags=Raw.Get2(); + RarTime *tbl[4]; + tbl[0]=&FileHead.mtime; + tbl[1]=&FileHead.ctime; + tbl[2]=&FileHead.atime; + tbl[3]=NULL; // Archive time is not used now. + for (int I=0;I<4;I++) + { + RarTime *CurTime=tbl[I]; + uint rmode=Flags>>(3-I)*4; + if ((rmode & 8)==0 || CurTime==NULL) + continue; + if (I!=0) + { + uint DosTime=Raw.Get4(); + CurTime->SetDos(DosTime); + } + RarLocalTime rlt; + CurTime->GetLocal(&rlt); + if (rmode & 4) + rlt.Second++; + rlt.Reminder=0; + int count=rmode&3; + for (int J=0;JSetLocal(&rlt); + } + } + NextBlockPos+=hd->PackSize; + bool CRCProcessedOnly=hd->CommentInHeader; + ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly); + if (hd->HeadCRC!=HeaderCRC) + { + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_WARNING); + + // If we have a broken encrypted header, we do not need to display + // the error message here, because it will be displayed for such + // headers later in this function. Also such headers are unlikely + // to have anything sensible in file name field, so it is useless + // to display the file name. + if (!Decrypt) + uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); + } + } + break; + case HEAD_ENDARC: + *(BaseBlock *)&EndArcHead=ShortBlock; + EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0; + EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0; + EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0; + EndArcHead.StoreVolNumber=(EndArcHead.Flags & EARC_VOLNUMBER)!=0; + if (EndArcHead.DataCRC) + EndArcHead.ArcDataCRC=Raw.Get4(); + if (EndArcHead.StoreVolNumber) + VolNumber=EndArcHead.VolNumber=Raw.Get2(); + break; +#ifndef SFX_MODULE + case HEAD3_CMT: + *(BaseBlock *)&CommHead=ShortBlock; + CommHead.UnpSize=Raw.Get2(); + CommHead.UnpVer=Raw.Get1(); + CommHead.Method=Raw.Get1(); + CommHead.CommCRC=Raw.Get2(); + break; + case HEAD3_SIGN: + *(BaseBlock *)&SignHead=ShortBlock; + SignHead.CreationTime=Raw.Get4(); + SignHead.ArcNameSize=Raw.Get2(); + SignHead.UserNameSize=Raw.Get2(); + break; + case HEAD3_AV: + *(BaseBlock *)&AVHead=ShortBlock; + AVHead.UnpVer=Raw.Get1(); + AVHead.Method=Raw.Get1(); + AVHead.AVVer=Raw.Get1(); + AVHead.AVInfoCRC=Raw.Get4(); + break; + case HEAD3_PROTECT: + *(BaseBlock *)&ProtectHead=ShortBlock; + ProtectHead.DataSize=Raw.Get4(); + ProtectHead.Version=Raw.Get1(); + ProtectHead.RecSectors=Raw.Get2(); + ProtectHead.TotalBlocks=Raw.Get4(); + Raw.GetB(ProtectHead.Mark,8); + NextBlockPos+=ProtectHead.DataSize; + RecoverySize=ProtectHead.RecSectors*512; + break; + case HEAD3_OLDSERVICE: + *(BaseBlock *)&SubBlockHead=ShortBlock; + SubBlockHead.DataSize=Raw.Get4(); + NextBlockPos+=SubBlockHead.DataSize; + SubBlockHead.SubType=Raw.Get2(); + SubBlockHead.Level=Raw.Get1(); + switch(SubBlockHead.SubType) + { + case UO_HEAD: + *(SubBlockHeader *)&UOHead=SubBlockHead; + UOHead.OwnerNameSize=Raw.Get2(); + UOHead.GroupNameSize=Raw.Get2(); + if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName)) + UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1; + if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName)) + UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1; + Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize); + Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize); + UOHead.OwnerName[UOHead.OwnerNameSize]=0; + UOHead.GroupName[UOHead.GroupNameSize]=0; + break; + case MAC_HEAD: + *(SubBlockHeader *)&MACHead=SubBlockHead; + MACHead.fileType=Raw.Get4(); + MACHead.fileCreator=Raw.Get4(); + break; + case EA_HEAD: + case BEEA_HEAD: + case NTACL_HEAD: + *(SubBlockHeader *)&EAHead=SubBlockHead; + EAHead.UnpSize=Raw.Get4(); + EAHead.UnpVer=Raw.Get1(); + EAHead.Method=Raw.Get1(); + EAHead.EACRC=Raw.Get4(); + break; + case STREAM_HEAD: + *(SubBlockHeader *)&StreamHead=SubBlockHead; + StreamHead.UnpSize=Raw.Get4(); + StreamHead.UnpVer=Raw.Get1(); + StreamHead.Method=Raw.Get1(); + StreamHead.StreamCRC=Raw.Get4(); + StreamHead.StreamNameSize=Raw.Get2(); + if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName)) + StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1; + Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize); + StreamHead.StreamName[StreamHead.StreamNameSize]=0; + break; + } + break; +#endif + default: + if (ShortBlock.Flags & LONG_BLOCK) + NextBlockPos+=Raw.Get4(); + break; + } + + ushort HeaderCRC=Raw.GetCRC15(false); + + // Old AV header does not have header CRC properly set. + if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN && + ShortBlock.HeaderType!=HEAD3_AV) + { + bool Recovered=false; + if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace) + { + // Last 7 bytes of recovered volume can contain zeroes, because + // REV files store its own information (volume number, etc.) here. + SaveFilePos SavePos(*this); + int64 Length=Tell(); + Seek(Length-7,SEEK_SET); + Recovered=true; + for (int J=0;J<7;J++) + if (GetByte()!=0) + Recovered=false; + } + if (!Recovered) + { + BrokenHeader=true; + ErrHandler.SetErrorCode(RARX_CRC); + + if (Decrypt) + { + uiMsg(UIERROR_CHECKSUMENC,FileName,FileName); + FailedHeaderDecryption=true; + return 0; + } + } + } + + if (NextBlockPos<=CurBlockPos) + { + BrokenHeaderMsg(); + return 0; + } + + return Raw.Size(); +} + + +size_t Archive::ReadHeader50() +{ + RawRead Raw(this); + + bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5; + + if (Decrypt) + { +#if defined(RAR_NOCRYPT) + return 0; +#else + RequestArcPassword(); + + byte HeadersInitV[SIZE_INITV]; + if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV) + { + UnexpEndArcMsg(); + return 0; + } + + byte PswCheck[SIZE_PSWCHECK]; + HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck); + // Verify password validity. + if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0) + { + uiMsg(UIERROR_BADPSW,FileName); + FailedHeaderDecryption=true; + ErrHandler.SetErrorCode(RARX_BADPWD); + return 0; + } + + Raw.SetCrypt(&HeadersCrypt); +#endif + } + + // Header size must not occupy more than 3 variable length integer bytes + // resulting in 2 MB maximum header size, so here we read 4 byte CRC32 + // followed by 3 bytes or less of header size. + const size_t FirstReadSize=7; // Smallest possible block size. + if (Raw.Read(FirstReadSize)=ShortBlock.HeadSize) + { + BrokenHeaderMsg(); + return 0; + } + } + + uint64 DataSize=0; + if ((ShortBlock.Flags & HFL_DATA)!=0) + DataSize=Raw.GetV(); + + NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize)+DataSize; + + switch(ShortBlock.HeaderType) + { + case HEAD_CRYPT: + { + *(BaseBlock *)&CryptHead=ShortBlock; + uint CryptVersion=(uint)Raw.GetV(); + if (CryptVersion>CRYPT_VERSION) + { + UnkEncVerMsg(FileName); + return 0; + } + uint EncFlags=(uint)Raw.GetV(); + CryptHead.UsePswCheck=(EncFlags & CHFL_CRYPT_PSWCHECK)!=0; + CryptHead.Lg2Count=Raw.Get1(); + if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) + { + UnkEncVerMsg(FileName); + return 0; + } + Raw.GetB(CryptHead.Salt,SIZE_SALT50); + if (CryptHead.UsePswCheck) + { + Raw.GetB(CryptHead.PswCheck,SIZE_PSWCHECK); + + byte csum[SIZE_PSWCHECK_CSUM]; + Raw.GetB(csum,SIZE_PSWCHECK_CSUM); + + sha256_context ctx; + sha256_init(&ctx); + sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK); + + byte Digest[SHA256_DIGEST_SIZE]; + sha256_done(&ctx, Digest); + + CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; + } + Encrypted=true; + } + break; + case HEAD_MAIN: + { + MainHead.Reset(); + *(BaseBlock *)&MainHead=ShortBlock; + uint ArcFlags=(uint)Raw.GetV(); + + Volume=(ArcFlags & MHFL_VOLUME)!=0; + Solid=(ArcFlags & MHFL_SOLID)!=0; + Locked=(ArcFlags & MHFL_LOCK)!=0; + Protected=(ArcFlags & MHFL_PROTECT)!=0; + Signed=false; + NewNumbering=true; + + if ((ArcFlags & MHFL_VOLNUMBER)!=0) + VolNumber=(uint)Raw.GetV(); + else + VolNumber=0; + FirstVolume=Volume && VolNumber==0; + + if (ExtraSize!=0) + ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead); + +#ifdef USE_QOPEN + if (MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE) + { + // We seek to QO block in the end of archive when processing + // QOpen.Load, so we need to preserve current block positions + // to not break normal archive processing by calling function. + int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos; + HEADER_TYPE SaveCurHeaderType=CurHeaderType; + + QOpen.Init(this,false); + QOpen.Load(MainHead.QOpenOffset); + + CurBlockPos=SaveCurBlockPos; + NextBlockPos=SaveNextBlockPos; + CurHeaderType=SaveCurHeaderType; + } +#endif + } + break; + case HEAD_FILE: + case HEAD_SERVICE: + { + FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead; + hd->Reset(); + *(BaseBlock *)hd=ShortBlock; + + bool FileBlock=ShortBlock.HeaderType==HEAD_FILE; + + hd->LargeFile=true; + + hd->PackSize=DataSize; + hd->FileFlags=(uint)Raw.GetV(); + hd->UnpSize=Raw.GetV(); + + hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0; + if (hd->UnknownUnpSize) + hd->UnpSize=INT64NDF; + + hd->MaxSize=Max(hd->PackSize,hd->UnpSize); + hd->FileAttr=(uint)Raw.GetV(); + if ((hd->FileFlags & FHFL_UTIME)!=0) + hd->mtime.SetUnix((time_t)Raw.Get4()); + + hd->FileHash.Type=HASH_NONE; + if ((hd->FileFlags & FHFL_CRC32)!=0) + { + hd->FileHash.Type=HASH_CRC32; + hd->FileHash.CRC32=Raw.Get4(); + } + + hd->RedirType=FSREDIR_NONE; + + uint CompInfo=(uint)Raw.GetV(); + hd->Method=(CompInfo>>7) & 7; + + // "+ 50" to not mix with old RAR format algorithms. For example, + // we may need to use the compression algorithm 15 in the future, + // but it was already used in RAR 1.5 and Unpack needs to distinguish + // them. + hd->UnpVer=(CompInfo & 0x3f) + 50; + + hd->HostOS=(byte)Raw.GetV(); + size_t NameSize=(size_t)Raw.GetV(); + hd->Inherited=(ShortBlock.Flags & HFL_INHERITED)!=0; + + hd->HSType=HSYS_UNKNOWN; + if (hd->HostOS==HOST5_UNIX) + hd->HSType=HSYS_UNIX; + else + if (hd->HostOS==HOST5_WINDOWS) + hd->HSType=HSYS_WINDOWS; + + hd->SplitBefore=(hd->Flags & HFL_SPLITBEFORE)!=0; + hd->SplitAfter=(hd->Flags & HFL_SPLITAFTER)!=0; + hd->SubBlock=(hd->Flags & HFL_CHILD)!=0; + hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0; + hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0; + hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf); + + hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE; + + char FileName[NM*4]; + size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1); + Raw.GetB((byte *)FileName,ReadNameSize); + FileName[ReadNameSize]=0; + + UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName)); + + // Should do it before converting names, because extra fields can + // affect name processing, like in case of NTFS streams. + if (ExtraSize!=0) + ProcessExtra50(&Raw,(size_t)ExtraSize,hd); + + if (FileBlock) + { +#ifndef SFX_MODULE + ConvertNameCase(hd->FileName); +#endif + ConvertFileHeader(hd); + } + + if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT)) + MainComment=true; + +#if 0 + // For RAR5 format we read the user specified recovery percent here. + // It would be useful to do it for shell extension too, so we display + // the correct recovery record size in archive properties. But then + // we would need to include the entire recovery record processing + // code to shell extension, which is not done now. + if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0) + { + RecoveryPercent=hd->SubData[0]; + RSBlockHeader Header; + GetRRInfo(this,&Header); + RecoverySize=Header.RecSectionSize*Header.RecCount; + } +#endif + + if (BadCRC) // Add the file name to broken header message displayed above. + uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName); + } + break; + case HEAD_ENDARC: + { + *(BaseBlock *)&EndArcHead=ShortBlock; + uint ArcFlags=(uint)Raw.GetV(); + EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0; + EndArcHead.StoreVolNumber=false; + EndArcHead.DataCRC=false; + EndArcHead.RevSpace=false; + } + break; + } + + if (NextBlockPos<=CurBlockPos) + { + BrokenHeaderMsg(); + return 0; + } + return Raw.Size(); +} + + +#if !defined(RAR_NOCRYPT) +void Archive::RequestArcPassword() +{ + if (!Cmd->Password.IsSet()) + { +#ifdef RARDLL + if (Cmd->Callback!=NULL) + { + wchar PasswordW[MAXPASSWORD]; + *PasswordW=0; + if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) + *PasswordW=0; + if (*PasswordW==0) + { + char PasswordA[MAXPASSWORD]; + *PasswordA=0; + if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) + *PasswordA=0; + GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + cleandata(PasswordA,sizeof(PasswordA)); + } + Cmd->Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); + } + if (!Cmd->Password.IsSet()) + { + Close(); + Cmd->DllError=ERAR_MISSING_PASSWORD; + ErrHandler.Exit(RARX_USERBREAK); + } +#else + if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password) || + !Cmd->Password.IsSet()) + { + Close(); + uiMsg(UIERROR_INCERRCOUNT); + ErrHandler.Exit(RARX_USERBREAK); + } +#endif + Cmd->ManualPassword=true; + } +} +#endif + + +void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb) +{ + // Read extra data from the end of block skipping any fields before it. + size_t ExtraStart=Raw->Size()-ExtraSize; + if (ExtraStartGetPos()) + return; + Raw->SetPos(ExtraStart); + while (Raw->DataLeft()>=2) + { + int64 FieldSize=Raw->GetV(); + if (FieldSize==0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft()) + break; + size_t NextPos=size_t(Raw->GetPos()+FieldSize); + uint64 FieldType=Raw->GetV(); + + FieldSize=int64(NextPos-Raw->GetPos()); // Field size without size and type fields. + + if (bb->HeaderType==HEAD_MAIN) + { + MainHeader *hd=(MainHeader *)bb; + if (FieldType==MHEXTRA_LOCATOR) + { + hd->Locator=true; + uint Flags=(uint)Raw->GetV(); + if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0) + { + uint64 Offset=Raw->GetV(); + if (Offset!=0) // 0 means that reserved space was not enough to write the offset. + hd->QOpenOffset=Offset+CurBlockPos; + } + if ((Flags & MHEXTRA_LOCATOR_RR)!=0) + { + uint64 Offset=Raw->GetV(); + if (Offset!=0) // 0 means that reserved space was not enough to write the offset. + hd->RROffset=Offset+CurBlockPos; + } + } + } + + if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE) + { + FileHeader *hd=(FileHeader *)bb; + switch(FieldType) + { + case FHEXTRA_CRYPT: + { + FileHeader *hd=(FileHeader *)bb; + uint EncVersion=(uint)Raw->GetV(); + if (EncVersion > CRYPT_VERSION) + UnkEncVerMsg(hd->FileName); + else + { + uint Flags=(uint)Raw->GetV(); + hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0; + hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0; + hd->Lg2Count=Raw->Get1(); + if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX) + UnkEncVerMsg(hd->FileName); + Raw->GetB(hd->Salt,SIZE_SALT50); + Raw->GetB(hd->InitV,SIZE_INITV); + if (hd->UsePswCheck) + { + Raw->GetB(hd->PswCheck,SIZE_PSWCHECK); + + // It is important to know if password check data is valid. + // If it is damaged and header CRC32 fails to detect it, + // archiver would refuse to decompress a possibly valid file. + // Since we want to be sure distinguishing a wrong password + // or corrupt file data, we use 64-bit password check data + // and to control its validity we use 32 bits of password + // check data SHA-256 additionally to 32-bit header CRC32. + byte csum[SIZE_PSWCHECK_CSUM]; + Raw->GetB(csum,SIZE_PSWCHECK_CSUM); + + sha256_context ctx; + sha256_init(&ctx); + sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK); + + byte Digest[SHA256_DIGEST_SIZE]; + sha256_done(&ctx, Digest); + + hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0; + + // RAR 5.21 and earlier set PswCheck field in service records to 0 + // even if UsePswCheck was present. + if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0) + hd->UsePswCheck=0; + } + hd->SaltSet=true; + hd->CryptMethod=CRYPT_RAR50; + hd->Encrypted=true; + } + } + break; + case FHEXTRA_HASH: + { + FileHeader *hd=(FileHeader *)bb; + uint Type=(uint)Raw->GetV(); + if (Type==FHEXTRA_HASH_BLAKE2) + { + hd->FileHash.Type=HASH_BLAKE2; + Raw->GetB(hd->FileHash.Digest,BLAKE2_DIGEST_SIZE); + } + } + break; + case FHEXTRA_HTIME: + if (FieldSize>=5) + { + byte Flags=(byte)Raw->GetV(); + bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0; + if ((Flags & FHEXTRA_HTIME_MTIME)!=0) + if (UnixTime) + hd->mtime.SetUnix(Raw->Get4()); + else + hd->mtime.SetWin(Raw->Get8()); + if ((Flags & FHEXTRA_HTIME_CTIME)!=0) + if (UnixTime) + hd->ctime.SetUnix(Raw->Get4()); + else + hd->ctime.SetWin(Raw->Get8()); + if ((Flags & FHEXTRA_HTIME_ATIME)!=0) + if (UnixTime) + hd->atime.SetUnix((time_t)Raw->Get4()); + else + hd->atime.SetWin(Raw->Get8()); + if (UnixTime && (Flags & FHEXTRA_HTIME_UNIX_NS)!=0) // Add nanoseconds. + { + uint ns; + if ((Flags & FHEXTRA_HTIME_MTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->mtime.Adjust(ns); + if ((Flags & FHEXTRA_HTIME_CTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->ctime.Adjust(ns); + if ((Flags & FHEXTRA_HTIME_ATIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000) + hd->atime.Adjust(ns); + } + } + break; + case FHEXTRA_VERSION: + if (FieldSize>=1) + { + Raw->GetV(); // Skip flags field. + uint Version=(uint)Raw->GetV(); + if (Version!=0) + { + hd->Version=true; + + wchar VerText[20]; + swprintf(VerText,ASIZE(VerText),L";%u",Version); + wcsncatz(FileHead.FileName,VerText,ASIZE(FileHead.FileName)); + } + } + break; + case FHEXTRA_REDIR: + { + hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV(); + uint Flags=(uint)Raw->GetV(); + hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0; + size_t NameSize=(size_t)Raw->GetV(); + + char UtfName[NM*4]; + *UtfName=0; + if (NameSizeGetB(UtfName,NameSize); + UtfName[NameSize]=0; + } +#ifdef _WIN_ALL + UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName)); +#endif + UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName)); + } + break; + case FHEXTRA_UOWNER: + { + uint Flags=(uint)Raw->GetV(); + hd->UnixOwnerNumeric=(Flags & FHEXTRA_UOWNER_NUMUID)!=0; + hd->UnixGroupNumeric=(Flags & FHEXTRA_UOWNER_NUMGID)!=0; + *hd->UnixOwnerName=*hd->UnixGroupName=0; + if ((Flags & FHEXTRA_UOWNER_UNAME)!=0) + { + size_t Length=(size_t)Raw->GetV(); + Length=Min(Length,ASIZE(hd->UnixOwnerName)-1); + Raw->GetB(hd->UnixOwnerName,Length); + hd->UnixOwnerName[Length]=0; + } + if ((Flags & FHEXTRA_UOWNER_GNAME)!=0) + { + size_t Length=(size_t)Raw->GetV(); + Length=Min(Length,ASIZE(hd->UnixGroupName)-1); + Raw->GetB(hd->UnixGroupName,Length); + hd->UnixGroupName[Length]=0; + } +#ifdef _UNIX + if (hd->UnixOwnerNumeric) + hd->UnixOwnerID=(uid_t)Raw->GetV(); + if (hd->UnixGroupNumeric) + hd->UnixGroupID=(gid_t)Raw->GetV(); +#else + // Need these fields in Windows too for 'list' command, + // but uid_t and gid_t are not defined. + if (hd->UnixOwnerNumeric) + hd->UnixOwnerID=(uint)Raw->GetV(); + if (hd->UnixGroupNumeric) + hd->UnixGroupID=(uint)Raw->GetV(); +#endif + hd->UnixOwnerSet=true; + } + break; + case FHEXTRA_SUBDATA: + { + // RAR 5.21 and earlier set FHEXTRA_SUBDATA size to 1 less than + // required. It did not hurt extraction, because UnRAR 5.21 + // and earlier ignored this field and set FieldSize as data left + // in entire extra area. But now we set the correct field size + // and set FieldSize based on actual extra record size, + // so we need to adjust it for those older archives here. + // FHEXTRA_SUBDATA in those archives always belongs to HEAD_SERVICE + // and always is last in extra area. So since its size is by 1 + // less than needed, we always have 1 byte left in extra area, + // which fact we use here to detect such archives. + if (bb->HeaderType==HEAD_SERVICE && Raw->Size()-NextPos==1) + FieldSize++; + + hd->SubData.Alloc((size_t)FieldSize); + Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize); + } + break; + } + } + + Raw->SetPos(NextPos); + } +} + + +#ifndef SFX_MODULE +size_t Archive::ReadHeader14() +{ + RawRead Raw(this); + if (CurBlockPos<=(int64)SFXSize) + { + Raw.Read(SIZEOF_MAINHEAD14); + MainHead.Reset(); + byte Mark[4]; + Raw.GetB(Mark,4); + uint HeadSize=Raw.Get2(); + byte Flags=Raw.Get1(); + NextBlockPos=CurBlockPos+HeadSize; + CurHeaderType=HEAD_MAIN; + + Volume=(Flags & MHD_VOLUME)!=0; + Solid=(Flags & MHD_SOLID)!=0; + Locked=(Flags & MHD_LOCK)!=0; + MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0; + MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0; + } + else + { + Raw.Read(SIZEOF_FILEHEAD14); + FileHead.Reset(); + + FileHead.HeaderType=HEAD_FILE; + FileHead.DataSize=Raw.Get4(); + FileHead.UnpSize=Raw.Get4(); + FileHead.FileHash.Type=HASH_RAR14; + FileHead.FileHash.CRC32=Raw.Get2(); + FileHead.HeadSize=Raw.Get2(); + uint FileTime=Raw.Get4(); + FileHead.FileAttr=Raw.Get1(); + FileHead.Flags=Raw.Get1()|LONG_BLOCK; + FileHead.UnpVer=(Raw.Get1()==2) ? 13 : 10; + size_t NameSize=Raw.Get1(); + FileHead.Method=Raw.Get1(); + + FileHead.SplitBefore=(FileHead.Flags & LHD_SPLIT_BEFORE)!=0; + FileHead.SplitAfter=(FileHead.Flags & LHD_SPLIT_AFTER)!=0; + FileHead.Encrypted=(FileHead.Flags & LHD_PASSWORD)!=0; + FileHead.CryptMethod=FileHead.Encrypted ? CRYPT_RAR13:CRYPT_NONE; + + FileHead.PackSize=FileHead.DataSize; + FileHead.WinSize=0x10000; + + FileHead.mtime.SetDos(FileTime); + + Raw.Read(NameSize); + + char FileName[NM]; + Raw.GetB((byte *)FileName,Min(NameSize,ASIZE(FileName))); + FileName[NameSize]=0; + IntToExt(FileName,FileName,ASIZE(FileName)); + CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName)); + ConvertNameCase(FileHead.FileName); + + if (Raw.Size()!=0) + NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize; + CurHeaderType=HEAD_FILE; + } + return NextBlockPos>CurBlockPos ? Raw.Size() : 0; +} +#endif + + +#ifndef SFX_MODULE +void Archive::ConvertNameCase(wchar *Name) +{ + if (Cmd->ConvertNames==NAMES_UPPERCASE) + wcsupper(Name); + if (Cmd->ConvertNames==NAMES_LOWERCASE) + wcslower(Name); +} +#endif + + +bool Archive::IsArcDir() +{ + return FileHead.Dir; +} + + +void Archive::ConvertAttributes() +{ +#if defined(_WIN_ALL) || defined(_EMX) + if (FileHead.HSType!=HSYS_WINDOWS) + FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20; +#endif +#ifdef _UNIX + // umask defines which permission bits must not be set by default + // when creating a file or directory. The typical default value + // for the process umask is S_IWGRP | S_IWOTH (octal 022), + // resulting in 0644 mode for new files. + // Normally umask is applied automatically when creating a file, + // but we set attributes with chmod later, so we need to calculate + // resulting attributes here. We do it only for non-Unix archives. + // We restore native Unix attributes as is, because it can be backup. + static mode_t mask = (mode_t) -1; + + if (mask == (mode_t) -1) + { + // umask call returns the current umask value. Argument (022) is not + // really important here. + mask = umask(022); + + // Restore the original umask value, which was changed to 022 above. + umask(mask); + } + + switch(FileHead.HSType) + { + case HSYS_WINDOWS: + { + // Mapping MSDOS, OS/2 and Windows file attributes to Unix. + + if (FileHead.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY + { + // For directories we use 0777 mask. + FileHead.FileAttr=0777 & ~mask; + } + else + if (FileHead.FileAttr & 1) // FILE_ATTRIBUTE_READONLY + { + // For read only files we use 0444 mask with 'w' bits turned off. + FileHead.FileAttr=0444 & ~mask; + } + else + { + // umask does not set +x for regular files, so we use 0666 + // instead of 0777 as for directories. + FileHead.FileAttr=0666 & ~mask; + } + } + break; + case HSYS_UNIX: + break; + default: + if (FileHead.Dir) + FileHead.FileAttr=0x41ff & ~mask; + else + FileHead.FileAttr=0x81b6 & ~mask; + break; + } +#endif +} + + +void Archive::ConvertFileHeader(FileHeader *hd) +{ + if (Format==RARFMT15 && hd->UnpVer<20 && (hd->FileAttr & 0x10)) + hd->Dir=true; + if (hd->HSType==HSYS_UNKNOWN) + if (hd->Dir) + hd->FileAttr=0x10; + else + hd->FileAttr=0x20; + +#ifdef _WIN_ALL + if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed. + ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName)); +#endif + + for (wchar *s=hd->FileName;*s!=0;s++) + { +#ifdef _UNIX + // Backslash is the invalid character for Windows file headers, + // but it can present in Unix file names extracted in Unix. + if (*s=='\\' && Format==RARFMT50 && hd->HSType==HSYS_WINDOWS) + *s='_'; +#endif + +#if defined(_WIN_ALL) || defined(_EMX) + // RAR 5.0 archives do not use '\' as path separator, so if we see it, + // it means that it is a part of Unix file name, which we cannot + // extract in Windows. + if (*s=='\\' && Format==RARFMT50) + *s='_'; + + // ':' in file names is allowed in Unix, but not in Windows. + // Even worse, file data will be written to NTFS stream on NTFS, + // so automatic name correction on file create error in extraction + // routine does not work. In Windows and DOS versions we better + // replace ':' now. + if (*s==':') + *s='_'; +#endif + + // This code must be performed only after other path separator checks, + // because it produces backslashes illegal for some of checks above. + // Backslash is allowed in file names in Unix, but not in Windows. + // Still, RAR 4.x uses backslashes as path separator even in Unix. + // Forward slash is not allowed in both systems. In RAR 5.0 we use + // the forward slash as universal path separator. + if (*s=='/' || *s=='\\' && Format!=RARFMT50) + *s=CPATHDIVIDER; + } +} + + +int64 Archive::GetStartPos() +{ + int64 StartPos=SFXSize+MarkHead.HeadSize; + if (Format==RARFMT15) + StartPos+=MainHead.HeadSize; + else // RAR 5.0. + StartPos+=CryptHead.HeadSize+FullHeaderSize(MainHead.HeadSize); + return StartPos; +} + + +bool Archive::ReadSubData(Array *UnpData,File *DestFile) +{ + if (BrokenHeader) + { + uiMsg(UIERROR_SUBHEADERBROKEN,FileName); + ErrHandler.SetErrorCode(RARX_CRC); + return false; + } + if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK)) + { + uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); + return false; + } + + if (SubHead.PackSize==0 && !SubHead.SplitAfter) + return true; + + SubDataIO.Init(); + Unpack Unpack(&SubDataIO); + Unpack.Init(SubHead.WinSize,false); + + if (DestFile==NULL) + { + if (SubHead.UnpSize>0x1000000) + { + // So huge allocation must never happen in valid archives. + uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName); + return false; + } + if (UnpData==NULL) + SubDataIO.SetTestMode(true); + else + { + UnpData->Alloc((size_t)SubHead.UnpSize); + SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize); + } + } + if (SubHead.Encrypted) + if (Cmd->Password.IsSet()) + SubDataIO.SetEncryption(false,SubHead.CryptMethod,&Cmd->Password, + SubHead.SaltSet ? SubHead.Salt:NULL,SubHead.InitV, + SubHead.Lg2Count,SubHead.HashKey,SubHead.PswCheck); + else + return false; + SubDataIO.UnpHash.Init(SubHead.FileHash.Type,1); + SubDataIO.SetPackedSizeToRead(SubHead.PackSize); + SubDataIO.EnableShowProgress(false); + SubDataIO.SetFiles(this,DestFile); + SubDataIO.UnpVolume=SubHead.SplitAfter; + SubDataIO.SetSubHeader(&SubHead,NULL); + Unpack.SetDestSize(SubHead.UnpSize); + if (SubHead.Method==0) + CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize); + else + Unpack.DoUnpack(SubHead.UnpVer,false); + + if (!SubDataIO.UnpHash.Cmp(&SubHead.FileHash,SubHead.UseHashKey ? SubHead.HashKey:NULL)) + { + uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName); + ErrHandler.SetErrorCode(RARX_CRC); + if (UnpData!=NULL) + UnpData->Reset(); + return false; + } + return true; +} diff --git a/reference/unrar/array.hpp b/reference/unrar/array.hpp new file mode 100644 index 00000000..20d258d5 --- /dev/null +++ b/reference/unrar/array.hpp @@ -0,0 +1,191 @@ +#ifndef _RAR_ARRAY_ +#define _RAR_ARRAY_ + +extern ErrorHandler ErrHandler; + +template class Array +{ + private: + T *Buffer; + size_t BufSize; + size_t AllocSize; + size_t MaxSize; + bool Secure; // Clean memory if true. + public: + Array(); + Array(size_t Size); + Array(const Array &Src); // Copy constructor. + ~Array(); + inline void CleanData(); + inline T& operator [](size_t Item) const; + inline T* operator + (size_t Pos); + inline size_t Size(); // Returns the size in items, not in bytes. + void Add(size_t Items); + void Alloc(size_t Items); + void Reset(); + void SoftReset(); + void operator = (Array &Src); + void Push(T Item); + void Append(T *Item,size_t Count); + T* Addr(size_t Item) {return Buffer+Item;} + void SetMaxSize(size_t Size) {MaxSize=Size;} + T* Begin() {return Buffer;} + T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;} + void SetSecure() {Secure=true;} +}; + + +template void Array::CleanData() +{ + Buffer=NULL; + BufSize=0; + AllocSize=0; + MaxSize=0; + Secure=false; +} + + +template Array::Array() +{ + CleanData(); +} + + +template Array::Array(size_t Size) +{ + CleanData(); + Add(Size); +} + + +// Copy constructor in case we need to pass an object as value. +template Array::Array(const Array &Src) +{ + CleanData(); + Alloc(Src.BufSize); + if (Src.BufSize!=0) + memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); +} + + +template Array::~Array() +{ + if (Buffer!=NULL) + { + if (Secure) + cleandata(Buffer,AllocSize*sizeof(T)); + free(Buffer); + } +} + + +template inline T& Array::operator [](size_t Item) const +{ + return Buffer[Item]; +} + + +template inline T* Array::operator +(size_t Pos) +{ + return Buffer+Pos; +} + + +template inline size_t Array::Size() +{ + return BufSize; +} + + +template void Array::Add(size_t Items) +{ + BufSize+=Items; + if (BufSize>AllocSize) + { + if (MaxSize!=0 && BufSize>MaxSize) + { + ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize); + ErrHandler.MemoryError(); + } + + size_t Suggested=AllocSize+AllocSize/4+32; + size_t NewSize=Max(BufSize,Suggested); + + T *NewBuffer; + if (Secure) + { + NewBuffer=(T *)malloc(NewSize*sizeof(T)); + if (NewBuffer==NULL) + ErrHandler.MemoryError(); + if (Buffer!=NULL) + { + memcpy(NewBuffer,Buffer,AllocSize*sizeof(T)); + cleandata(Buffer,AllocSize*sizeof(T)); + free(Buffer); + } + } + else + { + NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T)); + if (NewBuffer==NULL) + ErrHandler.MemoryError(); + } + Buffer=NewBuffer; + AllocSize=NewSize; + } +} + + +template void Array::Alloc(size_t Items) +{ + if (Items>AllocSize) + Add(Items-BufSize); + else + BufSize=Items; +} + + +template void Array::Reset() +{ + if (Buffer!=NULL) + { + free(Buffer); + Buffer=NULL; + } + BufSize=0; + AllocSize=0; +} + + +// Reset buffer size, but preserve already allocated memory if any, +// so we can reuse it without wasting time to allocation. +template void Array::SoftReset() +{ + BufSize=0; +} + + +template void Array::operator =(Array &Src) +{ + Reset(); + Alloc(Src.BufSize); + if (Src.BufSize!=0) + memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T)); +} + + +template void Array::Push(T Item) +{ + Add(1); + (*this)[Size()-1]=Item; +} + + +template void Array::Append(T *Items,size_t Count) +{ + size_t CurSize=Size(); + Add(Count); + memcpy(Buffer+CurSize,Items,Count*sizeof(T)); +} + +#endif diff --git a/reference/unrar/blake2s.cpp b/reference/unrar/blake2s.cpp new file mode 100644 index 00000000..317603da --- /dev/null +++ b/reference/unrar/blake2s.cpp @@ -0,0 +1,183 @@ +// Based on public domain code written in 2012 by Samuel Neves + +#include "rar.hpp" + +#ifdef USE_SSE +#include "blake2s_sse.cpp" +#endif + +static void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth); +static void blake2s_update( blake2s_state *S, const byte *in, size_t inlen ); +static void blake2s_final( blake2s_state *S, byte *digest ); + +#include "blake2sp.cpp" + +static const uint32 blake2s_IV[8] = +{ + 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL, + 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL +}; + +static const byte blake2s_sigma[10][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , + { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , + { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , + { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , + { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , + { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , +}; + +static inline void blake2s_set_lastnode( blake2s_state *S ) +{ + S->f[1] = ~0U; +} + + +/* Some helper functions, not necessarily useful */ +static inline void blake2s_set_lastblock( blake2s_state *S ) +{ + if( S->last_node ) blake2s_set_lastnode( S ); + + S->f[0] = ~0U; +} + + +static inline void blake2s_increment_counter( blake2s_state *S, const uint32 inc ) +{ + S->t[0] += inc; + S->t[1] += ( S->t[0] < inc ); +} + + +/* init2 xors IV with input parameter block */ +void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth) +{ +#ifdef USE_SSE + if (_SSE_Version>=SSE_SSE2) + blake2s_init_sse(); +#endif + + S->init(); // Clean data. + for( int i = 0; i < 8; ++i ) + S->h[i] = blake2s_IV[i]; + + S->h[0] ^= 0x02080020; // We use BLAKE2sp parameters block. + S->h[2] ^= node_offset; + S->h[3] ^= (node_depth<<16)|0x20000000; +} + + +#define G(r,i,m,a,b,c,d) \ + a = a + b + m[blake2s_sigma[r][2*i+0]]; \ + d = rotr32(d ^ a, 16); \ + c = c + d; \ + b = rotr32(b ^ c, 12); \ + a = a + b + m[blake2s_sigma[r][2*i+1]]; \ + d = rotr32(d ^ a, 8); \ + c = c + d; \ + b = rotr32(b ^ c, 7); + + +static void blake2s_compress( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] ) +{ + uint32 m[16]; + uint32 v[16]; + + for( size_t i = 0; i < 16; ++i ) + m[i] = RawGet4( block + i * 4 ); + + for( size_t i = 0; i < 8; ++i ) + v[i] = S->h[i]; + + v[ 8] = blake2s_IV[0]; + v[ 9] = blake2s_IV[1]; + v[10] = blake2s_IV[2]; + v[11] = blake2s_IV[3]; + v[12] = S->t[0] ^ blake2s_IV[4]; + v[13] = S->t[1] ^ blake2s_IV[5]; + v[14] = S->f[0] ^ blake2s_IV[6]; + v[15] = S->f[1] ^ blake2s_IV[7]; + + for ( uint r = 0; r <= 9; ++r ) // No gain on i7 if unrolled, but exe size grows. + { + G(r,0,m,v[ 0],v[ 4],v[ 8],v[12]); + G(r,1,m,v[ 1],v[ 5],v[ 9],v[13]); + G(r,2,m,v[ 2],v[ 6],v[10],v[14]); + G(r,3,m,v[ 3],v[ 7],v[11],v[15]); + G(r,4,m,v[ 0],v[ 5],v[10],v[15]); + G(r,5,m,v[ 1],v[ 6],v[11],v[12]); + G(r,6,m,v[ 2],v[ 7],v[ 8],v[13]); + G(r,7,m,v[ 3],v[ 4],v[ 9],v[14]); + } + + for( size_t i = 0; i < 8; ++i ) + S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; +} + + +void blake2s_update( blake2s_state *S, const byte *in, size_t inlen ) +{ + while( inlen > 0 ) + { + size_t left = S->buflen; + size_t fill = 2 * BLAKE2S_BLOCKBYTES - left; + + if( inlen > fill ) + { + memcpy( S->buf + left, in, fill ); // Fill buffer + S->buflen += fill; + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + +#ifdef USE_SSE +#ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode. + if (_SSE_Version>=SSE_SSE2) +#else + if (_SSE_Version>=SSE_SSSE3) +#endif + blake2s_compress_sse( S, S->buf ); + else + blake2s_compress( S, S->buf ); // Compress +#else + blake2s_compress( S, S->buf ); // Compress +#endif + + memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left + S->buflen -= BLAKE2S_BLOCKBYTES; + in += fill; + inlen -= fill; + } + else // inlen <= fill + { + memcpy( S->buf + left, in, (size_t)inlen ); + S->buflen += (size_t)inlen; // Be lazy, do not compress + in += inlen; + inlen = 0; + } + } +} + + +void blake2s_final( blake2s_state *S, byte *digest ) +{ + if( S->buflen > BLAKE2S_BLOCKBYTES ) + { + blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES ); + blake2s_compress( S, S->buf ); + S->buflen -= BLAKE2S_BLOCKBYTES; + memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen ); + } + + blake2s_increment_counter( S, ( uint32 )S->buflen ); + blake2s_set_lastblock( S ); + memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */ + blake2s_compress( S, S->buf ); + + for( int i = 0; i < 8; ++i ) /* Output full hash */ + RawPut4( S->h[i], digest + 4 * i ); +} + diff --git a/reference/unrar/blake2s.hpp b/reference/unrar/blake2s.hpp new file mode 100644 index 00000000..7dd71571 --- /dev/null +++ b/reference/unrar/blake2s.hpp @@ -0,0 +1,101 @@ +// Based on public domain code written in 2012 by Samuel Neves +#ifndef _RAR_BLAKE2_ +#define _RAR_BLAKE2_ + +#define BLAKE2_DIGEST_SIZE 32 + +enum blake2s_constant +{ + BLAKE2S_BLOCKBYTES = 64, + BLAKE2S_OUTBYTES = 32 +}; + + +// Alignment to 64 improves performance of both SSE and non-SSE versions. +// Alignment to n*16 is required for SSE version, so we selected 64. +// We use the custom alignment scheme instead of __declspec(align(x)), +// because it is less compiler dependent. Also the compiler directive +// does not help if structure is a member of class allocated through +// 'new' operator. +struct blake2s_state +{ + enum { BLAKE_ALIGNMENT = 64 }; + + // buffer and uint32 h[8], t[2], f[2]; + enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES }; + + byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT]; + + byte *buf; // byte buf[2 * BLAKE2S_BLOCKBYTES]. + uint32 *h, *t, *f; // uint32 h[8], t[2], f[2]. + + size_t buflen; + byte last_node; + + blake2s_state() + { + set_pointers(); + } + + // Required when we declare and assign in the same command. + blake2s_state(blake2s_state &st) + { + set_pointers(); + *this=st; + } + + void set_pointers() + { + // Set aligned pointers. Must be done in constructor, not in Init(), + // so assignments like 'blake2sp_state res=blake2ctx' work correctly + // even if blake2sp_init is not called for 'res'. + buf = (byte *) ALIGN_VALUE(ubuf, BLAKE_ALIGNMENT); + h = (uint32 *) (buf + 2 * BLAKE2S_BLOCKBYTES); + t = h + 8; + f = t + 2; + } + + void init() + { + memset( ubuf, 0, sizeof( ubuf ) ); + buflen = 0; + last_node = 0; + } + + // Since we use pointers, the default = would work incorrectly. + blake2s_state& operator = (blake2s_state &st) + { + if (this != &st) + { + memcpy(buf, st.buf, BLAKE_DATA_SIZE); + buflen = st.buflen; + last_node = st.last_node; + } + return *this; + } +}; + + +#ifdef RAR_SMP +class ThreadPool; +#endif + +struct blake2sp_state +{ + blake2s_state S[8]; + blake2s_state R; + byte buf[8 * BLAKE2S_BLOCKBYTES]; + size_t buflen; + +#ifdef RAR_SMP + ThreadPool *ThPool; + uint MaxThreads; +#endif +}; + +void blake2sp_init( blake2sp_state *S ); +void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen ); +void blake2sp_final( blake2sp_state *S, byte *digest ); + +#endif + diff --git a/reference/unrar/blake2s_sse.cpp b/reference/unrar/blake2s_sse.cpp new file mode 100644 index 00000000..1a02f210 --- /dev/null +++ b/reference/unrar/blake2s_sse.cpp @@ -0,0 +1,129 @@ +// Based on public domain code written in 2012 by Samuel Neves + +extern const byte blake2s_sigma[10][16]; + +// Initialization vector. +static __m128i blake2s_IV_0_3, blake2s_IV_4_7; + +#ifdef _WIN_64 +// Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro. +static __m128i crotr8, crotr16; +#endif + +static void blake2s_init_sse() +{ + // We cannot initialize these 128 bit variables in place when declaring + // them globally, because global scope initialization is performed before + // our SSE check and it would make code incompatible with older non-SSE2 + // CPUs. Also we cannot initialize them as static inside of function + // using these variables, because SSE static initialization is not thread + // safe: first thread starts initialization and sets "init done" flag even + // if it is not done yet, second thread can attempt to access half-init + // SSE data. So we moved init code here. + + blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A ); + blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 ); + +#ifdef _WIN_64 + crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 ); + crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 ); +#endif +} + + +#define LOAD(p) _mm_load_si128( (__m128i *)(p) ) +#define STORE(p,r) _mm_store_si128((__m128i *)(p), r) + +#ifdef _WIN_32 +// 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient +// to not use _mm_shuffle_epi8 here. +#define mm_rotr_epi32(r, c) ( \ + _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) +#else +#define mm_rotr_epi32(r, c) ( \ + c==8 ? _mm_shuffle_epi8(r,crotr8) \ + : c==16 ? _mm_shuffle_epi8(r,crotr16) \ + : _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) ) +#endif + + +#define G1(row1,row2,row3,row4,buf) \ + row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ + row4 = _mm_xor_si128( row4, row1 ); \ + row4 = mm_rotr_epi32(row4, 16); \ + row3 = _mm_add_epi32( row3, row4 ); \ + row2 = _mm_xor_si128( row2, row3 ); \ + row2 = mm_rotr_epi32(row2, 12); + +#define G2(row1,row2,row3,row4,buf) \ + row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \ + row4 = _mm_xor_si128( row4, row1 ); \ + row4 = mm_rotr_epi32(row4, 8); \ + row3 = _mm_add_epi32( row3, row4 ); \ + row2 = _mm_xor_si128( row2, row3 ); \ + row2 = mm_rotr_epi32(row2, 7); + +#define DIAGONALIZE(row1,row2,row3,row4) \ + row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \ + row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \ + row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) ); + +#define UNDIAGONALIZE(row1,row2,row3,row4) \ + row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \ + row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \ + row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) ); + +#ifdef _WIN_64 + // MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load + // from stack operations, which are slower than this code. + #define _mm_set_epi32(i3,i2,i1,i0) \ + _mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \ + _mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3))) +#endif + +// Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode +// and about the same in x64 mode in our test. Perhaps depends on compiler. +// We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather +// instructions here, but they did not show any speed gain on i7-6700K. +#define SSE_ROUND(m,row,r) \ +{ \ + __m128i buf; \ + buf=_mm_set_epi32(m[blake2s_sigma[r][6]],m[blake2s_sigma[r][4]],m[blake2s_sigma[r][2]],m[blake2s_sigma[r][0]]); \ + G1(row[0],row[1],row[2],row[3],buf); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][7]],m[blake2s_sigma[r][5]],m[blake2s_sigma[r][3]],m[blake2s_sigma[r][1]]); \ + G2(row[0],row[1],row[2],row[3],buf); \ + DIAGONALIZE(row[0],row[1],row[2],row[3]); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][14]],m[blake2s_sigma[r][12]],m[blake2s_sigma[r][10]],m[blake2s_sigma[r][8]]); \ + G1(row[0],row[1],row[2],row[3],buf); \ + buf=_mm_set_epi32(m[blake2s_sigma[r][15]],m[blake2s_sigma[r][13]],m[blake2s_sigma[r][11]],m[blake2s_sigma[r][9]]); \ + G2(row[0],row[1],row[2],row[3],buf); \ + UNDIAGONALIZE(row[0],row[1],row[2],row[3]); \ +} + + +static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] ) +{ + __m128i row[4]; + __m128i ff0, ff1; + + const uint32 *m = ( uint32 * )block; + + row[0] = ff0 = LOAD( &S->h[0] ); + row[1] = ff1 = LOAD( &S->h[4] ); + + row[2] = blake2s_IV_0_3; + row[3] = _mm_xor_si128( blake2s_IV_4_7, LOAD( &S->t[0] ) ); + SSE_ROUND( m, row, 0 ); + SSE_ROUND( m, row, 1 ); + SSE_ROUND( m, row, 2 ); + SSE_ROUND( m, row, 3 ); + SSE_ROUND( m, row, 4 ); + SSE_ROUND( m, row, 5 ); + SSE_ROUND( m, row, 6 ); + SSE_ROUND( m, row, 7 ); + SSE_ROUND( m, row, 8 ); + SSE_ROUND( m, row, 9 ); + STORE( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row[0], row[2] ) ) ); + STORE( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row[1], row[3] ) ) ); + return 0; +} diff --git a/reference/unrar/blake2sp.cpp b/reference/unrar/blake2sp.cpp new file mode 100644 index 00000000..da645883 --- /dev/null +++ b/reference/unrar/blake2sp.cpp @@ -0,0 +1,153 @@ +/* + BLAKE2 reference source code package - reference C implementations + + Written in 2012 by Samuel Neves + + To the extent possible under law, the author(s) have dedicated all copyright + and related and neighboring rights to this software to the public domain + worldwide. This software is distributed without any warranty. + + You should have received a copy of the CC0 Public Domain Dedication along with + this software. If not, see . +*/ + +#define PARALLELISM_DEGREE 8 + +void blake2sp_init( blake2sp_state *S ) +{ + memset( S->buf, 0, sizeof( S->buf ) ); + S->buflen = 0; + + blake2s_init_param( &S->R, 0, 1 ); // Init root. + + for( uint i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_init_param( &S->S[i], i, 0 ); // Init leaf. + + S->R.last_node = 1; + S->S[PARALLELISM_DEGREE - 1].last_node = 1; +} + + +struct Blake2ThreadData +{ + void Update(); + blake2s_state *S; + const byte *in; + size_t inlen; +}; + + +void Blake2ThreadData::Update() +{ + size_t inlen__ = inlen; + const byte *in__ = ( const byte * )in; + + while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ) + { +#ifdef USE_SSE + // We gain 5% in i7 SSE mode by prefetching next data block. + if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES) + _mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0); +#endif + blake2s_update( S, in__, BLAKE2S_BLOCKBYTES ); + in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + } +} + +#ifdef RAR_SMP +THREAD_PROC(Blake2Thread) +{ + Blake2ThreadData *td=(Blake2ThreadData *)Data; + td->Update(); +} +#endif + + +void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen ) +{ + size_t left = S->buflen; + size_t fill = sizeof( S->buf ) - left; + + if( left && inlen >= fill ) + { + memcpy( S->buf + left, in, fill ); + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); + + in += fill; + inlen -= fill; + left = 0; + } + + Blake2ThreadData btd_array[PARALLELISM_DEGREE]; + +#ifdef RAR_SMP + uint ThreadNumber = inlen < 0x1000 ? 1 : S->MaxThreads; + + if (ThreadNumber==6 || ThreadNumber==7) // 6 and 7 threads work slower than 4 here. + ThreadNumber=4; +#else + uint ThreadNumber=1; +#endif + + for (size_t id__=0;id__inlen = inlen; + btd->in = in + id__ * BLAKE2S_BLOCKBYTES; + btd->S = &S->S[id__]; + +#ifdef RAR_SMP + if (ThreadNumber>1) + S->ThPool->AddTask(Blake2Thread,(void*)btd); + else + btd->Update(); +#else + btd->Update(); +#endif + id__++; + } +#ifdef RAR_SMP + if (S->ThPool!=NULL) // Can be NULL in -mt1 mode. + S->ThPool->WaitDone(); +#endif // RAR_SMP + } + + in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES ); + inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES; + + if( inlen > 0 ) + memcpy( S->buf + left, in, (size_t)inlen ); + + S->buflen = left + (size_t)inlen; +} + + +void blake2sp_final( blake2sp_state *S, byte *digest ) +{ + byte hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES]; + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + { + if( S->buflen > i * BLAKE2S_BLOCKBYTES ) + { + size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES; + + if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES; + + blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left ); + } + + blake2s_final( &S->S[i], hash[i] ); + } + + for( size_t i = 0; i < PARALLELISM_DEGREE; ++i ) + blake2s_update( &S->R, hash[i], BLAKE2S_OUTBYTES ); + + blake2s_final( &S->R, digest ); +} diff --git a/reference/unrar/cmddata.cpp b/reference/unrar/cmddata.cpp new file mode 100644 index 00000000..758cd171 --- /dev/null +++ b/reference/unrar/cmddata.cpp @@ -0,0 +1,1400 @@ +#include "rar.hpp" + +CommandData::CommandData() +{ + Init(); +} + + +void CommandData::Init() +{ + RAROptions::Init(); + + *Command=0; + *ArcName=0; + FileLists=false; + + ListMode=RCLM_AUTO; + + BareOutput=false; + + + FileArgs.Reset(); + ExclArgs.Reset(); + InclArgs.Reset(); + StoreArgs.Reset(); + ArcNames.Reset(); + NextVolSizes.Reset(); +} + + +// Return the pointer to next position in the string and store dynamically +// allocated command line parameter in Par. +static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par) +{ + const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0); + if (NextCmd==NULL) + return NULL; + size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero. + *Par=(wchar *)malloc(ParSize*sizeof(wchar)); + if (*Par==NULL) + return NULL; + return GetCmdParam(CmdLine,*Par,ParSize); +} + + +#if !defined(SFX_MODULE) +void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[]) +{ + *Command=0; + NoMoreSwitches=false; +#ifdef CUSTOM_CMDLINE_PARSER + // In Windows we may prefer to implement our own command line parser + // to avoid replacing \" by " in standard parser. Such replacing corrupts + // destination paths like "dest path\" in extraction commands. + // Also our own parser is Unicode compatible. + const wchar *CmdLine=GetCommandLine(); + + wchar *Par; + for (bool FirstParam=true;;FirstParam=false) + { + if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL) + break; + if (!FirstParam) // First parameter is the executable name. + if (Preprocess) + PreprocessArg(Par); + else + ParseArg(Par); + free(Par); + } +#else + Array Arg; + for (int I=1;I EnvStrW(strlen(EnvStr)+1); + CharToWide(EnvStr,&EnvStrW[0],EnvStrW.Size()); + ProcessSwitchesString(&EnvStrW[0]); + } +} +#endif + + + +#if !defined(SFX_MODULE) +// Preprocess those parameters, which must be processed before the rest of +// command line. Return 'false' to stop further processing. +void CommandData::PreprocessArg(const wchar *Arg) +{ + if (IsSwitch(Arg[0]) && !NoMoreSwitches) + { + Arg++; + if (Arg[0]=='-' && Arg[1]==0) // Switch "--". + NoMoreSwitches=true; + if (wcsicomp(Arg,L"cfg-")==0) + ConfigDisabled=true; + if (wcsnicomp(Arg,L"ilog",4)==0) + { + // Ensure that correct log file name is already set + // if we need to report an error when processing the command line. + ProcessSwitch(Arg); + InitLogOptions(LogName,ErrlogCharset); + } + if (wcsnicomp(Arg,L"sc",2)==0) + { + // Process -sc before reading any file lists. + ProcessSwitch(Arg); + if (*LogName!=0) + InitLogOptions(LogName,ErrlogCharset); + } + } + else + if (*Command==0) + wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini. +} +#endif + + +#if !defined(SFX_MODULE) +void CommandData::ReadConfig() +{ + StringList List; + if (ReadTextFile(DefConfigName,&List,true)) + { + wchar *Str; + while ((Str=List.GetString())!=NULL) + { + while (IsSpace(*Str)) + Str++; + if (wcsnicomp(Str,L"switches=",9)==0) + ProcessSwitchesString(Str+9); + if (*Command!=0) + { + wchar Cmd[16]; + wcsncpyz(Cmd,Command,ASIZE(Cmd)); + wchar C0=toupperw(Cmd[0]); + wchar C1=toupperw(Cmd[1]); + if (C0=='I' || C0=='L' || C0=='M' || C0=='S' || C0=='V') + Cmd[1]=0; + if (C0=='R' && (C1=='R' || C1=='V')) + Cmd[2]=0; + wchar SwName[16+ASIZE(Cmd)]; + swprintf(SwName,ASIZE(SwName),L"switches_%s=",Cmd); + size_t Length=wcslen(SwName); + if (wcsnicomp(Str,SwName,Length)==0) + ProcessSwitchesString(Str+Length); + } + } + } +} +#endif + + +#if !defined(SFX_MODULE) +void CommandData::ProcessSwitchesString(const wchar *Str) +{ + wchar *Par; + while ((Str=AllocCmdParam(Str,&Par))!=NULL) + { + if (IsSwitch(*Par)) + ProcessSwitch(Par+1); + free(Par); + } +} +#endif + + +#if !defined(SFX_MODULE) +void CommandData::ProcessSwitch(const wchar *Switch) +{ + + switch(toupperw(Switch[0])) + { + case '@': + ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS; + break; + case 'A': + switch(toupperw(Switch[1])) + { + case 'C': + ClearArc=true; + break; + case 'D': + AppendArcNameToPath=true; + break; +#ifndef SFX_MODULE + case 'G': + if (Switch[2]=='-' && Switch[3]==0) + GenerateArcName=0; + else + { + GenerateArcName=true; + wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask)); + } + break; +#endif + case 'I': + IgnoreGeneralAttr=true; + break; + case 'N': // Reserved for archive name. + break; + case 'O': + AddArcOnly=true; + break; + case 'P': + wcscpy(ArcPath,Switch+2); + break; + case 'S': + SyncFiles=true; + break; + default: + BadSwitch(Switch); + break; + } + break; + case 'C': + if (Switch[2]==0) + switch(toupperw(Switch[1])) + { + case '-': + DisableComment=true; + break; + case 'U': + ConvertNames=NAMES_UPPERCASE; + break; + case 'L': + ConvertNames=NAMES_LOWERCASE; + break; + } + break; + case 'D': + if (Switch[2]==0) + switch(toupperw(Switch[1])) + { + case 'S': + DisableSortSolid=true; + break; + case 'H': + OpenShared=true; + break; + case 'F': + DeleteFiles=true; + break; + } + break; + case 'E': + switch(toupperw(Switch[1])) + { + case 'P': + switch(Switch[2]) + { + case 0: + ExclPath=EXCL_SKIPWHOLEPATH; + break; + case '1': + ExclPath=EXCL_BASEPATH; + break; + case '2': + ExclPath=EXCL_SAVEFULLPATH; + break; + case '3': + ExclPath=EXCL_ABSPATH; + break; + } + break; + default: + if (Switch[1]=='+') + { + InclFileAttr|=GetExclAttr(Switch+2); + InclAttrSet=true; + } + else + ExclFileAttr|=GetExclAttr(Switch+1); + break; + } + break; + case 'F': + if (Switch[1]==0) + FreshFiles=true; + else + BadSwitch(Switch); + break; + case 'H': + switch (toupperw(Switch[1])) + { + case 'P': + EncryptHeaders=true; + if (Switch[2]!=0) + { + Password.Set(Switch+2); + cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); + } + else + if (!Password.IsSet()) + { + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); + eprintf(L"\n"); + } + break; + default : + BadSwitch(Switch); + break; + } + break; + case 'I': + if (wcsnicomp(Switch+1,L"LOG",3)==0) + { + wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName)); + break; + } + if (wcsicomp(Switch+1,L"SND")==0) + { + Sound=true; + break; + } + if (wcsicomp(Switch+1,L"ERR")==0) + { + MsgStream=MSG_STDERR; + break; + } + if (wcsnicomp(Switch+1,L"EML",3)==0) + { + wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo)); + break; + } + if (wcsicomp(Switch+1,L"NUL")==0) + { + MsgStream=MSG_NULL; + break; + } + if (toupperw(Switch[1])=='D') + { + for (uint I=2;Switch[I]!=0;I++) + switch(toupperw(Switch[I])) + { + case 'Q': + MsgStream=MSG_ERRONLY; + break; + case 'C': + DisableCopyright=true; + break; + case 'D': + DisableDone=true; + break; + case 'P': + DisablePercentage=true; + break; + } + break; + } + if (wcsicomp(Switch+1,L"OFF")==0) + { + Shutdown=true; + break; + } + if (wcsicomp(Switch+1,L"VER")==0) + { + PrintVersion=true; + break; + } + break; + case 'K': + switch(toupperw(Switch[1])) + { + case 'B': + KeepBroken=true; + break; + case 0: + Lock=true; + break; + } + break; + case 'M': + switch(toupperw(Switch[1])) + { + case 'C': + { + const wchar *Str=Switch+2; + if (*Str=='-') + for (uint I=0;IMaxPoolThreads || Threads<1) + BadSwitch(Switch); + else + { + } + break; +#endif + default: + Method=Switch[1]-'0'; + if (Method>5 || Method<0) + BadSwitch(Switch); + break; + } + break; + case 'N': + case 'X': + if (Switch[1]!=0) + { + StringList *Args=toupperw(Switch[0])=='N' ? &InclArgs:&ExclArgs; + if (Switch[1]=='@' && !IsWildcard(Switch)) + { + RAR_CHARSET Charset=FilelistCharset; + +#if defined(_WIN_ALL) + // for compatibility reasons we use OEM encoding + // in Win32 console version by default + +// if (Charset==RCH_DEFAULT) +// Charset=RCH_OEM; +#endif + + ReadTextFile(Switch+2,Args,false,true,Charset,true,true,true); + } + else + Args->AddString(Switch+1); + } + break; + case 'O': + switch(toupperw(Switch[1])) + { + case '+': + Overwrite=OVERWRITE_ALL; + break; + case '-': + Overwrite=OVERWRITE_NONE; + break; + case 0: + Overwrite=OVERWRITE_FORCE_ASK; + break; +#ifdef _WIN_ALL + case 'C': + SetCompressedAttr=true; + break; +#endif + case 'H': + SaveHardLinks=true; + break; + + +#ifdef SAVE_LINKS + case 'L': + SaveSymLinks=true; + if (toupperw(Switch[2])=='A') + AbsoluteLinks=true; + break; +#endif +#ifdef _WIN_ALL + case 'N': + if (toupperw(Switch[2])=='I') + AllowIncompatNames=true; + break; +#endif + case 'R': + Overwrite=OVERWRITE_AUTORENAME; + break; +#ifdef _WIN_ALL + case 'S': + SaveStreams=true; + break; +#endif + case 'W': + ProcessOwners=true; + break; + default : + BadSwitch(Switch); + break; + } + break; + case 'P': + if (Switch[1]==0) + { + uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password); + eprintf(L"\n"); + } + else + { + Password.Set(Switch+1); + cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0])); + } + break; +#ifndef SFX_MODULE + case 'Q': + if (toupperw(Switch[1])=='O') + switch(toupperw(Switch[2])) + { + case 0: + QOpenMode=QOPEN_AUTO; + break; + case '-': + QOpenMode=QOPEN_NONE; + break; + case '+': + QOpenMode=QOPEN_ALWAYS; + break; + default: + BadSwitch(Switch); + break; + } + else + BadSwitch(Switch); + break; +#endif + case 'R': + switch(toupperw(Switch[1])) + { + case 0: + Recurse=RECURSE_ALWAYS; + break; + case '-': + Recurse=RECURSE_DISABLE; + break; + case '0': + Recurse=RECURSE_WILDCARDS; + break; + case 'I': + { + Priority=atoiw(Switch+2); + if (Priority<0 || Priority>15) + BadSwitch(Switch); + const wchar *ChPtr=wcschr(Switch+2,':'); + if (ChPtr!=NULL) + { + SleepTime=atoiw(ChPtr+1); + if (SleepTime>1000) + BadSwitch(Switch); + InitSystemOptions(SleepTime); + } + SetPriority(Priority); + } + break; + } + break; + case 'S': + if (IsDigit(Switch[1])) + { + Solid|=SOLID_COUNT; + SolidCount=atoiw(&Switch[1]); + } + else + switch(toupperw(Switch[1])) + { + case 0: + Solid|=SOLID_NORMAL; + break; + case '-': + Solid=SOLID_NONE; + break; + case 'E': + Solid|=SOLID_FILEEXT; + break; + case 'V': + Solid|=Switch[2]=='-' ? SOLID_VOLUME_DEPENDENT:SOLID_VOLUME_INDEPENDENT; + break; + case 'D': + Solid|=SOLID_VOLUME_DEPENDENT; + break; + case 'L': + if (IsDigit(Switch[2])) + FileSizeLess=atoilw(Switch+2); + break; + case 'M': + if (IsDigit(Switch[2])) + FileSizeMore=atoilw(Switch+2); + break; + case 'C': + { + bool AlreadyBad=false; // Avoid reporting "bad switch" several times. + + RAR_CHARSET rch=RCH_DEFAULT; + switch(toupperw(Switch[2])) + { + case 'A': + rch=RCH_ANSI; + break; + case 'O': + rch=RCH_OEM; + break; + case 'U': + rch=RCH_UNICODE; + break; + case 'F': + rch=RCH_UTF8; + break; + default : + BadSwitch(Switch); + AlreadyBad=true; + break; + }; + if (!AlreadyBad) + if (Switch[3]==0) + CommentCharset=FilelistCharset=ErrlogCharset=RedirectCharset=rch; + else + for (uint I=3;Switch[I]!=0 && !AlreadyBad;I++) + switch(toupperw(Switch[I])) + { + case 'C': + CommentCharset=rch; + break; + case 'L': + FilelistCharset=rch; + break; + case 'R': + RedirectCharset=rch; + break; + default: + BadSwitch(Switch); + AlreadyBad=true; + break; + } + } + break; + + } + break; + case 'T': + switch(toupperw(Switch[1])) + { + case 'K': + ArcTime=ARCTIME_KEEP; + break; + case 'L': + ArcTime=ARCTIME_LATEST; + break; + case 'O': + FileTimeBefore.SetAgeText(Switch+2); + break; + case 'N': + FileTimeAfter.SetAgeText(Switch+2); + break; + case 'B': + FileTimeBefore.SetIsoText(Switch+2); + break; + case 'A': + FileTimeAfter.SetIsoText(Switch+2); + break; + case 'S': + { + EXTTIME_MODE Mode=EXTTIME_HIGH3; + bool CommonMode=Switch[2]>='0' && Switch[2]<='4'; + if (CommonMode) + Mode=(EXTTIME_MODE)(Switch[2]-'0'); + if (Mode==EXTTIME_HIGH1 || Mode==EXTTIME_HIGH2) // '2' and '3' not supported anymore. + Mode=EXTTIME_HIGH3; + if (Switch[2]=='-') + Mode=EXTTIME_NONE; + if (CommonMode || Switch[2]=='-' || Switch[2]=='+' || Switch[2]==0) + xmtime=xctime=xatime=Mode; + else + { + if (Switch[3]>='0' && Switch[3]<='4') + Mode=(EXTTIME_MODE)(Switch[3]-'0'); + if (Mode==EXTTIME_HIGH1 || Mode==EXTTIME_HIGH2) // '2' and '3' not supported anymore. + Mode=EXTTIME_HIGH3; + if (Switch[3]=='-') + Mode=EXTTIME_NONE; + switch(toupperw(Switch[2])) + { + case 'M': + xmtime=Mode; + break; + case 'C': + xctime=Mode; + break; + case 'A': + xatime=Mode; + break; + } + } + } + break; + case '-': + Test=false; + break; + case 0: + Test=true; + break; + default: + BadSwitch(Switch); + break; + } + break; + case 'U': + if (Switch[1]==0) + UpdateFiles=true; + else + BadSwitch(Switch); + break; + case 'V': + switch(toupperw(Switch[1])) + { + case 'P': + VolumePause=true; + break; + case 'E': + if (toupperw(Switch[2])=='R') + VersionControl=atoiw(Switch+3)+1; + break; + case '-': + VolSize=0; + break; + default: + VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command. + break; + } + break; + case 'W': + wcsncpyz(TempPath,Switch+1,ASIZE(TempPath)); + AddEndSlash(TempPath,ASIZE(TempPath)); + break; + case 'Y': + AllYes=true; + break; + case 'Z': + if (Switch[1]==0) + { + // If comment file is not specified, we read data from stdin. + wcscpy(CommentFile,L"stdin"); + } + else + wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile)); + break; + case '?' : + OutHelp(RARX_SUCCESS); + break; + default : + BadSwitch(Switch); + break; + } +} +#endif + + +#if !defined(SFX_MODULE) +void CommandData::BadSwitch(const wchar *Switch) +{ + mprintf(St(MUnknownOption),Switch); + ErrHandler.Exit(RARX_USERERROR); +} +#endif + + +void CommandData::OutTitle() +{ + if (BareOutput || DisableCopyright) + return; +#if defined(__GNUC__) && defined(SFX_MODULE) + mprintf(St(MCopyrightS)); +#else +#ifndef SILENT + static bool TitleShown=false; + if (TitleShown) + return; + TitleShown=true; + + wchar Version[80]; + if (RARVER_BETA!=0) + swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA); + else + swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR); +#if defined(_WIN_32) || defined(_WIN_64) + wcsncatz(Version,L" ",ASIZE(Version)); +#endif +#ifdef _WIN_32 + wcsncatz(Version,St(Mx86),ASIZE(Version)); +#endif +#ifdef _WIN_64 + wcsncatz(Version,St(Mx64),ASIZE(Version)); +#endif + if (PrintVersion) + { + mprintf(L"%s",Version); + exit(0); + } + mprintf(St(MUCopyright),Version,RARVER_YEAR); +#endif +#endif +} + + +inline bool CmpMSGID(MSGID i1,MSGID i2) +{ +#ifdef MSGID_INT + return i1==i2; +#else + // If MSGID is const char*, we cannot compare pointers only. + // Pointers to different instances of same string can differ, + // so we need to compare complete strings. + return wcscmp(i1,i2)==0; +#endif +} + +void CommandData::OutHelp(RAR_EXIT ExitCode) +{ +#if !defined(SILENT) + OutTitle(); + static MSGID Help[]={ +#ifdef SFX_MODULE + // Console SFX switches definition. + MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV +#else + // UnRAR switches definition. + MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL, + MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm, + MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP, + MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU, + MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR, + MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal, + MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOW,MCHelpSwP, + MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSL,MCHelpSwSM, + MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU, + MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal, + MCHelpSwY +#endif + }; + + for (uint I=0;IRewind(); + while (Args->GetString(CurMask,ASIZE(CurMask))) + { + wchar *LastMaskChar=PointToLastChar(CurMask); + bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only. + + if (Dir) + { + // CheckName is a directory. + if (DirMask) + { + // We process the directory and have the directory exclusion mask. + // So let's convert "mask\" to "mask" and process it normally. + + *LastMaskChar=0; + } + else + { + // REMOVED, we want -npath\* to match empty folders too. + // If mask has wildcards in name part and does not have the trailing + // '\' character, we cannot use it for directories. + + // if (IsWildcard(PointToName(CurMask))) + // continue; + } + } + else + { + // If we process a file inside of directory excluded by "dirmask\". + // we want to exclude such file too. So we convert "dirmask\" to + // "dirmask\*". It is important for operations other than archiving + // with -x. When archiving with -x, directory matched by "dirmask\" + // is excluded from further scanning. + + if (DirMask) + wcsncatz(CurMask,L"*",ASIZE(CurMask)); + } + +#ifndef SFX_MODULE + if (CheckFullPath && IsFullPath(CurMask)) + { + // We do not need to do the special "*\" processing here, because + // unlike the "else" part of this "if", now we convert names to full + // format, so they all include the path, which is matched by "*\" + // correctly. Moreover, removing "*\" from mask would break + // the comparison, because now all names have the path. + + if (*FullName==0) + ConvertNameToFull(CheckName,FullName,ASIZE(FullName)); + if (CmpName(CurMask,FullName,MatchMode)) + return true; + } + else +#endif + { + wchar NewName[NM+2],*CurName=Name; + + // Important to convert before "*\" check below, so masks like + // d:*\something are processed properly. + wchar *CmpMask=ConvertPath(CurMask,NULL); + + if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1])) + { + // We want "*\name" to match 'name' not only in subdirectories, + // but also in the current directory. We convert the name + // from 'name' to '.\name' to be matched by "*\" part even if it is + // in current directory. + NewName[0]='.'; + NewName[1]=CPATHDIVIDER; + wcsncpyz(NewName+2,Name,ASIZE(NewName)-2); + CurName=NewName; + } + + if (CmpName(CmpMask,CurName,MatchMode)) + return true; + } + } + return false; +} + + +#ifndef SFX_MODULE +// Now this function performs only one task and only in Windows version: +// it skips symlinks to directories if -e1024 switch is specified. +// Symlinks are skipped in ScanTree class, so their entire contents +// is skipped too. Without this function we would check the attribute +// only directly before archiving, so we would skip the symlink record, +// but not the contents of symlinked directory. +bool CommandData::ExclDirByAttr(uint FileAttr) +{ +#ifdef _WIN_ALL + if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 && + (ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0) + return true; +#endif + return false; +} +#endif + + + + +#ifndef SFX_MODULE +// Return 'true' if we need to exclude the file from processing. +bool CommandData::TimeCheck(RarTime &ft) +{ + if (FileTimeBefore.IsSet() && ft>=FileTimeBefore) + return true; + if (FileTimeAfter.IsSet() && ft<=FileTimeAfter) + return true; + return false; +} +#endif + + +#ifndef SFX_MODULE +// Return 'true' if we need to exclude the file from processing. +bool CommandData::SizeCheck(int64 Size) +{ + if (FileSizeLess!=INT64NDF && Size>=FileSizeLess) + return(true); + if (FileSizeMore!=INT64NDF && Size<=FileSizeMore) + return(true); + return(false); +} +#endif + + + + +int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType, + wchar *MatchedArg,uint MatchedArgSize) +{ + if (MatchedArg!=NULL && MatchedArgSize>0) + *MatchedArg=0; + if (wcslen(FileHead.FileName)>=NM) + return 0; + bool Dir=FileHead.Dir; + if (ExclCheck(FileHead.FileName,Dir,false,true)) + return 0; +#ifndef SFX_MODULE + if (TimeCheck(FileHead.mtime)) + return 0; + if ((FileHead.FileAttr & ExclFileAttr)!=0 || InclAttrSet && (FileHead.FileAttr & InclFileAttr)==0) + return 0; + if (!Dir && SizeCheck(FileHead.UnpSize)) + return 0; +#endif + wchar *ArgName; + FileArgs.Rewind(); + for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++) + if (CmpName(ArgName,FileHead.FileName,MatchType)) + { + if (ExactMatch!=NULL) + *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0; + if (MatchedArg!=NULL) + wcsncpyz(MatchedArg,ArgName,MatchedArgSize); + return StringCount; + } + return 0; +} + + +void CommandData::ProcessCommand() +{ +#ifndef SFX_MODULE + + const wchar *SingleCharCommands=L"FUADPXETK"; + if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0) + OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters. + + const wchar *ArcExt=GetExt(ArcName); +#ifdef _UNIX + if (ArcExt==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName)))) + wcsncatz(ArcName,L".rar",ASIZE(ArcName)); +#else + if (ArcExt==NULL) + wcsncatz(ArcName,L".rar",ASIZE(ArcName)); +#endif + // Treat arcname.part1 as arcname.part1.rar. + if (ArcExt!=NULL && wcsnicomp(ArcExt,L".part",5)==0 && IsDigit(ArcExt[5]) && + !FileExist(ArcName)) + { + wchar Name[NM]; + wcsncpyz(Name,ArcName,ASIZE(Name)); + wcsncatz(Name,L".rar",ASIZE(Name)); + if (FileExist(Name)) + wcsncpyz(ArcName,Name,ASIZE(ArcName)); + } + + if (wcschr(L"AFUMD",*Command)==NULL) + { + if (GenerateArcName) + GenerateArchiveName(ArcName,ASIZE(ArcName),GenerateMask,false); + + StringList ArcMasks; + ArcMasks.AddString(ArcName); + ScanTree Scan(&ArcMasks,Recurse,SaveSymLinks,SCAN_SKIPDIRS); + FindData FindData; + while (Scan.GetNext(&FindData)==SCAN_SUCCESS) + AddArcName(FindData.Name); + } + else + AddArcName(ArcName); +#endif + + switch(Command[0]) + { + case 'P': + case 'X': + case 'E': + case 'T': + case 'I': + { + CmdExtract Extract(this); + Extract.DoExtract(); + } + break; +#ifndef SILENT + case 'V': + case 'L': + ListArchive(this); + break; + default: + OutHelp(RARX_USERERROR); +#endif + } + if (!BareOutput) + mprintf(L"\n"); +} + + +void CommandData::AddArcName(const wchar *Name) +{ + ArcNames.AddString(Name); +} + + +bool CommandData::GetArcName(wchar *Name,int MaxSize) +{ + return ArcNames.GetString(Name,MaxSize); +} + + +bool CommandData::IsSwitch(int Ch) +{ +#if defined(_WIN_ALL) || defined(_EMX) + return Ch=='-' || Ch=='/'; +#else + return Ch=='-'; +#endif +} + + +#ifndef SFX_MODULE +uint CommandData::GetExclAttr(const wchar *Str) +{ + if (IsDigit(*Str)) + return wcstol(Str,NULL,0); + + uint Attr=0; + while (*Str!=0) + { + switch(toupperw(*Str)) + { +#ifdef _UNIX + case 'D': + Attr|=S_IFDIR; + break; + case 'V': + Attr|=S_IFCHR; + break; +#elif defined(_WIN_ALL) || defined(_EMX) + case 'R': + Attr|=0x1; + break; + case 'H': + Attr|=0x2; + break; + case 'S': + Attr|=0x4; + break; + case 'D': + Attr|=0x10; + break; + case 'A': + Attr|=0x20; + break; +#endif + } + Str++; + } + return Attr; +} +#endif + + + + +#ifndef SFX_MODULE +bool CommandData::CheckWinSize() +{ + // Define 0x100000000 as macro to avoid troubles with older compilers. + const uint64 MaxDictSize=INT32TO64(1,0); + // Limit the dictionary size to 4 GB. + for (uint64 I=0x10000;I<=MaxDictSize;I*=2) + if (WinSize==I) + return true; + WinSize=0x400000; + return false; +} +#endif + + +#ifndef SFX_MODULE +void CommandData::ReportWrongSwitches(RARFORMAT Format) +{ + if (Format==RARFMT15) + { + if (HashType!=HASH_CRC32) + uiMsg(UIERROR_INCOMPATSWITCH,L"-ht",4); +#ifdef _WIN_ALL + if (SaveSymLinks) + uiMsg(UIERROR_INCOMPATSWITCH,L"-ol",4); +#endif + if (SaveHardLinks) + uiMsg(UIERROR_INCOMPATSWITCH,L"-oh",4); + +#ifdef _WIN_ALL + // Do not report a wrong dictionary size here, because we are not sure + // yet about archive format. We can switch to RAR5 mode later + // if we update RAR5 archive. + + +#endif + if (QOpenMode!=QOPEN_AUTO) + uiMsg(UIERROR_INCOMPATSWITCH,L"-qo",4); + } + if (Format==RARFMT50) + { + } +} +#endif diff --git a/reference/unrar/cmddata.hpp b/reference/unrar/cmddata.hpp new file mode 100644 index 00000000..e12d4fbc --- /dev/null +++ b/reference/unrar/cmddata.hpp @@ -0,0 +1,64 @@ +#ifndef _RAR_CMDDATA_ +#define _RAR_CMDDATA_ + + +#define DefaultStoreList L"7z;ace;arj;bz2;cab;gz;jpeg;jpg;lha;lz;lzh;mp3;rar;taz;tgz;xz;z;zip;zipx" + +enum RAR_CMD_LIST_MODE {RCLM_AUTO,RCLM_REJECT_LISTS,RCLM_ACCEPT_LISTS}; + +class CommandData:public RAROptions +{ + private: + void ProcessSwitchesString(const wchar *Str); + void ProcessSwitch(const wchar *Switch); + void BadSwitch(const wchar *Switch); + uint GetExclAttr(const wchar *Str); + + bool FileLists; + bool NoMoreSwitches; + RAR_CMD_LIST_MODE ListMode; + bool BareOutput; + public: + CommandData(); + void Init(); + + void ParseCommandLine(bool Preprocess,int argc, char *argv[]); + void ParseArg(wchar *ArgW); + void ParseDone(); + void ParseEnvVar(); + void ReadConfig(); + void PreprocessArg(const wchar *Arg); + void OutTitle(); + void OutHelp(RAR_EXIT ExitCode); + bool IsSwitch(int Ch); + bool ExclCheck(const wchar *CheckName,bool Dir,bool CheckFullPath,bool CheckInclList); + static bool CheckArgs(StringList *Args,bool Dir,const wchar *CheckName,bool CheckFullPath,int MatchMode); + bool ExclDirByAttr(uint FileAttr); + bool TimeCheck(RarTime &ft); + bool SizeCheck(int64 Size); + bool AnyFiltersActive(); + int IsProcessFile(FileHeader &FileHead,bool *ExactMatch=NULL,int MatchType=MATCH_WILDSUBPATH, + wchar *MatchedArg=NULL,uint MatchedArgSize=0); + void ProcessCommand(); + void AddArcName(const wchar *Name); + bool GetArcName(wchar *Name,int MaxSize); + bool CheckWinSize(); + + int GetRecoverySize(const wchar *Str,int DefSize); + +#ifndef SFX_MODULE + void ReportWrongSwitches(RARFORMAT Format); +#endif + + wchar Command[NM+16]; + + wchar ArcName[NM]; + + StringList FileArgs; + StringList ExclArgs; + StringList InclArgs; + StringList ArcNames; + StringList StoreArgs; +}; + +#endif diff --git a/reference/unrar/coder.cpp b/reference/unrar/coder.cpp new file mode 100644 index 00000000..9d971a8a --- /dev/null +++ b/reference/unrar/coder.cpp @@ -0,0 +1,48 @@ + + +inline unsigned int RangeCoder::GetChar() +{ + return(UnpackRead->GetChar()); +} + + +void RangeCoder::InitDecoder(Unpack *UnpackRead) +{ + RangeCoder::UnpackRead=UnpackRead; + + low=code=0; + range=uint(-1); + for (int i=0;i < 4;i++) + code=(code << 8) | GetChar(); +} + + +// (int) cast before "low" added only to suppress compiler warnings. +#define ARI_DEC_NORMALIZE(code,low,range,read) \ +{ \ + while ((low^(low+range))GetChar(); \ + range <<= 8; \ + low <<= 8; \ + } \ +} + + +inline int RangeCoder::GetCurrentCount() +{ + return (code-low)/(range /= SubRange.scale); +} + + +inline uint RangeCoder::GetCurrentShiftCount(uint SHIFT) +{ + return (code-low)/(range >>= SHIFT); +} + + +inline void RangeCoder::Decode() +{ + low += range*SubRange.LowCount; + range *= SubRange.HighCount-SubRange.LowCount; +} diff --git a/reference/unrar/coder.hpp b/reference/unrar/coder.hpp new file mode 100644 index 00000000..7b36ff21 --- /dev/null +++ b/reference/unrar/coder.hpp @@ -0,0 +1,23 @@ +/**************************************************************************** + * Contents: 'Carryless rangecoder' by Dmitry Subbotin * + ****************************************************************************/ + + +class RangeCoder +{ + public: + void InitDecoder(Unpack *UnpackRead); + inline int GetCurrentCount(); + inline uint GetCurrentShiftCount(uint SHIFT); + inline void Decode(); + inline void PutChar(unsigned int c); + inline unsigned int GetChar(); + + uint low, code, range; + struct SUBRANGE + { + uint LowCount, HighCount, scale; + } SubRange; + + Unpack *UnpackRead; +}; diff --git a/reference/unrar/compress.hpp b/reference/unrar/compress.hpp new file mode 100644 index 00000000..8e4f1ed3 --- /dev/null +++ b/reference/unrar/compress.hpp @@ -0,0 +1,50 @@ +#ifndef _RAR_COMPRESS_ +#define _RAR_COMPRESS_ + +// Combine pack and unpack constants to class to avoid polluting global +// namespace with numerous short names. +class PackDef +{ + public: + static const uint MAX_LZ_MATCH = 0x1001; + static const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. + static const uint LOW_DIST_REP_COUNT = 16; + + static const uint NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ + static const uint DC = 64; + static const uint LDC = 16; + static const uint RC = 44; + static const uint HUFF_TABLE_SIZE = NC + DC + RC + LDC; + static const uint BC = 20; + + static const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ + static const uint DC30 = 60; + static const uint LDC30 = 17; + static const uint RC30 = 28; + static const uint BC30 = 20; + static const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30; + + static const uint NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ + static const uint DC20 = 48; + static const uint RC20 = 28; + static const uint BC20 = 19; + static const uint MC20 = 257; + + // Largest alphabet size among all values listed above. + static const uint LARGEST_TABLE_SIZE = 306; + + enum { + CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, + CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA + }; +}; + + +enum FilterType { + // These values must not be changed, because we use them directly + // in RAR5 compression and decompression code. + FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM, + FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE +}; + +#endif diff --git a/reference/unrar/consio.cpp b/reference/unrar/consio.cpp new file mode 100644 index 00000000..2f71a991 --- /dev/null +++ b/reference/unrar/consio.cpp @@ -0,0 +1,352 @@ +#include "rar.hpp" +#include "log.cpp" + +static MESSAGE_TYPE MsgStream=MSG_STDOUT; +static RAR_CHARSET RedirectCharset=RCH_DEFAULT; + +const int MaxMsgSize=2*NM+2048; + +static bool StdoutRedirected=false,StderrRedirected=false,StdinRedirected=false; + +#ifdef _WIN_ALL +static bool IsRedirected(DWORD nStdHandle) +{ + HANDLE hStd=GetStdHandle(nStdHandle); + DWORD Mode; + return GetFileType(hStd)!=FILE_TYPE_CHAR || GetConsoleMode(hStd,&Mode)==0; +} +#endif + + +void InitConsole() +{ +#ifdef _WIN_ALL + // We want messages like file names or progress percent to be printed + // immediately. Use only in Windows, in Unix they can cause wprintf %ls + // to fail with non-English strings. + setbuf(stdout,NULL); + setbuf(stderr,NULL); + + // Detect if output is redirected and set output mode properly. + // We do not want to send Unicode output to files and especially to pipes + // like '|more', which cannot handle them correctly in Windows. + // In Unix console output is UTF-8 and it is handled correctly + // when redirecting, so no need to perform any adjustments. + StdoutRedirected=IsRedirected(STD_OUTPUT_HANDLE); + StderrRedirected=IsRedirected(STD_ERROR_HANDLE); + StdinRedirected=IsRedirected(STD_INPUT_HANDLE); +#ifdef _MSC_VER + if (!StdoutRedirected) + _setmode(_fileno(stdout), _O_U16TEXT); + if (!StderrRedirected) + _setmode(_fileno(stderr), _O_U16TEXT); +#endif +#elif defined(_UNIX) + StdoutRedirected=!isatty(fileno(stdout)); + StderrRedirected=!isatty(fileno(stderr)); + StdinRedirected=!isatty(fileno(stdin)); +#endif +} + + +void InitConsoleOptions(MESSAGE_TYPE MsgStream,RAR_CHARSET RedirectCharset) +{ + ::MsgStream=MsgStream; + ::RedirectCharset=RedirectCharset; +} + + +#ifndef SILENT +static void cvt_wprintf(FILE *dest,const wchar *fmt,va_list arglist) +{ + // This buffer is for format string only, not for entire output, + // so it can be short enough. + wchar fmtw[1024]; + PrintfPrepareFmt(fmt,fmtw,ASIZE(fmtw)); +#ifdef _WIN_ALL + safebuf wchar Msg[MaxMsgSize]; + if (dest==stdout && StdoutRedirected || dest==stderr && StderrRedirected) + { + HANDLE hOut=GetStdHandle(dest==stdout ? STD_OUTPUT_HANDLE:STD_ERROR_HANDLE); + vswprintf(Msg,ASIZE(Msg),fmtw,arglist); + DWORD Written; + if (RedirectCharset==RCH_UNICODE) + WriteFile(hOut,Msg,(DWORD)wcslen(Msg)*sizeof(*Msg),&Written,NULL); + else + { + // Avoid Unicode for redirect in Windows, it does not work with pipes. + safebuf char MsgA[MaxMsgSize]; + if (RedirectCharset==RCH_UTF8) + WideToUtf(Msg,MsgA,ASIZE(MsgA)); + else + WideToChar(Msg,MsgA,ASIZE(MsgA)); + if (RedirectCharset==RCH_DEFAULT || RedirectCharset==RCH_OEM) + CharToOemA(MsgA,MsgA); // Console tools like 'more' expect OEM encoding. + + // We already converted \n to \r\n above, so we use WriteFile instead + // of C library to avoid unnecessary additional conversion. + WriteFile(hOut,MsgA,(DWORD)strlen(MsgA),&Written,NULL); + } + return; + } + // MSVC2008 vfwprintf writes every character to console separately + // and it is too slow. We use direct WriteConsole call instead. + vswprintf(Msg,ASIZE(Msg),fmtw,arglist); + HANDLE hOut=GetStdHandle(dest==stderr ? STD_ERROR_HANDLE:STD_OUTPUT_HANDLE); + DWORD Written; + WriteConsole(hOut,Msg,(DWORD)wcslen(Msg),&Written,NULL); +#else + vfwprintf(dest,fmtw,arglist); + // We do not use setbuf(NULL) in Unix (see comments in InitConsole). + fflush(dest); +#endif +} + + +void mprintf(const wchar *fmt,...) +{ + if (MsgStream==MSG_NULL || MsgStream==MSG_ERRONLY) + return; + + fflush(stderr); // Ensure proper message order. + + va_list arglist; + va_start(arglist,fmt); + FILE *dest=MsgStream==MSG_STDERR ? stderr:stdout; + cvt_wprintf(dest,fmt,arglist); + va_end(arglist); +} +#endif + + +#ifndef SILENT +void eprintf(const wchar *fmt,...) +{ + if (MsgStream==MSG_NULL) + return; + + fflush(stdout); // Ensure proper message order. + + va_list arglist; + va_start(arglist,fmt); + cvt_wprintf(stderr,fmt,arglist); + va_end(arglist); +} +#endif + + +#ifndef SILENT +static void GetPasswordText(wchar *Str,uint MaxLength) +{ + if (MaxLength==0) + return; + if (StdinRedirected) + getwstr(Str,MaxLength); // Read from pipe or redirected file. + else + { +#ifdef _WIN_ALL + HANDLE hConIn=GetStdHandle(STD_INPUT_HANDLE); + HANDLE hConOut=GetStdHandle(STD_OUTPUT_HANDLE); + DWORD ConInMode,ConOutMode; + DWORD Read=0; + GetConsoleMode(hConIn,&ConInMode); + GetConsoleMode(hConOut,&ConOutMode); + SetConsoleMode(hConIn,ENABLE_LINE_INPUT); + SetConsoleMode(hConOut,ENABLE_PROCESSED_OUTPUT|ENABLE_WRAP_AT_EOL_OUTPUT); + + ReadConsole(hConIn,Str,MaxLength-1,&Read,NULL); + Str[Read]=0; + SetConsoleMode(hConIn,ConInMode); + SetConsoleMode(hConOut,ConOutMode); +#else + char StrA[MAXPASSWORD]; +#if defined(_EMX) || defined (__VMS) + fgets(StrA,ASIZE(StrA)-1,stdin); +#elif defined(__sun) + strncpyz(StrA,getpassphrase(""),ASIZE(StrA)); +#else + strncpyz(StrA,getpass(""),ASIZE(StrA)); +#endif + CharToWide(StrA,Str,MaxLength); + cleandata(StrA,sizeof(StrA)); +#endif + } + Str[MaxLength-1]=0; + RemoveLF(Str); +} +#endif + + +#ifndef SILENT +bool GetConsolePassword(UIPASSWORD_TYPE Type,const wchar *FileName,SecPassword *Password) +{ + if (!StdinRedirected) + uiAlarm(UIALARM_QUESTION); + + while (true) + { + if (!StdinRedirected) + if (Type==UIPASSWORD_GLOBAL) + eprintf(L"\n%s: ",St(MAskPsw)); + else + eprintf(St(MAskPswFor),FileName); + + wchar PlainPsw[MAXPASSWORD]; + GetPasswordText(PlainPsw,ASIZE(PlainPsw)); + if (*PlainPsw==0 && Type==UIPASSWORD_GLOBAL) + return false; + if (!StdinRedirected && Type==UIPASSWORD_GLOBAL) + { + eprintf(St(MReAskPsw)); + wchar CmpStr[MAXPASSWORD]; + GetPasswordText(CmpStr,ASIZE(CmpStr)); + if (*CmpStr==0 || wcscmp(PlainPsw,CmpStr)!=0) + { + eprintf(St(MNotMatchPsw)); + cleandata(PlainPsw,sizeof(PlainPsw)); + cleandata(CmpStr,sizeof(CmpStr)); + continue; + } + cleandata(CmpStr,sizeof(CmpStr)); + } + Password->Set(PlainPsw); + cleandata(PlainPsw,sizeof(PlainPsw)); + break; + } + return true; +} +#endif + + +#ifndef SILENT +bool getwstr(wchar *str,size_t n) +{ + // Print buffered prompt title function before waiting for input. + fflush(stderr); + + *str=0; +#if defined(_WIN_ALL) + // fgetws does not work well with non-English text in Windows, + // so we do not use it. + if (StdinRedirected) // ReadConsole does not work if redirected. + { + // fgets does not work well with pipes in Windows in our test. + // Let's use files. + Array StrA(n*4); // Up to 4 UTF-8 characters per wchar_t. + File SrcFile; + SrcFile.SetHandleType(FILE_HANDLESTD); + int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1); + if (ReadSize<=0) + { + // Looks like stdin is a null device. We can enter to infinite loop + // calling Ask(), so let's better exit. + ErrHandler.Exit(RARX_USERBREAK); + } + StrA[ReadSize]=0; + CharToWide(&StrA[0],str,n); + cleandata(&StrA[0],StrA.Size()); // We can use this function to enter passwords. + } + else + { + DWORD ReadSize=0; + if (ReadConsole(GetStdHandle(STD_INPUT_HANDLE),str,DWORD(n-1),&ReadSize,NULL)==0) + return false; + str[ReadSize]=0; + } +#else + if (fgetws(str,n,stdin)==NULL) + ErrHandler.Exit(RARX_USERBREAK); // Avoid infinite Ask() loop. +#endif + RemoveLF(str); + return true; +} +#endif + + +#ifndef SILENT +// We allow this function to return 0 in case of invalid input, +// because it might be convenient to press Enter to some not dangerous +// prompts like "insert disk with next volume". We should call this function +// again in case of 0 in dangerous prompt such as overwriting file. +int Ask(const wchar *AskStr) +{ + uiAlarm(UIALARM_QUESTION); + + const int MaxItems=10; + wchar Item[MaxItems][40]; + int ItemKeyPos[MaxItems],NumItems=0; + + for (const wchar *NextItem=AskStr;NextItem!=NULL;NextItem=wcschr(NextItem+1,'_')) + { + wchar *CurItem=Item[NumItems]; + wcsncpyz(CurItem,NextItem+1,ASIZE(Item[0])); + wchar *EndItem=wcschr(CurItem,'_'); + if (EndItem!=NULL) + *EndItem=0; + int KeyPos=0,CurKey; + while ((CurKey=CurItem[KeyPos])!=0) + { + bool Found=false; + for (int I=0;I4 ? L"\n":L" "):L", "); + int KeyPos=ItemKeyPos[I]; + for (int J=0;J[{key};"{string}"p used to redefine + // a keyboard key on some terminals. + if (Data[J]=='\"') + return true; + if (!IsDigit(Data[J]) && Data[J]!=';') + break; + } + return false; +} + + +void OutComment(const wchar *Comment,size_t Size) +{ + if (IsCommentUnsafe(Comment,Size)) + return; + const size_t MaxOutSize=0x400; + for (size_t I=0;I>1)^0xEDB88320 : (C>>1); + CRCTab[I]=C; + } +} + + +static void InitTables() +{ + InitCRC32(crc_tables[0]); + + for (uint I=0;I<256;I++) // Build additional lookup tables. + { + uint C=crc_tables[0][I]; + for (uint J=1;J<8;J++) + { + C=crc_tables[0][(byte)C]^(C>>8); + crc_tables[J][I]=C; + } + } +} + + +struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32; + +uint CRC32(uint StartCRC,const void *Addr,size_t Size) +{ + byte *Data=(byte *)Addr; + + // Align Data to 8 for better performance. + for (;Size>0 && ((size_t)Data & 7);Size--,Data++) + StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + for (;Size>=8;Size-=8,Data+=8) + { +#ifdef BIG_ENDIAN + StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24); + uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24); +#else + StartCRC ^= *(uint32 *) Data; + uint NextData = *(uint32 *) (Data +4); +#endif + StartCRC = crc_tables[7][(byte) StartCRC ] ^ + crc_tables[6][(byte)(StartCRC >> 8) ] ^ + crc_tables[5][(byte)(StartCRC >> 16)] ^ + crc_tables[4][(byte)(StartCRC >> 24)] ^ + crc_tables[3][(byte) NextData ] ^ + crc_tables[2][(byte)(NextData >>8 ) ] ^ + crc_tables[1][(byte)(NextData >> 16)] ^ + crc_tables[0][(byte)(NextData >> 24)]; + } + + for (;Size>0;Size--,Data++) // Process left data. + StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8); + + return StartCRC; +} + + +#ifndef SFX_MODULE +// For RAR 1.4 archives in case somebody still has them. +ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size) +{ + byte *Data=(byte *)Addr; + for (size_t I=0;I>15))&0xffff; + } + return StartCRC; +} +#endif + + diff --git a/reference/unrar/crc.hpp b/reference/unrar/crc.hpp new file mode 100644 index 00000000..d8fea281 --- /dev/null +++ b/reference/unrar/crc.hpp @@ -0,0 +1,15 @@ +#ifndef _RAR_CRC_ +#define _RAR_CRC_ + +// This function is only to intialize external CRC tables. We do not need to +// call it before calculating CRC32. +void InitCRC32(uint *CRCTab); + +uint CRC32(uint StartCRC,const void *Addr,size_t Size); + +#ifndef SFX_MODULE +ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size); +#endif + + +#endif diff --git a/reference/unrar/crypt.cpp b/reference/unrar/crypt.cpp new file mode 100644 index 00000000..fc2126d4 --- /dev/null +++ b/reference/unrar/crypt.cpp @@ -0,0 +1,134 @@ +#include "rar.hpp" + +#ifndef SFX_MODULE +#include "crypt1.cpp" +#include "crypt2.cpp" +#endif +#include "crypt3.cpp" +#include "crypt5.cpp" + + +CryptData::CryptData() +{ + Method=CRYPT_NONE; + memset(KDF3Cache,0,sizeof(KDF3Cache)); + memset(KDF5Cache,0,sizeof(KDF5Cache)); + KDF3CachePos=0; + KDF5CachePos=0; + memset(CRCTab,0,sizeof(CRCTab)); +} + + +CryptData::~CryptData() +{ + cleandata(KDF3Cache,sizeof(KDF3Cache)); + cleandata(KDF5Cache,sizeof(KDF5Cache)); +} + + + + +void CryptData::DecryptBlock(byte *Buf,size_t Size) +{ + switch(Method) + { +#ifndef SFX_MODULE + case CRYPT_RAR13: + Decrypt13(Buf,Size); + break; + case CRYPT_RAR15: + Crypt15(Buf,Size); + break; + case CRYPT_RAR20: + for (size_t I=0;IIsSet() || Method==CRYPT_NONE) + return false; + + CryptData::Method=Method; + + wchar PwdW[MAXPASSWORD]; + Password->Get(PwdW,ASIZE(PwdW)); + char PwdA[MAXPASSWORD]; + WideToChar(PwdW,PwdA,ASIZE(PwdA)); + + switch(Method) + { +#ifndef SFX_MODULE + case CRYPT_RAR13: + SetKey13(PwdA); + break; + case CRYPT_RAR15: + SetKey15(PwdA); + break; + case CRYPT_RAR20: + SetKey20(PwdA); + break; +#endif + case CRYPT_RAR30: + SetKey30(Encrypt,Password,PwdW,Salt); + break; + case CRYPT_RAR50: + SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck); + break; + } + cleandata(PwdA,sizeof(PwdA)); + cleandata(PwdW,sizeof(PwdW)); + return true; +} + + +// Use the current system time to additionally randomize data. +static void TimeRandomize(byte *RndBuf,size_t BufSize) +{ + static uint Count=0; + RarTime CurTime; + CurTime.SetCurrentTime(); + uint64 Random=CurTime.GetWin()+clock(); + for (size_t I=0;I> ( (I & 7) * 8 )); + RndBuf[I]=byte( (RndByte ^ I) + Count++); + } +} + + + + +// Fill buffer with random data. +void GetRnd(byte *RndBuf,size_t BufSize) +{ + bool Success=false; +#if defined(_WIN_ALL) + HCRYPTPROV hProvider = 0; + if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE; + CryptReleaseContext(hProvider, 0); + } +#elif defined(_UNIX) + FILE *rndf = fopen("/dev/urandom", "r"); + if (rndf!=NULL) + { + Success=fread(RndBuf, BufSize, 1, rndf) == BufSize; + fclose(rndf); + } +#endif + // We use this code only as the last resort if code above failed. + if (!Success) + TimeRandomize(RndBuf,BufSize); +} diff --git a/reference/unrar/crypt.hpp b/reference/unrar/crypt.hpp new file mode 100644 index 00000000..f6382ef5 --- /dev/null +++ b/reference/unrar/crypt.hpp @@ -0,0 +1,101 @@ +#ifndef _RAR_CRYPT_ +#define _RAR_CRYPT_ + + +enum CRYPT_METHOD { + CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50 +}; + +#define SIZE_SALT50 16 +#define SIZE_SALT30 8 +#define SIZE_INITV 16 +#define SIZE_PSWCHECK 8 +#define SIZE_PSWCHECK_CSUM 4 + +#define CRYPT_BLOCK_SIZE 16 +#define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf + +#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count. +#define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count. +#define CRYPT_VERSION 0 // Supported encryption version. + + +class CryptData +{ + struct KDF5CacheItem + { + SecPassword Pwd; + byte Salt[SIZE_SALT50]; + byte Key[32]; + uint Lg2Count; // Log2 of PBKDF2 repetition count. + byte PswCheckValue[SHA256_DIGEST_SIZE]; + byte HashKeyValue[SHA256_DIGEST_SIZE]; + }; + + struct KDF3CacheItem + { + SecPassword Pwd; + byte Salt[SIZE_SALT30]; + byte Key[16]; + byte Init[16]; + bool SaltPresent; + }; + + + private: + void SetKey13(const char *Password); + void Decrypt13(byte *Data,size_t Count); + + void SetKey15(const char *Password); + void Crypt15(byte *Data,size_t Count); + + void SetKey20(const char *Password); + void Swap20(byte *Ch1,byte *Ch2); + void UpdKeys20(byte *Buf); + void EncryptBlock20(byte *Buf); + void DecryptBlock20(byte *Buf); + + void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt); + void SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck); + + KDF3CacheItem KDF3Cache[4]; + uint KDF3CachePos; + + KDF5CacheItem KDF5Cache[4]; + uint KDF5CachePos; + + CRYPT_METHOD Method; + + Rijndael rin; + + uint CRCTab[256]; // For RAR 1.5 and RAR 2.0 encryption. + + byte SubstTable20[256]; + uint Key20[4]; + + byte Key13[3]; + ushort Key15[4]; + public: + CryptData(); + ~CryptData(); + bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password, + const byte *Salt,const byte *InitV,uint Lg2Cnt, + byte *HashKey,byte *PswCheck); + void SetAV15Encryption(); + void SetCmt13Encryption(); + void EncryptBlock(byte *Buf,size_t Size); + void DecryptBlock(byte *Buf,size_t Size); + static void SetSalt(byte *Salt,size_t SaltSize); +}; + +void GetRnd(byte *RndBuf,size_t BufSize); + +void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, + size_t DataLength,byte *ResDigest); +void pbkdf2(const byte *pass, size_t pass_len, const byte *salt, + size_t salt_len,byte *key, byte *Value1, byte *Value2, + uint rounds); + +void ConvertHashToMAC(HashValue *Value,byte *Key); + +#endif diff --git a/reference/unrar/crypt1.cpp b/reference/unrar/crypt1.cpp new file mode 100644 index 00000000..14263937 --- /dev/null +++ b/reference/unrar/crypt1.cpp @@ -0,0 +1,79 @@ +extern uint CRCTab[256]; + +void CryptData::SetKey13(const char *Password) +{ + Key13[0]=Key13[1]=Key13[2]=0; + for (size_t I=0;Password[I]!=0;I++) + { + byte P=Password[I]; + Key13[0]+=P; + Key13[1]^=P; + Key13[2]+=P; + Key13[2]=(byte)rotls(Key13[2],1,8); + } +} + + +void CryptData::SetKey15(const char *Password) +{ + InitCRC32(CRCTab); + uint PswCRC=CRC32(0xffffffff,Password,strlen(Password)); + Key15[0]=PswCRC&0xffff; + Key15[1]=(PswCRC>>16)&0xffff; + Key15[2]=Key15[3]=0; + for (size_t I=0;Password[I]!=0;I++) + { + byte P=Password[I]; + Key15[2]^=P^CRCTab[P]; + Key15[3]+=P+(CRCTab[P]>>16); + } +} + + +void CryptData::SetAV15Encryption() +{ + InitCRC32(CRCTab); + Method=CRYPT_RAR15; + Key15[0]=0x4765; + Key15[1]=0x9021; + Key15[2]=0x7382; + Key15[3]=0x5215; +} + + +void CryptData::SetCmt13Encryption() +{ + Method=CRYPT_RAR13; + Key13[0]=0; + Key13[1]=7; + Key13[2]=77; +} + + +void CryptData::Decrypt13(byte *Data,size_t Count) +{ + while (Count--) + { + Key13[1]+=Key13[2]; + Key13[0]+=Key13[1]; + *Data-=Key13[0]; + Data++; + } +} + + +void CryptData::Crypt15(byte *Data,size_t Count) +{ + while (Count--) + { + Key15[0]+=0x1234; + Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1]; + Key15[2]-=CRCTab[(Key15[0] & 0x1fe)>>1]>>16; + Key15[0]^=Key15[2]; + Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1]; + Key15[3]=rotrs(Key15[3]&0xffff,1,16); + Key15[0]^=Key15[3]; + *Data^=(byte)(Key15[0]>>8); + Data++; + } +} diff --git a/reference/unrar/crypt2.cpp b/reference/unrar/crypt2.cpp new file mode 100644 index 00000000..5fa4a97d --- /dev/null +++ b/reference/unrar/crypt2.cpp @@ -0,0 +1,133 @@ +#define NROUNDS 32 + +#define substLong(t) ( (uint)SubstTable20[(uint)t&255] | \ + ((uint)SubstTable20[(int)(t>> 8)&255]<< 8) | \ + ((uint)SubstTable20[(int)(t>>16)&255]<<16) | \ + ((uint)SubstTable20[(int)(t>>24)&255]<<24) ) + + +static byte InitSubstTable20[256]={ + 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42, + 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137, + 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6, + 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235, + 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36, + 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251, + 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11, + 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51, + 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7, + 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80, + 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129, + 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10, + 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108, + 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225, + 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52, + 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84 +}; + + +void CryptData::SetKey20(const char *Password) +{ + InitCRC32(CRCTab); + + char Psw[MAXPASSWORD]; + strncpyz(Psw,Password,ASIZE(Psw)); // We'll need to modify it below. + size_t PswLength=strlen(Psw); + + Key20[0]=0xD3A3B879L; + Key20[1]=0x3F6D12F7L; + Key20[2]=0x7515A235L; + Key20[3]=0xA4E7F123L; + + memcpy(SubstTable20,InitSubstTable20,sizeof(SubstTable20)); + for (uint J=0;J<256;J++) + for (size_t I=0;I=0;I--) + { + T=((C+rotls(D,11,32))^Key20[I&3]); + TA=A^substLong(T); + T=((D^rotls(C,17,32))+Key20[I&3]); + TB=B^substLong(T); + A=C; + B=D; + C=TA; + D=TB; + } + RawPut4(C^Key20[0],Buf+0); + RawPut4(D^Key20[1],Buf+4); + RawPut4(A^Key20[2],Buf+8); + RawPut4(B^Key20[3],Buf+12); + UpdKeys20(InBuf); +} + + +void CryptData::UpdKeys20(byte *Buf) +{ + for (int I=0;I<16;I+=4) + { + Key20[0]^=CRCTab[Buf[I]]; + Key20[1]^=CRCTab[Buf[I+1]]; + Key20[2]^=CRCTab[Buf[I+2]]; + Key20[3]^=CRCTab[Buf[I+3]]; + } +} + + +void CryptData::Swap20(byte *Ch1,byte *Ch2) +{ + byte Ch=*Ch1; + *Ch1=*Ch2; + *Ch2=Ch; +} diff --git a/reference/unrar/crypt3.cpp b/reference/unrar/crypt3.cpp new file mode 100644 index 00000000..48406487 --- /dev/null +++ b/reference/unrar/crypt3.cpp @@ -0,0 +1,68 @@ +void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt) +{ + byte AESKey[16],AESInit[16]; + + bool Cached=false; + for (uint I=0;I>8); + PswNum[2]=(byte)(I>>16); + sha1_process(&c, PswNum, 3); + if (I%(HashRounds/16)==0) + { + sha1_context tempc=c; + uint32 digest[5]; + sha1_done( &tempc, digest ); + AESInit[I/(HashRounds/16)]=(byte)digest[4]; + } + } + uint32 digest[5]; + sha1_done( &c, digest ); + for (int I=0;I<4;I++) + for (int J=0;J<4;J++) + AESKey[I*4+J]=(byte)(digest[I]>>(J*8)); + + KDF3Cache[KDF3CachePos].Pwd=*Password; + if ((KDF3Cache[KDF3CachePos].SaltPresent=(Salt!=NULL))==true) + memcpy(KDF3Cache[KDF3CachePos].Salt,Salt,SIZE_SALT30); + memcpy(KDF3Cache[KDF3CachePos].Key,AESKey,sizeof(AESKey)); + SecHideData(KDF3Cache[KDF3CachePos].Key,sizeof(KDF3Cache[KDF3CachePos].Key),true,false); + memcpy(KDF3Cache[KDF3CachePos].Init,AESInit,sizeof(AESInit)); + KDF3CachePos=(KDF3CachePos+1)%ASIZE(KDF3Cache); + + cleandata(RawPsw,sizeof(RawPsw)); + } + rin.Init(Encrypt, AESKey, 128, AESInit); + cleandata(AESKey,sizeof(AESKey)); + cleandata(AESInit,sizeof(AESInit)); +} + diff --git a/reference/unrar/crypt5.cpp b/reference/unrar/crypt5.cpp new file mode 100644 index 00000000..7562469f --- /dev/null +++ b/reference/unrar/crypt5.cpp @@ -0,0 +1,233 @@ +static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data, + size_t DataLength,byte *ResDigest, + sha256_context *ICtxOpt,bool *SetIOpt, + sha256_context *RCtxOpt,bool *SetROpt) +{ + const size_t Sha256BlockSize=64; // As defined in RFC 4868. + + byte KeyHash[SHA256_DIGEST_SIZE]; + if (KeyLength > Sha256BlockSize) // Convert longer keys to key hash. + { + sha256_context KCtx; + sha256_init(&KCtx); + sha256_process(&KCtx, Key, KeyLength); + sha256_done(&KCtx, KeyHash); + + Key = KeyHash; + KeyLength = SHA256_DIGEST_SIZE; + } + + byte KeyBuf[Sha256BlockSize]; // Store the padded key here. + sha256_context ICtx; + + if (ICtxOpt!=NULL && *SetIOpt) + ICtx=*ICtxOpt; // Use already calculated first block context. + else + { + // This calculation is the same for all iterations with same password. + // So for PBKDF2 we can calculate it only for first block and then reuse + // to improve performance. + + for (size_t I = 0; I < KeyLength; I++) // Use 0x36 padding for inner digest. + KeyBuf[I] = Key[I] ^ 0x36; + for (size_t I = KeyLength; I < Sha256BlockSize; I++) + KeyBuf[I] = 0x36; + + sha256_init(&ICtx); + sha256_process(&ICtx, KeyBuf, Sha256BlockSize); // Hash padded key. + } + + if (ICtxOpt!=NULL && !*SetIOpt) // Store constant context for further reuse. + { + *ICtxOpt=ICtx; + *SetIOpt=true; + } + + sha256_process(&ICtx, Data, DataLength); // Hash data. + + byte IDig[SHA256_DIGEST_SIZE]; // Internal digest for padded key and data. + sha256_done(&ICtx, IDig); + + sha256_context RCtx; + + if (RCtxOpt!=NULL && *SetROpt) + RCtx=*RCtxOpt; // Use already calculated first block context. + else + { + // This calculation is the same for all iterations with same password. + // So for PBKDF2 we can calculate it only for first block and then reuse + // to improve performance. + + for (size_t I = 0; I < KeyLength; I++) // Use 0x5c for outer key padding. + KeyBuf[I] = Key[I] ^ 0x5c; + for (size_t I = KeyLength; I < Sha256BlockSize; I++) + KeyBuf[I] = 0x5c; + + sha256_init(&RCtx); + sha256_process(&RCtx, KeyBuf, Sha256BlockSize); // Hash padded key. + } + + if (RCtxOpt!=NULL && !*SetROpt) // Store constant context for further reuse. + { + *RCtxOpt=RCtx; + *SetROpt=true; + } + + sha256_process(&RCtx, IDig, SHA256_DIGEST_SIZE); // Hash internal digest. + + sha256_done(&RCtx, ResDigest); +} + + +// PBKDF2 for 32 byte key length. We generate the key for specified number +// of iteration count also as two supplementary values (key for checksums +// and password verification) for iterations+16 and iterations+32. +void pbkdf2(const byte *Pwd, size_t PwdLength, + const byte *Salt, size_t SaltLength, + byte *Key, byte *V1, byte *V2, uint Count) +{ + const size_t MaxSalt=64; + byte SaltData[MaxSalt+4]; + memcpy(SaltData, Salt, Min(SaltLength,MaxSalt)); + + SaltData[SaltLength + 0] = 0; // Salt concatenated to 1. + SaltData[SaltLength + 1] = 0; + SaltData[SaltLength + 2] = 0; + SaltData[SaltLength + 3] = 1; + + // First iteration: HMAC of password, salt and block index (1). + byte U1[SHA256_DIGEST_SIZE]; + hmac_sha256(Pwd, PwdLength, SaltData, SaltLength + 4, U1, NULL, NULL, NULL, NULL); + byte Fn[SHA256_DIGEST_SIZE]; // Current function value. + memcpy(Fn, U1, sizeof(Fn)); // Function at first iteration. + + uint CurCount[] = { Count-1, 16, 16 }; + byte *CurValue[] = { Key , V1, V2 }; + + sha256_context ICtxOpt,RCtxOpt; + bool SetIOpt=false,SetROpt=false; + + byte U2[SHA256_DIGEST_SIZE]; + for (uint I = 0; I < 3; I++) // For output key and 2 supplementary values. + { + for (uint J = 0; J < CurCount[I]; J++) + { + // U2 = PRF (P, U1). + hmac_sha256(Pwd, PwdLength, U1, sizeof(U1), U2, &ICtxOpt, &SetIOpt, &RCtxOpt, &SetROpt); + memcpy(U1, U2, sizeof(U1)); + for (uint K = 0; K < sizeof(Fn); K++) // Function ^= U. + Fn[K] ^= U1[K]; + } + memcpy(CurValue[I], Fn, SHA256_DIGEST_SIZE); + } + + cleandata(SaltData, sizeof(SaltData)); + cleandata(Fn, sizeof(Fn)); + cleandata(U1, sizeof(U1)); + cleandata(U2, sizeof(U2)); +} + + +void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW, + const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey, + byte *PswCheck) +{ + if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX) + return; + + byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE]; + bool Found=false; + for (uint I=0;ILg2Count==Lg2Cnt && Item->Pwd==*Password && + memcmp(Item->Salt,Salt,SIZE_SALT50)==0) + { + memcpy(Key,Item->Key,sizeof(Key)); + SecHideData(Key,sizeof(Key),false,false); + + memcpy(PswCheckValue,Item->PswCheckValue,sizeof(PswCheckValue)); + memcpy(HashKeyValue,Item->HashKeyValue,sizeof(HashKeyValue)); + Found=true; + break; + } + } + + if (!Found) + { + char PwdUtf[MAXPASSWORD*4]; + WideToUtf(PwdW,PwdUtf,ASIZE(PwdUtf)); + + pbkdf2((byte *)PwdUtf,strlen(PwdUtf),Salt,SIZE_SALT50,Key,HashKeyValue,PswCheckValue,(1<Lg2Count=Lg2Cnt; + Item->Pwd=*Password; + memcpy(Item->Salt,Salt,SIZE_SALT50); + memcpy(Item->Key,Key,sizeof(Item->Key)); + memcpy(Item->PswCheckValue,PswCheckValue,sizeof(PswCheckValue)); + memcpy(Item->HashKeyValue,HashKeyValue,sizeof(HashKeyValue)); + SecHideData(Item->Key,sizeof(Item->Key),true,false); + } + if (HashKey!=NULL) + memcpy(HashKey,HashKeyValue,SHA256_DIGEST_SIZE); + if (PswCheck!=NULL) + { + memset(PswCheck,0,SIZE_PSWCHECK); + for (uint I=0;IType==HASH_CRC32) + { + byte RawCRC[4]; + RawPut4(Value->CRC32,RawCRC); + byte Digest[SHA256_DIGEST_SIZE]; + hmac_sha256(Key,SHA256_DIGEST_SIZE,RawCRC,sizeof(RawCRC),Digest,NULL,NULL,NULL,NULL); + Value->CRC32=0; + for (uint I=0;ICRC32^=Digest[I] << ((I & 3) * 8); + } + if (Value->Type==HASH_BLAKE2) + { + byte Digest[BLAKE2_DIGEST_SIZE]; + hmac_sha256(Key,BLAKE2_DIGEST_SIZE,Value->Digest,sizeof(Value->Digest),Digest,NULL,NULL,NULL,NULL); + memcpy(Value->Digest,Digest,sizeof(Value->Digest)); + } +} + + +#if 0 +static void TestPBKDF2(); +struct TestKDF {TestKDF() {TestPBKDF2();exit(0);}} GlobalTestKDF; + +void TestPBKDF2() // Test PBKDF2 HMAC-SHA256 +{ + byte Key[32],V1[32],V2[32]; + + pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 1); + byte Res1[32]={0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b }; + mprintf(L"\nPBKDF2 test1: %s", memcmp(Key,Res1,32)==0 ? L"OK":L"Failed"); + + pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 4096); + byte Res2[32]={0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a }; + mprintf(L"\nPBKDF2 test2: %s", memcmp(Key,Res2,32)==0 ? L"OK":L"Failed"); + + pbkdf2((byte *)"just some long string pretending to be a password", 49, (byte *)"salt, salt, salt, a lot of salt", 31, Key, V1, V2, 65536); + byte Res3[32]={0x08, 0x0f, 0xa3, 0x1d, 0x42, 0x2d, 0xb0, 0x47, 0x83, 0x9b, 0xce, 0x3a, 0x3b, 0xce, 0x49, 0x51, 0xe2, 0x62, 0xb9, 0xff, 0x76, 0x2f, 0x57, 0xe9, 0xc4, 0x71, 0x96, 0xce, 0x4b, 0x6b, 0x6e, 0xbf}; + mprintf(L"\nPBKDF2 test3: %s", memcmp(Key,Res3,32)==0 ? L"OK":L"Failed"); +} +#endif diff --git a/reference/unrar/dll.cpp b/reference/unrar/dll.cpp new file mode 100644 index 00000000..d12c0b17 --- /dev/null +++ b/reference/unrar/dll.cpp @@ -0,0 +1,474 @@ +#include "rar.hpp" + +static int RarErrorToDll(RAR_EXIT ErrCode); + +struct DataSet +{ + CommandData Cmd; + Archive Arc; + CmdExtract Extract; + int OpenMode; + int HeaderSize; + + DataSet():Arc(&Cmd),Extract(&Cmd) {}; +}; + + +HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r) +{ + RAROpenArchiveDataEx rx; + memset(&rx,0,sizeof(rx)); + rx.ArcName=r->ArcName; + rx.OpenMode=r->OpenMode; + rx.CmtBuf=r->CmtBuf; + rx.CmtBufSize=r->CmtBufSize; + HANDLE hArc=RAROpenArchiveEx(&rx); + r->OpenResult=rx.OpenResult; + r->CmtSize=rx.CmtSize; + r->CmtState=rx.CmtState; + return hArc; +} + + +HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r) +{ + DataSet *Data=NULL; + try + { + r->OpenResult=0; + Data=new DataSet; + Data->Cmd.DllError=0; + Data->OpenMode=r->OpenMode; + Data->Cmd.FileArgs.AddString(L"*"); + + char AnsiArcName[NM]; + *AnsiArcName=0; + if (r->ArcName!=NULL) + { + strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName)); +#ifdef _WIN_ALL + if (!AreFileApisANSI()) + { + OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName)); + AnsiArcName[ASIZE(AnsiArcName)-1]=0; + } +#endif + } + + wchar ArcName[NM]; + GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName)); + + Data->Cmd.AddArcName(ArcName); + Data->Cmd.Overwrite=OVERWRITE_ALL; + Data->Cmd.VersionControl=1; + + Data->Cmd.Callback=r->Callback; + Data->Cmd.UserData=r->UserData; + + // Open shared mode is added by request of dll users, who need to + // browse and unpack archives while downloading. + Data->Cmd.OpenShared = true; + if (!Data->Arc.Open(ArcName,FMF_OPENSHARED)) + { + r->OpenResult=ERAR_EOPEN; + delete Data; + return NULL; + } + if (!Data->Arc.IsArchive(true)) + { + if (Data->Cmd.DllError!=0) + r->OpenResult=Data->Cmd.DllError; + else + { + RAR_EXIT ErrCode=ErrHandler.GetErrorCode(); + if (ErrCode!=RARX_SUCCESS && ErrCode!=RARX_WARNING) + r->OpenResult=RarErrorToDll(ErrCode); + else + r->OpenResult=ERAR_BAD_ARCHIVE; + } + delete Data; + return NULL; + } + r->Flags=0; + + if (Data->Arc.Volume) + r->Flags|=0x01; + if (Data->Arc.Locked) + r->Flags|=0x04; + if (Data->Arc.Solid) + r->Flags|=0x08; + if (Data->Arc.NewNumbering) + r->Flags|=0x10; + if (Data->Arc.Signed) + r->Flags|=0x20; + if (Data->Arc.Protected) + r->Flags|=0x40; + if (Data->Arc.Encrypted) + r->Flags|=0x80; + if (Data->Arc.FirstVolume) + r->Flags|=0x100; + + Array CmtDataW; + if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW)) + { + Array CmtData(CmtDataW.Size()*4+1); + memset(&CmtData[0],0,CmtData.Size()); + WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1); + size_t Size=strlen(&CmtData[0])+1; + + r->Flags|=2; + r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; + r->CmtSize=(uint)Min(Size,r->CmtBufSize); + memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1); + if (Size<=r->CmtBufSize) + r->CmtBuf[r->CmtSize-1]=0; + } + else + r->CmtState=r->CmtSize=0; + Data->Extract.ExtractArchiveInit(Data->Arc); + return (HANDLE)Data; + } + catch (RAR_EXIT ErrCode) + { + if (Data!=NULL && Data->Cmd.DllError!=0) + r->OpenResult=Data->Cmd.DllError; + else + r->OpenResult=RarErrorToDll(ErrCode); + if (Data != NULL) + delete Data; + return NULL; + } + catch (std::bad_alloc&) // Catch 'new' exception. + { + r->OpenResult=ERAR_NO_MEMORY; + if (Data != NULL) + delete Data; + } + return NULL; // To make compilers happy. +} + + +int PASCAL RARCloseArchive(HANDLE hArcData) +{ + DataSet *Data=(DataSet *)hArcData; + bool Success=Data==NULL ? false:Data->Arc.Close(); + delete Data; + return Success ? ERAR_SUCCESS : ERAR_ECLOSE; +} + + +int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D) +{ + struct RARHeaderDataEx X; + memset(&X,0,sizeof(X)); + + int Code=RARReadHeaderEx(hArcData,&X); + + strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName)); + strncpyz(D->FileName,X.FileName,ASIZE(D->FileName)); + D->Flags=X.Flags; + D->PackSize=X.PackSize; + D->UnpSize=X.UnpSize; + D->HostOS=X.HostOS; + D->FileCRC=X.FileCRC; + D->FileTime=X.FileTime; + D->UnpVer=X.UnpVer; + D->Method=X.Method; + D->FileAttr=X.FileAttr; + D->CmtSize=0; + D->CmtState=0; + + return Code; +} + + +int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D) +{ + DataSet *Data=(DataSet *)hArcData; + try + { + if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0) + { + if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC && + Data->Arc.EndArcHead.NextVolume) + if (MergeArchive(Data->Arc,NULL,false,'L')) + { + Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); + return RARReadHeaderEx(hArcData,D); + } + else + return ERAR_EOPEN; + + if (Data->Arc.BrokenHeader) + return ERAR_BAD_DATA; + + // Might be necessary if RARSetPassword is still called instead of + // open callback for RAR5 archives and if password is invalid. + if (Data->Arc.FailedHeaderDecryption) + return ERAR_BAD_PASSWORD; + + return ERAR_END_ARCHIVE; + } + FileHeader *hd=&Data->Arc.FileHead; + if (Data->OpenMode==RAR_OM_LIST && hd->SplitBefore) + { + int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL); + if (Code==0) + return RARReadHeaderEx(hArcData,D); + else + return Code; + } + wcsncpy(D->ArcNameW,Data->Arc.FileName,ASIZE(D->ArcNameW)); + WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName)); + + wcsncpy(D->FileNameW,hd->FileName,ASIZE(D->FileNameW)); + WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName)); +#ifdef _WIN_ALL + CharToOemA(D->FileName,D->FileName); +#endif + + D->Flags=0; + if (hd->SplitBefore) + D->Flags|=RHDF_SPLITBEFORE; + if (hd->SplitAfter) + D->Flags|=RHDF_SPLITAFTER; + if (hd->Encrypted) + D->Flags|=RHDF_ENCRYPTED; + if (hd->Solid) + D->Flags|=RHDF_SOLID; + if (hd->Dir) + D->Flags|=RHDF_DIRECTORY; + + D->PackSize=uint(hd->PackSize & 0xffffffff); + D->PackSizeHigh=uint(hd->PackSize>>32); + D->UnpSize=uint(hd->UnpSize & 0xffffffff); + D->UnpSizeHigh=uint(hd->UnpSize>>32); + D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX; + if (Data->Arc.Format==RARFMT50) + D->UnpVer=Data->Arc.FileHead.UnpVer==0 ? 50 : 200; // If it is not 0, just set it to something big. + else + D->UnpVer=Data->Arc.FileHead.UnpVer; + D->FileCRC=hd->FileHash.CRC32; + D->FileTime=hd->mtime.GetDos(); + + uint64 MRaw=hd->mtime.GetWin(); + D->MtimeLow=(uint)MRaw; + D->MtimeHigh=(uint)(MRaw>>32); + uint64 CRaw=hd->ctime.GetWin(); + D->CtimeLow=(uint)CRaw; + D->CtimeHigh=(uint)(CRaw>>32); + uint64 ARaw=hd->atime.GetWin(); + D->AtimeLow=(uint)ARaw; + D->AtimeHigh=(uint)(ARaw>>32); + + D->Method=hd->Method+0x30; + D->FileAttr=hd->FileAttr; + D->CmtSize=0; + D->CmtState=0; + + D->DictSize=uint(hd->WinSize/1024); + + switch (hd->FileHash.Type) + { + case HASH_RAR14: + case HASH_CRC32: + D->HashType=RAR_HASH_CRC32; + break; + case HASH_BLAKE2: + D->HashType=RAR_HASH_BLAKE2; + memcpy(D->Hash,hd->FileHash.Digest,BLAKE2_DIGEST_SIZE); + break; + default: + D->HashType=RAR_HASH_NONE; + break; + } + + D->RedirType=hd->RedirType; + // RedirNameSize sanity check is useful in case some developer + // did not initialize Reserved area with 0 as required in docs. + // We have taken 'Redir*' fields from Reserved area. We may remove + // this RedirNameSize check sometimes later. + if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL && + D->RedirNameSize>0 && D->RedirNameSize<100000) + wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize); + D->DirTarget=hd->DirTarget; + } + catch (RAR_EXIT ErrCode) + { + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); + } + return ERAR_SUCCESS; +} + + +int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName,wchar *DestPathW,wchar *DestNameW) +{ + DataSet *Data=(DataSet *)hArcData; + try + { + Data->Cmd.DllError=0; + if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT || + Operation==RAR_SKIP && !Data->Arc.Solid) + { + if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE && + Data->Arc.FileHead.SplitAfter) + if (MergeArchive(Data->Arc,NULL,false,'L')) + { + Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); + return ERAR_SUCCESS; + } + else + return ERAR_EOPEN; + Data->Arc.SeekToNext(); + } + else + { + Data->Cmd.DllOpMode=Operation; + + *Data->Cmd.ExtrPath=0; + *Data->Cmd.DllDestName=0; + + if (DestPath!=NULL) + { + char ExtrPathA[NM]; + strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2); +#ifdef _WIN_ALL + // We must not apply OemToCharBuffA directly to DestPath, + // because we do not know DestPath length and OemToCharBuffA + // does not stop at 0. + OemToCharA(ExtrPathA,ExtrPathA); +#endif + CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); + AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); + } + if (DestName!=NULL) + { + char DestNameA[NM]; + strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2); +#ifdef _WIN_ALL + // We must not apply OemToCharBuffA directly to DestName, + // because we do not know DestName length and OemToCharBuffA + // does not stop at 0. + OemToCharA(DestNameA,DestNameA); +#endif + CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName)); + } + + if (DestPathW!=NULL) + { + wcsncpy(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath)); + AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); + } + + if (DestNameW!=NULL) + wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName)); + + wcscpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T"); + Data->Cmd.Test=Operation!=RAR_EXTRACT; + bool Repeat=false; + Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); + + // Now we process extra file information if any. + // + // Archive can be closed if we process volumes, next volume is missing + // and current one is already removed or deleted. So we need to check + // if archive is still open to avoid calling file operations on + // the invalid file handle. Some of our file operations like Seek() + // process such invalid handle correctly, some not. + while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 && + Data->Arc.GetHeaderType()==HEAD_SERVICE) + { + Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); + Data->Arc.SeekToNext(); + } + Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); + } + } + catch (std::bad_alloc&) + { + return ERAR_NO_MEMORY; + } + catch (RAR_EXIT ErrCode) + { + return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); + } + return Data->Cmd.DllError; +} + + +int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName) +{ + return(ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL)); +} + + +int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName) +{ + return(ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName)); +} + + +void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc) +{ + DataSet *Data=(DataSet *)hArcData; + Data->Cmd.ChangeVolProc=ChangeVolProc; +} + + +void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData) +{ + DataSet *Data=(DataSet *)hArcData; + Data->Cmd.Callback=Callback; + Data->Cmd.UserData=UserData; +} + + +void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc) +{ + DataSet *Data=(DataSet *)hArcData; + Data->Cmd.ProcessDataProc=ProcessDataProc; +} + + +#ifndef RAR_NOCRYPT +void PASCAL RARSetPassword(HANDLE hArcData,char *Password) +{ + DataSet *Data=(DataSet *)hArcData; + wchar PasswordW[MAXPASSWORD]; + GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW)); + Data->Cmd.Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); +} +#endif + + +int PASCAL RARGetDllVersion() +{ + return RAR_DLL_VERSION; +} + + +static int RarErrorToDll(RAR_EXIT ErrCode) +{ + switch(ErrCode) + { + case RARX_FATAL: + return ERAR_EREAD; + case RARX_CRC: + return ERAR_BAD_DATA; + case RARX_WRITE: + return ERAR_EWRITE; + case RARX_OPEN: + return ERAR_EOPEN; + case RARX_CREATE: + return ERAR_ECREATE; + case RARX_MEMORY: + return ERAR_NO_MEMORY; + case RARX_BADPWD: + return ERAR_BAD_PASSWORD; + case RARX_SUCCESS: + return ERAR_SUCCESS; // 0. + default: + return ERAR_UNKNOWN; + } +} diff --git a/reference/unrar/dll.def b/reference/unrar/dll.def new file mode 100644 index 00000000..54420e2a --- /dev/null +++ b/reference/unrar/dll.def @@ -0,0 +1,12 @@ +EXPORTS + RAROpenArchive + RAROpenArchiveEx + RARCloseArchive + RARReadHeader + RARReadHeaderEx + RARProcessFile + RARSetCallback + RARSetChangeVolProc + RARSetProcessDataProc + RARSetPassword + RARGetDllVersion diff --git a/reference/unrar/dll.hpp b/reference/unrar/dll.hpp new file mode 100644 index 00000000..7f829066 --- /dev/null +++ b/reference/unrar/dll.hpp @@ -0,0 +1,185 @@ +#ifndef _UNRAR_DLL_ +#define _UNRAR_DLL_ + +#pragma pack(1) + +#define ERAR_SUCCESS 0 +#define ERAR_END_ARCHIVE 10 +#define ERAR_NO_MEMORY 11 +#define ERAR_BAD_DATA 12 +#define ERAR_BAD_ARCHIVE 13 +#define ERAR_UNKNOWN_FORMAT 14 +#define ERAR_EOPEN 15 +#define ERAR_ECREATE 16 +#define ERAR_ECLOSE 17 +#define ERAR_EREAD 18 +#define ERAR_EWRITE 19 +#define ERAR_SMALL_BUF 20 +#define ERAR_UNKNOWN 21 +#define ERAR_MISSING_PASSWORD 22 +#define ERAR_EREFERENCE 23 +#define ERAR_BAD_PASSWORD 24 + +#define RAR_OM_LIST 0 +#define RAR_OM_EXTRACT 1 +#define RAR_OM_LIST_INCSPLIT 2 + +#define RAR_SKIP 0 +#define RAR_TEST 1 +#define RAR_EXTRACT 2 + +#define RAR_VOL_ASK 0 +#define RAR_VOL_NOTIFY 1 + +#define RAR_DLL_VERSION 8 + +#define RAR_HASH_NONE 0 +#define RAR_HASH_CRC32 1 +#define RAR_HASH_BLAKE2 2 + + +#ifdef _UNIX +#define CALLBACK +#define PASCAL +#define LONG long +#define HANDLE void * +#define LPARAM long +#define UINT unsigned int +#endif + +#define RHDF_SPLITBEFORE 0x01 +#define RHDF_SPLITAFTER 0x02 +#define RHDF_ENCRYPTED 0x04 +#define RHDF_SOLID 0x10 +#define RHDF_DIRECTORY 0x20 + + +struct RARHeaderData +{ + char ArcName[260]; + char FileName[260]; + unsigned int Flags; + unsigned int PackSize; + unsigned int UnpSize; + unsigned int HostOS; + unsigned int FileCRC; + unsigned int FileTime; + unsigned int UnpVer; + unsigned int Method; + unsigned int FileAttr; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; +}; + + +struct RARHeaderDataEx +{ + char ArcName[1024]; + wchar_t ArcNameW[1024]; + char FileName[1024]; + wchar_t FileNameW[1024]; + unsigned int Flags; + unsigned int PackSize; + unsigned int PackSizeHigh; + unsigned int UnpSize; + unsigned int UnpSizeHigh; + unsigned int HostOS; + unsigned int FileCRC; + unsigned int FileTime; + unsigned int UnpVer; + unsigned int Method; + unsigned int FileAttr; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int DictSize; + unsigned int HashType; + char Hash[32]; + unsigned int RedirType; + wchar_t *RedirName; + unsigned int RedirNameSize; + unsigned int DirTarget; + unsigned int MtimeLow; + unsigned int MtimeHigh; + unsigned int CtimeLow; + unsigned int CtimeHigh; + unsigned int AtimeLow; + unsigned int AtimeHigh; + unsigned int Reserved[988]; +}; + + +struct RAROpenArchiveData +{ + char *ArcName; + unsigned int OpenMode; + unsigned int OpenResult; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; +}; + +typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2); + +#define ROADF_VOLUME 0x0001 +#define ROADF_COMMENT 0x0002 +#define ROADF_LOCK 0x0004 +#define ROADF_SOLID 0x0008 +#define ROADF_NEWNUMBERING 0x0010 +#define ROADF_SIGNED 0x0020 +#define ROADF_RECOVERY 0x0040 +#define ROADF_ENCHEADERS 0x0080 +#define ROADF_FIRSTVOLUME 0x0100 + +struct RAROpenArchiveDataEx +{ + char *ArcName; + wchar_t *ArcNameW; + unsigned int OpenMode; + unsigned int OpenResult; + char *CmtBuf; + unsigned int CmtBufSize; + unsigned int CmtSize; + unsigned int CmtState; + unsigned int Flags; + UNRARCALLBACK Callback; + LPARAM UserData; + unsigned int Reserved[28]; +}; + +enum UNRARCALLBACK_MESSAGES { + UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW, + UCM_NEEDPASSWORDW +}; + +typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode); +typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size); + +#ifdef __cplusplus +extern "C" { +#endif + +HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *ArchiveData); +HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *ArchiveData); +int PASCAL RARCloseArchive(HANDLE hArcData); +int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *HeaderData); +int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *HeaderData); +int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName); +int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar_t *DestPath,wchar_t *DestName); +void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData); +void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc); +void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc); +void PASCAL RARSetPassword(HANDLE hArcData,char *Password); +int PASCAL RARGetDllVersion(); + +#ifdef __cplusplus +} +#endif + +#pragma pack() + +#endif diff --git a/reference/unrar/dll.rc b/reference/unrar/dll.rc new file mode 100644 index 00000000..7aa83145 --- /dev/null +++ b/reference/unrar/dll.rc @@ -0,0 +1,28 @@ +#include +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 5, 50, 100, 2418 +PRODUCTVERSION 5, 50, 100, 2418 +FILEOS VOS__WINDOWS32 +FILETYPE VFT_APP +{ + BLOCK "StringFileInfo" + { + BLOCK "040904E4" + { + VALUE "CompanyName", "Alexander Roshal\0" + VALUE "ProductName", "RAR decompression library\0" + VALUE "FileDescription", "RAR decompression library\0" + VALUE "FileVersion", "5.50.0\0" + VALUE "ProductVersion", "5.50.0\0" + VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2017\0" + VALUE "OriginalFilename", "Unrar.dll\0" + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0409, 0x04E4 + } +} + diff --git a/reference/unrar/encname.cpp b/reference/unrar/encname.cpp new file mode 100644 index 00000000..5556af3e --- /dev/null +++ b/reference/unrar/encname.cpp @@ -0,0 +1,69 @@ +#include "rar.hpp" + +EncodeFileName::EncodeFileName() +{ + Flags=0; + FlagBits=0; + FlagsPos=0; + DestSize=0; +} + + + + +void EncodeFileName::Decode(char *Name,byte *EncName,size_t EncSize,wchar *NameW, + size_t MaxDecSize) +{ + size_t EncPos=0,DecPos=0; + byte HighByte=EncPos=EncSize) + break; + Flags=EncName[EncPos++]; + FlagBits=8; + } + switch(Flags>>6) + { + case 0: + if (EncPos>=EncSize) + break; + NameW[DecPos++]=EncName[EncPos++]; + break; + case 1: + if (EncPos>=EncSize) + break; + NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8); + break; + case 2: + if (EncPos+1>=EncSize) + break; + NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8); + EncPos+=2; + break; + case 3: + { + if (EncPos>=EncSize) + break; + int Length=EncName[EncPos++]; + if ((Length & 0x80)!=0) + { + if (EncPos>=EncSize) + break; + byte Correction=EncName[EncPos++]; + for (Length=(Length&0x7f)+2;Length>0 && DecPos0 && DecPos1) + exit(RARX_USERBREAK); + // Otherwise return from signal handler and let Wait() function to close + // files and quit. We cannot use the same approach as in Windows, + // because Unix signal handler can block execution of our main code. +#endif + +#if defined(_WIN_ALL) && !defined(_MSC_VER) + // never reached, just to avoid a compiler warning + return TRUE; +#endif +} + + +void ErrorHandler::SetSignalHandlers(bool Enable) +{ + EnableBreak=Enable; +#ifdef _WIN_ALL + SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE); +#else + signal(SIGINT,Enable ? ProcessSignal:SIG_IGN); + signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN); +#endif +} + + +void ErrorHandler::Throw(RAR_EXIT Code) +{ + if (Code==RARX_USERBREAK && !EnableBreak) + return; +#if !defined(SILENT) + // Do not write "aborted" when just displaying online help. + if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR) + mprintf(L"\n%s\n",St(MProgAborted)); +#endif + SetErrorCode(Code); + throw Code; +} + + +void ErrorHandler::SysErrMsg() +{ +#if !defined(SFX_MODULE) && !defined(SILENT) +#ifdef _WIN_ALL + wchar *lpMsgBuf=NULL; + int ErrType=GetLastError(); + if (ErrType!=0 && FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM, + NULL,ErrType,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&lpMsgBuf,0,NULL)) + { + wchar *CurMsg=lpMsgBuf; + while (CurMsg!=NULL) + { + while (*CurMsg=='\r' || *CurMsg=='\n') + CurMsg++; + if (*CurMsg==0) + break; + wchar *EndMsg=wcschr(CurMsg,'\r'); + if (EndMsg==NULL) + EndMsg=wcschr(CurMsg,'\n'); + if (EndMsg!=NULL) + { + *EndMsg=0; + EndMsg++; + } + uiMsg(UIERROR_SYSERRMSG,CurMsg); + CurMsg=EndMsg; + } + } + LocalFree( lpMsgBuf ); +#endif + +#if defined(_UNIX) || defined(_EMX) + if (errno!=0) + { + char *err=strerror(errno); + if (err!=NULL) + { + wchar Msg[1024]; + CharToWide(err,Msg,ASIZE(Msg)); + uiMsg(UIERROR_SYSERRMSG,Msg); + } + } +#endif + +#endif +} + + +int ErrorHandler::GetSystemErrorCode() +{ +#ifdef _WIN_ALL + return GetLastError(); +#else + return errno; +#endif +} + + +void ErrorHandler::SetSystemErrorCode(int Code) +{ +#ifdef _WIN_ALL + SetLastError(Code); +#else + errno=Code; +#endif +} diff --git a/reference/unrar/errhnd.hpp b/reference/unrar/errhnd.hpp new file mode 100644 index 00000000..26df96d5 --- /dev/null +++ b/reference/unrar/errhnd.hpp @@ -0,0 +1,70 @@ +#ifndef _RAR_ERRHANDLER_ +#define _RAR_ERRHANDLER_ + +enum RAR_EXIT // RAR exit code. +{ + RARX_SUCCESS = 0, + RARX_WARNING = 1, + RARX_FATAL = 2, + RARX_CRC = 3, + RARX_LOCK = 4, + RARX_WRITE = 5, + RARX_OPEN = 6, + RARX_USERERROR = 7, + RARX_MEMORY = 8, + RARX_CREATE = 9, + RARX_NOFILES = 10, + RARX_BADPWD = 11, + RARX_USERBREAK = 255 +}; + +class ErrorHandler +{ + private: + RAR_EXIT ExitCode; + uint ErrCount; + bool EnableBreak; + bool Silent; + bool DisableShutdown; // Shutdown is not suitable after last error. + public: + ErrorHandler(); + void Clean(); + void MemoryError(); + void OpenError(const wchar *FileName); + void CloseError(const wchar *FileName); + void ReadError(const wchar *FileName); + bool AskRepeatRead(const wchar *FileName); + void WriteError(const wchar *ArcName,const wchar *FileName); + void WriteErrorFAT(const wchar *FileName); + bool AskRepeatWrite(const wchar *FileName,bool DiskFull); + void SeekError(const wchar *FileName); + void GeneralErrMsg(const wchar *fmt,...); + void MemoryErrorMsg(); + void OpenErrorMsg(const wchar *FileName); + void OpenErrorMsg(const wchar *ArcName,const wchar *FileName); + void CreateErrorMsg(const wchar *FileName); + void CreateErrorMsg(const wchar *ArcName,const wchar *FileName); + void ReadErrorMsg(const wchar *FileName); + void ReadErrorMsg(const wchar *ArcName,const wchar *FileName); + void WriteErrorMsg(const wchar *ArcName,const wchar *FileName); + void ArcBrokenMsg(const wchar *ArcName); + void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName); + void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName); + void Exit(RAR_EXIT ExitCode); + void SetErrorCode(RAR_EXIT Code); + RAR_EXIT GetErrorCode() {return ExitCode;} + uint GetErrorCount() {return ErrCount;} + void SetSignalHandlers(bool Enable); + void Throw(RAR_EXIT Code); + void SetSilent(bool Mode) {Silent=Mode;}; + void SysErrMsg(); + int GetSystemErrorCode(); + void SetSystemErrorCode(int Code); + bool IsShutdownEnabled() {return !DisableShutdown;} + + bool UserBreak; // Ctrl+Break is pressed. + bool MainExit; // main() is completed. +}; + + +#endif diff --git a/reference/unrar/extinfo.cpp b/reference/unrar/extinfo.cpp new file mode 100644 index 00000000..5cb90a40 --- /dev/null +++ b/reference/unrar/extinfo.cpp @@ -0,0 +1,178 @@ +#include "rar.hpp" + +#include "hardlinks.cpp" +#include "win32stm.cpp" + +#ifdef _WIN_ALL +#include "win32acl.cpp" +#include "win32lnk.cpp" +#endif + +#ifdef _UNIX +#include "uowners.cpp" +#ifdef SAVE_LINKS +#include "ulinks.cpp" +#endif +#endif + + + +// RAR2 service header extra records. +#ifndef SFX_MODULE +void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name) +{ + if (Cmd->Test) + return; + switch(Arc.SubBlockHead.SubType) + { +#ifdef _UNIX + case UO_HEAD: + if (Cmd->ProcessOwners) + ExtractUnixOwner20(Arc,Name); + break; +#endif +#ifdef _WIN_ALL + case NTACL_HEAD: + if (Cmd->ProcessOwners) + ExtractACL20(Arc,Name); + break; + case STREAM_HEAD: + ExtractStreams20(Arc,Name); + break; +#endif + } +} +#endif + + +// RAR3 and RAR5 service header extra records. +void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name) +{ +#ifdef _UNIX + if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 && + Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) + ExtractUnixOwner30(Arc,Name); +#endif +#ifdef _WIN_ALL + if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL)) + ExtractACL(Arc,Name); + if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) + ExtractStreams(Arc,Name,Cmd->Test); +#endif +} + + +// Extra data stored directly in file header. +void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name) +{ +#ifdef _UNIX + if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet) + SetUnixOwner(Arc,Name); +#endif +} + + + + +// Calculate a number of path components except \. and \.. +static int CalcAllowedDepth(const wchar *Name) +{ + int AllowedDepth=0; + while (*Name!=0) + { + if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1])) + { + bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0); + bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0); + if (!Dot && !Dot2) + AllowedDepth++; + } + Name++; + } + return AllowedDepth; +} + + +// Check if all existing path components are directories and not links. +static bool LinkInPath(const wchar *Name) +{ + wchar Path[NM]; + if (wcslen(Name)>=ASIZE(Path)) + return true; // It should not be that long, skip. + wcsncpyz(Path,Name,ASIZE(Path)); + for (wchar *s=Path+wcslen(Path)-1;s>Path;s--) + if (IsPathDiv(*s)) + { + *s=0; + FindData FD; + if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir)) + return true; + } + return false; +} + + +bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName) +{ + // Catch root dir based /path/file paths also as stuff like \\?\. + // Do not check PrepSrcName here, it can be root based if destination path + // is a root based. + if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName)) + return false; + + // Number of ".." in link target. + int UpLevels=0; + for (int Pos=0;*TargetName!=0;Pos++) + { + bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' && + (IsPathDiv(TargetName[2]) || TargetName[2]==0) && + (Pos==0 || IsPathDiv(*(TargetName-1))); + if (Dot2) + UpLevels++; + TargetName++; + } + // If link target includes "..", it must not have another links + // in the path, because they can bypass our safety check. For example, + // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next + // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next. + if (UpLevels>0 && LinkInPath(PrepSrcName)) + return false; + + // We could check just prepared src name, but for extra safety + // we check both original (as from archive header) and prepared + // (after applying the destination path and -ep switches) names. + + int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth. + + // Remove the destination path from prepared name if any. We should not + // count the destination path depth, because the link target must point + // inside of this path, not outside of it. + size_t ExtrPathLength=wcslen(Cmd->ExtrPath); + if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0) + { + PrepSrcName+=ExtrPathLength; + while (IsPathDiv(*PrepSrcName)) + PrepSrcName++; + } + int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName); + + return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels; +} + + +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) +{ +#if defined(SAVE_LINKS) && defined(_UNIX) + // For RAR 3.x archives we process links even in test mode to skip link data. + if (Arc.Format==RARFMT15) + return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName); + if (Arc.Format==RARFMT50) + return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead); +#elif defined _WIN_ALL + // RAR 5.0 archives store link information in file header, so there is + // no need to additionally test it if we do not create a file. + if (Arc.Format==RARFMT50) + return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead); +#endif + return false; +} diff --git a/reference/unrar/extinfo.hpp b/reference/unrar/extinfo.hpp new file mode 100644 index 00000000..2b0005da --- /dev/null +++ b/reference/unrar/extinfo.hpp @@ -0,0 +1,23 @@ +#ifndef _RAR_EXTINFO_ +#define _RAR_EXTINFO_ + +bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName); +bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName); +#ifdef _UNIX +void SetUnixOwner(Archive &Arc,const wchar *FileName); +#endif + +bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); + +void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize); + +#ifdef _WIN_ALL +bool SetPrivilege(LPCTSTR PrivName); +#endif + +void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name); +void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name); +void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name); + + +#endif diff --git a/reference/unrar/extract.cpp b/reference/unrar/extract.cpp new file mode 100644 index 00000000..f9461136 --- /dev/null +++ b/reference/unrar/extract.cpp @@ -0,0 +1,1167 @@ +#include "rar.hpp" + +CmdExtract::CmdExtract(CommandData *Cmd) +{ + CmdExtract::Cmd=Cmd; + + *ArcName=0; + + *DestFileName=0; + + TotalFileCount=0; + Unp=new Unpack(&DataIO); +#ifdef RAR_SMP + Unp->SetThreads(Cmd->Threads); +#endif +} + + +CmdExtract::~CmdExtract() +{ + delete Unp; +} + + +void CmdExtract::DoExtract() +{ +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + Fat32=NotFat32=false; +#endif + PasswordCancelled=false; + DataIO.SetCurrentCommand(Cmd->Command[0]); + + FindData FD; + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + if (FindFile::FastFind(ArcName,&FD)) + DataIO.TotalArcSize+=FD.Size; + + Cmd->ArcNames.Rewind(); + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + { + if (Cmd->ManualPassword) + Cmd->Password.Clean(); // Clean user entered password before processing next archive. + while (true) + { + EXTRACT_ARC_CODE Code=ExtractArchive(); + if (Code!=EXTRACT_ARC_REPEAT) + break; + } + if (FindFile::FastFind(ArcName,&FD)) + DataIO.ProcessedArcSize+=FD.Size; + } + + // Clean user entered password. Not really required, just for extra safety. + if (Cmd->ManualPassword) + Cmd->Password.Clean(); + + if (TotalFileCount==0 && Cmd->Command[0]!='I' && + ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password. + { + if (!PasswordCancelled) + uiMsg(UIERROR_NOFILESTOEXTRACT,ArcName); + ErrHandler.SetErrorCode(RARX_NOFILES); + } + else + if (!Cmd->DisableDone) + if (Cmd->Command[0]=='I') + mprintf(St(MDone)); + else + if (ErrHandler.GetErrorCount()==0) + mprintf(St(MExtrAllOk)); + else + mprintf(St(MExtrTotalErr),ErrHandler.GetErrorCount()); +} + + +void CmdExtract::ExtractArchiveInit(Archive &Arc) +{ + DataIO.UnpArcSize=Arc.FileLength(); + + FileCount=0; + MatchedArgs=0; +#ifndef SFX_MODULE + FirstFile=true; +#endif + + PasswordAll=(Cmd->Password.IsSet()); + + DataIO.UnpVolume=false; + + PrevProcessed=false; + AllMatchesExact=true; + ReconstructDone=false; + AnySolidDataUnpackedWell=false; + + StartTime.SetCurrentTime(); +} + + +EXTRACT_ARC_CODE CmdExtract::ExtractArchive() +{ + Archive Arc(Cmd); + if (!Arc.WOpen(ArcName)) + return EXTRACT_ARC_NEXT; + + if (!Arc.IsArchive(true)) + { +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (CmpExt(ArcName,L"rev")) + { + wchar FirstVolName[NM]; + VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),true); + + // If several volume names from same volume set are specified + // and current volume is not first in set and first volume is present + // and specified too, let's skip the current volume. + if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && + Cmd->ArcNames.Search(FirstVolName,false)) + return EXTRACT_ARC_NEXT; + RecVolumesTest(Cmd,NULL,ArcName); + TotalFileCount++; // Suppress "No files to extract" message. + return EXTRACT_ARC_NEXT; + } +#endif + + mprintf(St(MNotRAR),ArcName); + +#ifndef SFX_MODULE + if (CmpExt(ArcName,L"rar")) +#endif + ErrHandler.SetErrorCode(RARX_WARNING); + return EXTRACT_ARC_NEXT; + } + + if (Arc.FailedHeaderDecryption) // Bad archive password. + return EXTRACT_ARC_NEXT; + +#ifndef SFX_MODULE + if (Arc.Volume && !Arc.FirstVolume) + { + wchar FirstVolName[NM]; + VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering); + + // If several volume names from same volume set are specified + // and current volume is not first in set and first volume is present + // and specified too, let's skip the current volume. + if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) && + Cmd->ArcNames.Search(FirstVolName,false)) + return EXTRACT_ARC_NEXT; + } +#endif + + int64 VolumeSetSize=0; // Total size of volumes after the current volume. + + if (Arc.Volume) + { + // Calculate the total size of all accessible volumes. + // This size is necessary to display the correct total progress indicator. + + wchar NextName[NM]; + wcscpy(NextName,Arc.FileName); + + while (true) + { + // First volume is already added to DataIO.TotalArcSize + // in initial TotalArcSize calculation in DoExtract. + // So we skip it and start from second volume. + NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering); + FindData FD; + if (FindFile::FastFind(NextName,&FD)) + VolumeSetSize+=FD.Size; + else + break; + } + DataIO.TotalArcSize+=VolumeSetSize; + } + + ExtractArchiveInit(Arc); + + if (*Cmd->Command=='T' || *Cmd->Command=='I') + Cmd->Test=true; + + + if (*Cmd->Command=='I') + { + Cmd->DisablePercentage=true; + } + else + uiStartArchiveExtract(!Cmd->Test,ArcName); + + Arc.ViewComment(); + + + while (1) + { + size_t Size=Arc.ReadHeader(); + + + bool Repeat=false; + if (!ExtractCurrentFile(Arc,Size,Repeat)) + if (Repeat) + { + // If we started extraction from not first volume and need to + // restart it from first, we must correct DataIO.TotalArcSize + // for correct total progress display. We subtract the size + // of current volume and all volumes after it and add the size + // of new (first) volume. + FindData OldArc,NewArc; + if (FindFile::FastFind(Arc.FileName,&OldArc) && + FindFile::FastFind(ArcName,&NewArc)) + DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size; + return EXTRACT_ARC_REPEAT; + } + else + break; + } + + +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (Cmd->Test && Arc.Volume) + RecVolumesTest(Cmd,&Arc,ArcName); +#endif + + return EXTRACT_ARC_NEXT; +} + + +bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat) +{ + // We can get negative sizes in corrupt archive and it is unacceptable + // for size comparisons in CmdExtract::UnstoreFile and ComprDataIO::UnpRead, + // where we cast sizes to size_t and can exceed another read or available + // size. We could fix it when reading an archive. But we prefer to do it + // here, because this function is called directly in unrar.dll, so we fix + // bad parameters passed to dll. Also we want to see real negative sizes + // in the listing of corrupt archive. + if (Arc.FileHead.PackSize<0) + Arc.FileHead.PackSize=0; + if (Arc.FileHead.UnpSize<0) + Arc.FileHead.UnpSize=0; + + wchar Command=Cmd->Command[0]; + if (HeaderSize==0) + if (DataIO.UnpVolume) + { +#ifdef NOVOLUME + return false; +#else + // Supposing we unpack an old RAR volume without end of archive record + // and last file is not split between volumes. + if (!MergeArchive(Arc,&DataIO,false,Command)) + { + ErrHandler.SetErrorCode(RARX_WARNING); + return false; + } +#endif + } + else + return false; + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + if (HeaderType!=HEAD_FILE) + { +#ifndef SFX_MODULE + if (HeaderType==HEAD3_OLDSERVICE && PrevProcessed) + SetExtraInfo20(Cmd,Arc,DestFileName); +#endif + if (HeaderType==HEAD_SERVICE && PrevProcessed) + SetExtraInfo(Cmd,Arc,DestFileName); + if (HeaderType==HEAD_ENDARC) + if (Arc.EndArcHead.NextVolume) + { +#ifndef NOVOLUME + if (!MergeArchive(Arc,&DataIO,false,Command)) + { + ErrHandler.SetErrorCode(RARX_WARNING); + return false; + } +#endif + Arc.Seek(Arc.CurBlockPos,SEEK_SET); + return true; + } + else + return false; + Arc.SeekToNext(); + return true; + } + PrevProcessed=false; + + if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact) + return false; + + int MatchType=MATCH_WILDSUBPATH; + + bool EqualNames=false; + wchar MatchedArg[NM]; + int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,MatchedArg,ASIZE(MatchedArg)); + bool MatchFound=MatchNumber!=0; +#ifndef SFX_MODULE + if (Cmd->ExclPath==EXCL_BASEPATH) + { + wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath)); + *PointToName(Cmd->ArcPath)=0; + if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here. + *Cmd->ArcPath=0; + } +#endif + if (MatchFound && !EqualNames) + AllMatchesExact=false; + + Arc.ConvertAttributes(); + +#if !defined(SFX_MODULE) && !defined(RARDLL) + if (Arc.FileHead.SplitBefore && FirstFile) + { + wchar CurVolName[NM]; + wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName)); + VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),Arc.NewNumbering); + + if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName)) + { + // If first volume name does not match the current name and if such + // volume name really exists, let's unpack from this first volume. + Repeat=true; + return false; + } +#ifndef RARDLL + if (!ReconstructDone) + { + ReconstructDone=true; + if (RecVolumesRestore(Cmd,Arc.FileName,true)) + { + Repeat=true; + return false; + } + } +#endif + wcsncpyz(ArcName,CurVolName,ASIZE(ArcName)); + } +#endif + + wchar ArcFileName[NM]; + ConvertPath(Arc.FileHead.FileName,ArcFileName); + + if (Arc.FileHead.Version) + { + if (Cmd->VersionControl!=1 && !EqualNames) + { + if (Cmd->VersionControl==0) + MatchFound=false; + int Version=ParseVersionFileName(ArcFileName,false); + if (Cmd->VersionControl-1==Version) + ParseVersionFileName(ArcFileName,true); + else + MatchFound=false; + } + } + else + if (!Arc.IsArcDir() && Cmd->VersionControl>1) + MatchFound=false; + + DataIO.UnpVolume=Arc.FileHead.SplitAfter; + DataIO.NextVolumeMissing=false; + + Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET); + + bool ExtrFile=false; + bool SkipSolid=false; + +#ifndef SFX_MODULE + if (FirstFile && (MatchFound || Arc.Solid) && Arc.FileHead.SplitBefore) + { + if (MatchFound) + { + uiMsg(UIERROR_NEEDPREVVOL,Arc.FileName,ArcFileName); +#ifdef RARDLL + Cmd->DllError=ERAR_BAD_DATA; +#endif + ErrHandler.SetErrorCode(RARX_OPEN); + } + MatchFound=false; + } + + FirstFile=false; +#endif + + if (MatchFound || (SkipSolid=Arc.Solid)!=0) + { + // First common call of uiStartFileExtract. It is done before overwrite + // prompts, so if SkipSolid state is changed below, we'll need to make + // additional uiStartFileExtract calls with updated parameters. + if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid)) + return false; + + ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName)); + + // DestFileName can be set empty in case of excessive -ap switch. + ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore; + + if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X')) + { + FindData FD; + if (FindFile::FastFind(DestFileName,&FD)) + { + if (FD.mtime >= Arc.FileHead.mtime) + { + // If directory already exists and its modification time is newer + // than start of extraction, it is likely it was created + // when creating a path to one of already extracted items. + // In such case we'll better update its time even if archived + // directory is older. + + if (!FD.IsDir || FD.mtimeFreshFiles) + ExtrFile=false; + } + + if (!CheckUnpVer(Arc,ArcFileName)) + { + ErrHandler.SetErrorCode(RARX_FATAL); +#ifdef RARDLL + Cmd->DllError=ERAR_UNKNOWN_FORMAT; +#endif + Arc.SeekToNext(); + return !Arc.Solid; // Can try extracting next file only in non-solid archive. + } + + while (true) // Repeat password prompt in case of wrong password here. + { + if (Arc.FileHead.Encrypted) + { + // Stop archive extracting if user cancelled a password prompt. +#ifdef RARDLL + if (!ExtrDllGetPassword()) + { + Cmd->DllError=ERAR_MISSING_PASSWORD; + return false; + } +#else + if (!ExtrGetPassword(Arc,ArcFileName)) + { + PasswordCancelled=true; + return false; + } +#endif + // Skip only the current encrypted file if empty password is entered. + // Actually our "cancel" code above intercepts empty passwords too now, + // so we keep the code below just in case we'll decide process empty + // and cancelled passwords differently sometimes. + if (!Cmd->Password.IsSet()) + { + ErrHandler.SetErrorCode(RARX_WARNING); +#ifdef RARDLL + Cmd->DllError=ERAR_MISSING_PASSWORD; +#endif + ExtrFile=false; + } + } + + + // Set a password before creating the file, so we can skip creating + // in case of wrong password. + SecPassword FilePassword=Cmd->Password; +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + ConvertDosPassword(Arc,FilePassword); +#endif + + byte PswCheck[SIZE_PSWCHECK]; + DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword, + Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL, + Arc.FileHead.InitV,Arc.FileHead.Lg2Count, + Arc.FileHead.HashKey,PswCheck); + + // If header is damaged, we cannot rely on password check value, + // because it can be damaged too. + if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck && + memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 && + !Arc.BrokenHeader) + { + uiMsg(UIWAIT_BADPSW,ArcFileName); + + if (!PasswordAll) // If entered manually and not through -p. + { + Cmd->Password.Clean(); + continue; // Request a password again. + } +#ifdef RARDLL + // If we already have ERAR_EOPEN as result of missing volume, + // we should not replace it with less precise ERAR_BAD_PASSWORD. + if (Cmd->DllError!=ERAR_EOPEN) + Cmd->DllError=ERAR_BAD_PASSWORD; +#endif + ErrHandler.SetErrorCode(RARX_BADPWD); + ExtrFile=false; + } + break; + } + +#ifdef RARDLL + if (*Cmd->DllDestName!=0) + wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName)); +#endif + + File CurFile; + + bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE; + if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY) + { + if (ExtrFile && Command!='P' && !Cmd->Test) + { + // Overwrite prompt for symbolic and hard links. + bool UserReject=false; + if (FileExist(DestFileName) && !UserReject) + FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); + if (UserReject) + ExtrFile=false; + } + } + else + if (Arc.IsArcDir()) + { + if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) + return true; + TotalFileCount++; + ExtrCreateDir(Arc,ArcFileName); + // It is important to not increment MatchedArgs here, so we extract + // dir with its entire contents and not dir record only even if + // dir record precedes files. + return true; + } + else + if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY). + ExtrFile=ExtrCreateFile(Arc,CurFile); + + if (!ExtrFile && Arc.Solid) + { + SkipSolid=true; + ExtrFile=true; + + // We changed SkipSolid, so we need to call uiStartFileExtract + // with "Skip" parameter to change the operation status + // from "extracting" to "skipping". For example, it can be necessary + // if user answered "No" to overwrite prompt when unpacking + // a solid archive. + if (!uiStartFileExtract(ArcFileName,false,false,true)) + return false; + } + if (ExtrFile) + { + // Set it in test mode, so we also test subheaders such as NTFS streams + // after tested file. + if (Cmd->Test) + PrevProcessed=true; + + bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk. + + if (!SkipSolid) + { + if (!TestMode && Command!='P' && CurFile.IsDevice()) + { + uiMsg(UIERROR_INVALIDNAME,Arc.FileName,DestFileName); + ErrHandler.WriteError(Arc.FileName,DestFileName); + } + TotalFileCount++; + } + FileCount++; + if (Command!='I') + if (SkipSolid) + mprintf(St(MExtrSkipFile),ArcFileName); + else + switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch. + { + case 'T': + mprintf(St(MExtrTestFile),ArcFileName); + break; +#ifndef SFX_MODULE + case 'P': + mprintf(St(MExtrPrinting),ArcFileName); + break; +#endif + case 'X': + case 'E': + mprintf(St(MExtrFile),DestFileName); + break; + } + if (!Cmd->DisablePercentage) + mprintf(L" "); + + DataIO.CurUnpRead=0; + DataIO.CurUnpWrite=0; + DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads); + DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads); + DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize); + DataIO.SetFiles(&Arc,&CurFile); + DataIO.SetTestMode(TestMode); + DataIO.SetSkipUnpCRC(SkipSolid); + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + if (!TestMode && !Arc.BrokenHeader && + Arc.FileHead.UnpSize>0xffffffff && (Fat32 || !NotFat32)) + { + if (!Fat32) // Not detected yet. + NotFat32=!(Fat32=IsFAT(Cmd->ExtrPath)); + if (Fat32) + uiMsg(UIMSG_FAT32SIZE); // Inform user about FAT32 size limit. + } +#endif + + if (!TestMode && !Arc.BrokenHeader && + (Arc.FileHead.PackSize<<11)>Arc.FileHead.UnpSize && + (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize)) + CurFile.Prealloc(Arc.FileHead.UnpSize); + + CurFile.SetAllowDelete(!Cmd->KeepBroken); + + bool FileCreateMode=!TestMode && !SkipSolid && Command!='P'; + bool ShowChecksum=true; // Display checksum verification result. + + bool LinkSuccess=true; // Assume success for test mode. + if (LinkEntry) + { + FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType; + + if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY) + { + wchar NameExisting[NM]; + ExtrPrepareName(Arc,Arc.FileHead.RedirName,NameExisting,ASIZE(NameExisting)); + if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch. + if (Type==FSREDIR_HARDLINK) + LinkSuccess=ExtractHardlink(DestFileName,NameExisting,ASIZE(NameExisting)); + else + LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting)); + } + else + if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION) + { + if (FileCreateMode) + LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName); + } + else + { + uiMsg(UIERROR_UNKNOWNEXTRA, Arc.FileName, DestFileName); + LinkSuccess=false; + } + + if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode) + { + // RAR 5.x links have a valid data checksum even in case of + // failure, because they do not store any data. + // We do not want to display "OK" in this case. + // For 4.x symlinks we verify the checksum only when extracting, + // but not when testing an archive. + ShowChecksum=false; + } + PrevProcessed=FileCreateMode && LinkSuccess; + } + else + if (!Arc.FileHead.SplitBefore) + if (Arc.FileHead.Method==0) + UnstoreFile(DataIO,Arc.FileHead.UnpSize); + else + { + Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid); + Unp->SetDestSize(Arc.FileHead.UnpSize); +#ifndef SFX_MODULE + if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15) + Unp->DoUnpack(15,FileCount>1 && Arc.Solid); + else +#endif + Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid); + } + + Arc.SeekToNext(); + + // We check for "split after" flag to detect partially extracted files + // from incomplete volume sets. For them file header contains packed + // data hash, which must not be compared against unpacked data hash + // to prevent accidental match. Moreover, for -m0 volumes packed data + // hash would match truncated unpacked data hash and lead to fake "OK" + // in incomplete volume set. + bool ValidCRC=!Arc.FileHead.SplitAfter && DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL); + + // We set AnySolidDataUnpackedWell to true if we found at least one + // valid non-zero solid file in preceding solid stream. If it is true + // and if current encrypted file is broken, we do not need to hint + // about a wrong password and can report CRC error only. + if (!Arc.FileHead.Solid) + AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found. + else + if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC) + AnySolidDataUnpackedWell=true; + + bool BrokenFile=false; + + // Checksum is not calculated in skip solid mode for performance reason. + if (!SkipSolid && ShowChecksum) + { + if (ValidCRC) + { + if (Command!='P' && Command!='I') + mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ", + Arc.FileHead.FileHash.Type==HASH_NONE ? L" ?":St(MOk)); + } + else + { + if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck || + Arc.BrokenHeader) && !AnySolidDataUnpackedWell) + uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName); + else + uiMsg(UIERROR_CHECKSUM,Arc.FileName,ArcFileName); + BrokenFile=true; + ErrHandler.SetErrorCode(RARX_CRC); +#ifdef RARDLL + // If we already have ERAR_EOPEN as result of missing volume + // or ERAR_BAD_PASSWORD for RAR5 wrong password, + // we should not replace it with less precise ERAR_BAD_DATA. + if (Cmd->DllError!=ERAR_EOPEN && Cmd->DllError!=ERAR_BAD_PASSWORD) + Cmd->DllError=ERAR_BAD_DATA; +#endif + } + } + else + mprintf(L"\b\b\b\b\b "); + + if (!TestMode && (Command=='X' || Command=='E') && + (!LinkEntry || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) && + (!BrokenFile || Cmd->KeepBroken)) + { + // We could preallocate more space that really written to broken file. + if (BrokenFile) + CurFile.Truncate(); + +#if defined(_WIN_ALL) || defined(_EMX) + if (Cmd->ClearArc) + Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE; +#endif + + + CurFile.SetOpenFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); + CurFile.Close(); +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (Cmd->SetCompressedAttr && + (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0) + SetFileCompression(CurFile.FileName,true); +#endif + SetFileHeaderExtra(Cmd,Arc,CurFile.FileName); + + CurFile.SetCloseFileTime( + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); + if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(CurFile.FileName,Arc.FileHead.FileAttr)) + uiMsg(UIERROR_FILEATTR,Arc.FileName,CurFile.FileName); + + PrevProcessed=true; + } + } + } + // It is important to increment it for files, but not dirs. So we extract + // dir with its entire contents, not just dir record only even if dir + // record precedes files. + if (MatchFound) + MatchedArgs++; + if (DataIO.NextVolumeMissing) + return false; + if (!ExtrFile) + if (!Arc.Solid) + Arc.SeekToNext(); + else + if (!SkipSolid) + return false; + return true; +} + + +void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize) +{ + // 512 KB and larger buffer reported to reduce performance on old XP + // computers with WDC WD2000JD HDD. According to test made by user + // 256 KB buffer is optimal. + Array Buffer(0x40000); + while (1) + { + uint Code=DataIO.UnpRead(&Buffer[0],Buffer.Size()); + if (Code==0 || (int)Code==-1) + break; + Code=Code=0) + DestUnpSize-=Code; + } +} + + +bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) +{ + SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. + + File Existing; + if (!Existing.WOpen(NameExisting)) + { + uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew); + uiMsg(UIERROR_FILECOPYHINT,ArcName); +#ifdef RARDLL + Cmd->DllError=ERAR_EREFERENCE; +#endif + return false; + } + + Array Buffer(0x100000); + int64 CopySize=0; + + while (true) + { + Wait(); + int ReadSize=Existing.Read(&Buffer[0],Buffer.Size()); + if (ReadSize==0) + break; + New.Write(&Buffer[0],ReadSize); + CopySize+=ReadSize; + } + + return true; +} + + +void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize) +{ + wcsncpyz(DestName,Cmd->ExtrPath,DestSize); + + if (*Cmd->ExtrPath!=0) + { + wchar LastChar=*PointToLastChar(Cmd->ExtrPath); + // We need IsPathDiv check here to correctly handle Unix forward slash + // in the end of destination path in Windows: rar x arc dest/ + // IsDriveDiv is needed for current drive dir: rar x arc d: + if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar)) + { + // Destination path can be without trailing slash if it come from GUI shell. + AddEndSlash(DestName,DestSize); + } + } + +#ifndef SFX_MODULE + if (Cmd->AppendArcNameToPath) + { + wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize); + SetExt(DestName,NULL,DestSize); + AddEndSlash(DestName,DestSize); + } +#endif + +#ifndef SFX_MODULE + size_t ArcPathLength=wcslen(Cmd->ArcPath); + if (ArcPathLength>0) + { + size_t NameLength=wcslen(ArcFileName); + + // Earlier we compared lengths only here, but then noticed a cosmetic bug + // in WinRAR. When extracting a file reference from subfolder with + // "Extract relative paths", so WinRAR sets ArcPath, if reference target + // is missing, error message removed ArcPath both from reference and target + // names. If target was stored in another folder, its name looked wrong. + if (NameLength>=ArcPathLength && + wcsnicompc(Cmd->ArcPath,ArcFileName,ArcPathLength)==0 && + (IsPathDiv(Cmd->ArcPath[ArcPathLength-1]) || + IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0)) + { + ArcFileName+=Min(ArcPathLength,NameLength); + while (IsPathDiv(*ArcFileName)) + ArcFileName++; + if (*ArcFileName==0) // Excessive -ap switch. + { + *DestName=0; + return; + } + } + } +#endif + + wchar Command=Cmd->Command[0]; + // Use -ep3 only in systems, where disk letters are exist, not in Unix. + bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':'); + + // We do not use any user specified destination paths when extracting + // absolute paths in -ep3 mode. + if (AbsPaths) + *DestName=0; + + if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH) + wcsncatz(DestName,PointToName(ArcFileName),DestSize); + else + wcsncatz(DestName,ArcFileName,DestSize); + +#ifdef _WIN_ALL + // Must do after Cmd->ArcPath processing above, so file name and arc path + // trailing spaces are in sync. + if (!Cmd->AllowIncompatNames) + MakeNameCompatible(DestName); +#endif + + wchar DiskLetter=toupperw(DestName[0]); + + if (AbsPaths) + { + if (DestName[1]=='_' && IsPathDiv(DestName[2]) && + DiskLetter>='A' && DiskLetter<='Z') + DestName[1]=':'; + else + if (DestName[0]=='_' && DestName[1]=='_') + { + // Convert __server\share to \\server\share. + DestName[0]=CPATHDIVIDER; + DestName[1]=CPATHDIVIDER; + } + } +} + + +#ifdef RARDLL +bool CmdExtract::ExtrDllGetPassword() +{ + if (!Cmd->Password.IsSet()) + { + if (Cmd->Callback!=NULL) + { + wchar PasswordW[MAXPASSWORD]; + *PasswordW=0; + if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1) + *PasswordW=0; + if (*PasswordW==0) + { + char PasswordA[MAXPASSWORD]; + *PasswordA=0; + if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1) + *PasswordA=0; + GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW)); + cleandata(PasswordA,sizeof(PasswordA)); + } + Cmd->Password.Set(PasswordW); + cleandata(PasswordW,sizeof(PasswordW)); + Cmd->ManualPassword=true; + } + if (!Cmd->Password.IsSet()) + return false; + } + return true; +} +#endif + + +#ifndef RARDLL +bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName) +{ + if (!Cmd->Password.IsSet()) + { + if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password) || !Cmd->Password.IsSet()) + { + // Suppress "test is ok" message in GUI if user entered + // an empty password or cancelled a password prompt. + uiMsg(UIERROR_INCERRCOUNT); + + return false; + } + Cmd->ManualPassword=true; + } +#if !defined(SILENT) + else + if (!PasswordAll && !Arc.FileHead.Solid) + { + eprintf(St(MUseCurPsw),ArcFileName); + switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll))) + { + case -1: + ErrHandler.Exit(RARX_USERBREAK); + case 2: + if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)) + return false; + break; + case 3: + PasswordAll=true; + break; + } + } +#endif + return true; +} +#endif + + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +void CmdExtract::ConvertDosPassword(Archive &Arc,SecPassword &DestPwd) +{ + if (Arc.Format==RARFMT15 && Arc.FileHead.HostOS==HOST_MSDOS) + { + // We need the password in OEM encoding if file was encrypted by + // native RAR/DOS (not extender based). Let's make the conversion. + wchar PlainPsw[MAXPASSWORD]; + Cmd->Password.Get(PlainPsw,ASIZE(PlainPsw)); + char PswA[MAXPASSWORD]; + CharToOemBuffW(PlainPsw,PswA,ASIZE(PswA)); + PswA[ASIZE(PswA)-1]=0; + CharToWide(PswA,PlainPsw,ASIZE(PlainPsw)); + DestPwd.Set(PlainPsw); + cleandata(PlainPsw,sizeof(PlainPsw)); + cleandata(PswA,sizeof(PswA)); + } +} +#endif + + +void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName) +{ + if (Cmd->Test) + { + mprintf(St(MExtrTestFile),ArcFileName); + mprintf(L" %s",St(MOk)); + return; + } + + MKDIR_CODE MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); + bool DirExist=false; + if (MDCode!=MKDIR_SUCCESS) + { + DirExist=FileExist(DestFileName); + if (DirExist && !IsDir(GetFileAttr(DestFileName))) + { + // File with name same as this directory exists. Propose user + // to overwrite it. + bool UserReject; + FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime); + DirExist=false; + } + if (!DirExist) + { + CreatePath(DestFileName,true); + MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); + if (MDCode!=MKDIR_SUCCESS) + { + wchar OrigName[ASIZE(DestFileName)]; + wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); + MakeNameUsable(DestFileName,true); + CreatePath(DestFileName,true); + MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr); +#ifndef SFX_MODULE + if (MDCode==MKDIR_SUCCESS) + uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); +#endif + } + } + } + if (MDCode==MKDIR_SUCCESS) + { + mprintf(St(MCreatDir),DestFileName); + mprintf(L" %s",St(MOk)); + PrevProcessed=true; + } + else + if (DirExist) + { + if (!Cmd->IgnoreGeneralAttr) + SetFileAttr(DestFileName,Arc.FileHead.FileAttr); + PrevProcessed=true; + } + else + { + uiMsg(UIERROR_DIRCREATE,Arc.FileName,DestFileName); + ErrHandler.SysErrMsg(); +#ifdef RARDLL + Cmd->DllError=ERAR_ECREATE; +#endif + ErrHandler.SetErrorCode(RARX_CREATE); + } + if (PrevProcessed) + { +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + if (Cmd->SetCompressedAttr && + (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()) + SetFileCompression(DestFileName,true); +#endif + SetFileHeaderExtra(Cmd,Arc,DestFileName); + SetDirTime(DestFileName, + Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime, + Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime, + Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime); + } +} + + +bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile) +{ + bool Success=true; + wchar Command=Cmd->Command[0]; +#if !defined(SFX_MODULE) + if (Command=='P') + CurFile.SetHandleType(FILE_HANDLESTD); +#endif + if ((Command=='E' || Command=='X') && !Cmd->Test) + { + bool UserReject; + // Specify "write only" mode to avoid OpenIndiana NAS problems + // with SetFileTime and read+write files. + if (!FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) + { + Success=false; + if (!UserReject) + { + ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); +#ifdef RARDLL + Cmd->DllError=ERAR_ECREATE; +#endif + if (!IsNameUsable(DestFileName)) + { + uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName); + + wchar OrigName[ASIZE(DestFileName)]; + wcsncpyz(OrigName,DestFileName,ASIZE(OrigName)); + + MakeNameUsable(DestFileName,true); + + CreatePath(DestFileName,true); + if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true)) + { +#ifndef SFX_MODULE + uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName); +#endif + Success=true; + } + else + ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName); + } + } + } + } + return Success; +} + + +bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName) +{ + bool WrongVer; + if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 archives. + WrongVer=Arc.FileHead.UnpVer>VER_UNPACK5; + else + { +#ifdef SFX_MODULE // SFX can unpack only RAR 2.9 archives. + WrongVer=Arc.FileHead.UnpVer!=VER_UNPACK; +#else // All formats since 1.3 for RAR. + WrongVer=Arc.FileHead.UnpVer<13 || Arc.FileHead.UnpVer>VER_UNPACK; +#endif + } + + // We can unpack stored files regardless of compression version field. + if (Arc.FileHead.Method==0) + WrongVer=false; + + if (WrongVer) + { + ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName); + uiMsg(UIERROR_NEWERRAR,Arc.FileName); + } + return !WrongVer; +} diff --git a/reference/unrar/extract.hpp b/reference/unrar/extract.hpp new file mode 100644 index 00000000..85a21f53 --- /dev/null +++ b/reference/unrar/extract.hpp @@ -0,0 +1,62 @@ +#ifndef _RAR_EXTRACT_ +#define _RAR_EXTRACT_ + +enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT}; + +class CmdExtract +{ + private: + EXTRACT_ARC_CODE ExtractArchive(); + bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize); + void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize); +#ifdef RARDLL + bool ExtrDllGetPassword(); +#else + bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName); +#endif +#if defined(_WIN_ALL) && !defined(SFX_MODULE) + void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd); +#endif + void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName); + bool ExtrCreateFile(Archive &Arc,File &CurFile); + bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName); + + RarTime StartTime; // time when extraction started + + CommandData *Cmd; + + ComprDataIO DataIO; + Unpack *Unp; + unsigned long TotalFileCount; + + unsigned long FileCount; + unsigned long MatchedArgs; + bool FirstFile; + bool AllMatchesExact; + bool ReconstructDone; + + // If any non-zero solid file was successfully unpacked before current. + // If true and if current encrypted file is broken, obviously + // the password is correct and we can report broken CRC without + // any wrong password hints. + bool AnySolidDataUnpackedWell; + + wchar ArcName[NM]; + + bool PasswordAll; + bool PrevProcessed; // If previous file was successfully extracted or tested. + wchar DestFileName[NM]; + bool PasswordCancelled; +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) + bool Fat32,NotFat32; +#endif + public: + CmdExtract(CommandData *Cmd); + ~CmdExtract(); + void DoExtract(); + void ExtractArchiveInit(Archive &Arc); + bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat); + static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize); +}; + +#endif diff --git a/reference/unrar/filcreat.cpp b/reference/unrar/filcreat.cpp new file mode 100644 index 00000000..a64a7d4d --- /dev/null +++ b/reference/unrar/filcreat.cpp @@ -0,0 +1,163 @@ +#include "rar.hpp" + +// If NewFile==NULL, we delete created file after user confirmation. +// It is useful we we need to overwrite an existing folder or file, +// but need user confirmation for that. +bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, + bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly) +{ + if (UserReject!=NULL) + *UserReject=false; +#ifdef _WIN_ALL + bool ShortNameChanged=false; +#endif + while (FileExist(Name)) + { +#if defined(_WIN_ALL) + if (!ShortNameChanged) + { + // Avoid the infinite loop if UpdateExistingShortName returns + // the same name. + ShortNameChanged=true; + + // Maybe our long name matches the short name of existing file. + // Let's check if we can change the short name. + if (UpdateExistingShortName(Name)) + continue; + } + // Allow short name check again. It is necessary, because rename and + // autorename below can change the name, so we need to check it again. + ShortNameChanged=false; +#endif + UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0)); + + if (Choice==UIASKREP_R_REPLACE) + break; + if (Choice==UIASKREP_R_SKIP) + { + if (UserReject!=NULL) + *UserReject=true; + return false; + } + if (Choice==UIASKREP_R_CANCEL) + ErrHandler.Exit(RARX_USERBREAK); + } + + // Try to truncate the existing file first instead of delete, + // so we preserve existing file permissions such as NTFS permissions. + uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD; + if (NewFile!=NULL && NewFile->Create(Name,FileMode)) + return true; + + CreatePath(Name,true); + return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name); +} + + +bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize) +{ + wchar NewName[NM]; + size_t NameLength=wcslen(Name); + wchar *Ext=GetExt(Name); + if (Ext==NULL) + Ext=Name+NameLength; + for (uint FileVer=1;;FileVer++) + { + swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext); + if (!FileExist(NewName)) + { + wcsncpyz(Name,NewName,MaxNameSize); + break; + } + if (FileVer>=1000000) + return false; + } + return true; +} + + +#if defined(_WIN_ALL) +// If we find a file, which short name is equal to 'Name', we try to change +// its short name, while preserving the long name. It helps when unpacking +// an archived file, which long name is equal to short name of already +// existing file. Otherwise we would overwrite the already existing file, +// even though its long name does not match the name of unpacking file. +bool UpdateExistingShortName(const wchar *Name) +{ + wchar LongPathName[NM]; + DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName)); + if (Res==0 || Res>=ASIZE(LongPathName)) + return false; + wchar ShortPathName[NM]; + Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName)); + if (Res==0 || Res>=ASIZE(ShortPathName)) + return false; + wchar *LongName=PointToName(LongPathName); + wchar *ShortName=PointToName(ShortPathName); + + // We continue only if file has a short name, which does not match its + // long name, and this short name is equal to name of file which we need + // to create. + if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 || + wcsicomp(PointToName(Name),ShortName)!=0) + return false; + + // Generate the temporary new name for existing file. + wchar NewName[NM]; + *NewName=0; + for (int I=0;I<10000 && *NewName==0;I+=123) + { + // Here we copy the path part of file to create. We'll make the temporary + // file in the same folder. + wcsncpyz(NewName,Name,ASIZE(NewName)); + + // Here we set the random name part. + swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I); + + // If such file is already exist, try next random name. + if (FileExist(NewName)) + *NewName=0; + } + + // If we could not generate the name not used by any other file, we return. + if (*NewName==0) + return false; + + // FastFind returns the name without path, but we need the fully qualified + // name for renaming, so we use the path from file to create and long name + // from existing file. + wchar FullName[NM]; + wcsncpyz(FullName,Name,ASIZE(FullName)); + SetName(FullName,LongName,ASIZE(FullName)); + + // Rename the existing file to randomly generated name. Normally it changes + // the short name too. + if (!MoveFile(FullName,NewName)) + return false; + + // Now we need to create the temporary empty file with same name as + // short name of our already existing file. We do it to occupy its previous + // short name and not allow to use it again when renaming the file back to + // its original long name. + File KeepShortFile; + bool Created=false; + if (!FileExist(Name)) + Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD); + + // Now we rename the existing file from temporary name to original long name. + // Since its previous short name is occupied by another file, it should + // get another short name. + MoveFile(NewName,FullName); + + if (Created) + { + // Delete the temporary zero length file occupying the short name, + KeepShortFile.Close(); + KeepShortFile.Delete(); + } + // We successfully changed the short name. Maybe sometimes we'll simplify + // this function by use of SetFileShortName Windows API call. + // But SetFileShortName is not available in older Windows. + return true; +} +#endif diff --git a/reference/unrar/filcreat.hpp b/reference/unrar/filcreat.hpp new file mode 100644 index 00000000..44f801d4 --- /dev/null +++ b/reference/unrar/filcreat.hpp @@ -0,0 +1,14 @@ +#ifndef _RAR_FILECREATE_ +#define _RAR_FILECREATE_ + +bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize, + bool *UserReject,int64 FileSize=INT64NDF, + RarTime *FileTime=NULL,bool WriteOnly=false); + +bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize); + +#if defined(_WIN_ALL) +bool UpdateExistingShortName(const wchar *Name); +#endif + +#endif diff --git a/reference/unrar/file.cpp b/reference/unrar/file.cpp new file mode 100644 index 00000000..5e6235a7 --- /dev/null +++ b/reference/unrar/file.cpp @@ -0,0 +1,729 @@ +#include "rar.hpp" + +File::File() +{ + hFile=FILE_BAD_HANDLE; + *FileName=0; + NewFile=false; + LastWrite=false; + HandleType=FILE_HANDLENORMAL; + SkipClose=false; + IgnoreReadErrors=false; + ErrorType=FILE_SUCCESS; + OpenShared=false; + AllowDelete=true; + AllowExceptions=true; +#ifdef _WIN_ALL + NoSequentialRead=false; + CreateMode=FMF_UNDEFINED; +#endif +} + + +File::~File() +{ + if (hFile!=FILE_BAD_HANDLE && !SkipClose) + if (NewFile) + Delete(); + else + Close(); +} + + +void File::operator = (File &SrcFile) +{ + hFile=SrcFile.hFile; + NewFile=SrcFile.NewFile; + LastWrite=SrcFile.LastWrite; + HandleType=SrcFile.HandleType; + wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName)); + SrcFile.SkipClose=true; +} + + +bool File::Open(const wchar *Name,uint Mode) +{ + ErrorType=FILE_SUCCESS; + FileHandle hNewFile; + bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0; + bool UpdateMode=(Mode & FMF_UPDATE)!=0; + bool WriteMode=(Mode & FMF_WRITE)!=0; +#ifdef _WIN_ALL + uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ; + if (UpdateMode) + Access|=GENERIC_WRITE; + uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ; + if (OpenShared) + ShareMode|=FILE_SHARE_WRITE; + uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN; + hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + + DWORD LastError; + if (hNewFile==FILE_BAD_HANDLE) + { + LastError=GetLastError(); + + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + { + hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL); + + // For archive names longer than 260 characters first CreateFile + // (without \\?\) fails and sets LastError to 3 (access denied). + // We need the correct "file not found" error code to decide + // if we create a new archive or quit with "cannot create" error. + // So we need to check the error code after \\?\ CreateFile again, + // otherwise we'll fail to create new archives with long names. + // But we cannot simply assign the new code to LastError, + // because it would break "..\arcname.rar" relative names processing. + // First CreateFile returns the correct "file not found" code for such + // names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating + // dots as a directory name. So we check only for "file not found" + // error here and for other errors use the first CreateFile result. + if (GetLastError()==ERROR_FILE_NOT_FOUND) + LastError=ERROR_FILE_NOT_FOUND; + } + } + if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND) + ErrorType=FILE_NOTFOUND; + +#else + int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY); +#ifdef O_BINARY + flags|=O_BINARY; +#if defined(_AIX) && defined(_LARGE_FILE_API) + flags|=O_LARGEFILE; +#endif +#endif + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + + int handle=open(NameA,flags); +#ifdef LOCK_EX + +#ifdef _OSF_SOURCE + extern "C" int flock(int, int); +#endif + + if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1) + { + close(handle); + return false; + } +#endif + if (handle==-1) + hNewFile=FILE_BAD_HANDLE; + else + { +#ifdef FILE_USE_OPEN + hNewFile=handle; +#else + hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY); +#endif + } + if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT) + ErrorType=FILE_NOTFOUND; +#endif + NewFile=false; + HandleType=FILE_HANDLENORMAL; + SkipClose=false; + bool Success=hNewFile!=FILE_BAD_HANDLE; + if (Success) + { + hFile=hNewFile; + wcsncpyz(FileName,Name,ASIZE(FileName)); + } + return Success; +} + + +#if !defined(SFX_MODULE) +void File::TOpen(const wchar *Name) +{ + if (!WOpen(Name)) + ErrHandler.Exit(RARX_OPEN); +} +#endif + + +bool File::WOpen(const wchar *Name) +{ + if (Open(Name)) + return true; + ErrHandler.OpenErrorMsg(Name); + return false; +} + + +bool File::Create(const wchar *Name,uint Mode) +{ + // OpenIndiana based NAS and CIFS shares fail to set the file time if file + // was created in read+write mode and some data was written and not flushed + // before SetFileTime call. So we should use the write only mode if we plan + // SetFileTime call and do not need to read from file. + bool WriteMode=(Mode & FMF_WRITE)!=0; + bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared; +#ifdef _WIN_ALL + CreateMode=Mode; + uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE; + DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0; + + // Windows automatically removes dots and spaces in the end of file name, + // So we detect such names and process them with \\?\ prefix. + wchar *LastChar=PointToLastChar(Name); + bool Special=*LastChar=='.' || *LastChar==' '; + + if (Special && (Mode & FMF_STANDARDNAMES)==0) + hFile=FILE_BAD_HANDLE; + else + hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); + + if (hFile==FILE_BAD_HANDLE) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL); + } + +#else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); +#ifdef FILE_USE_OPEN + hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666); +#else + hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY); +#endif +#endif + NewFile=true; + HandleType=FILE_HANDLENORMAL; + SkipClose=false; + wcsncpyz(FileName,Name,ASIZE(FileName)); + return hFile!=FILE_BAD_HANDLE; +} + + +#if !defined(SFX_MODULE) +void File::TCreate(const wchar *Name,uint Mode) +{ + if (!WCreate(Name,Mode)) + ErrHandler.Exit(RARX_FATAL); +} +#endif + + +bool File::WCreate(const wchar *Name,uint Mode) +{ + if (Create(Name,Mode)) + return true; + ErrHandler.CreateErrorMsg(Name); + return false; +} + + +bool File::Close() +{ + bool Success=true; + + if (hFile!=FILE_BAD_HANDLE) + { + if (!SkipClose) + { +#ifdef _WIN_ALL + // We use the standard system handle for stdout in Windows + // and it must not be closed here. + if (HandleType==FILE_HANDLENORMAL) + Success=CloseHandle(hFile)==TRUE; +#else +#ifdef FILE_USE_OPEN + Success=close(hFile)!=-1; +#else + Success=fclose(hFile)!=EOF; +#endif +#endif + } + hFile=FILE_BAD_HANDLE; + } + HandleType=FILE_HANDLENORMAL; + if (!Success && AllowExceptions) + ErrHandler.CloseError(FileName); + return Success; +} + + +bool File::Delete() +{ + if (HandleType!=FILE_HANDLENORMAL) + return false; + if (hFile!=FILE_BAD_HANDLE) + Close(); + if (!AllowDelete) + return false; + return DelFile(FileName); +} + + +bool File::Rename(const wchar *NewName) +{ + // No need to rename if names are already same. + bool Success=wcscmp(FileName,NewName)==0; + + if (!Success) + Success=RenameFile(FileName,NewName); + + if (Success) + wcscpy(FileName,NewName); + + return Success; +} + + +bool File::Write(const void *Data,size_t Size) +{ + if (Size==0) + return true; + if (HandleType==FILE_HANDLESTD) + { +#ifdef _WIN_ALL + hFile=GetStdHandle(STD_OUTPUT_HANDLE); +#else + // Cannot use the standard stdout here, because it already has wide orientation. + if (hFile==FILE_BAD_HANDLE) + { +#ifdef FILE_USE_OPEN + hFile=dup(STDOUT_FILENO); // Open new stdout stream. +#else + hFile=fdopen(dup(STDOUT_FILENO),"w"); // Open new stdout stream. +#endif + } +#endif + } + bool Success; + while (1) + { + Success=false; +#ifdef _WIN_ALL + DWORD Written=0; + if (HandleType!=FILE_HANDLENORMAL) + { + // writing to stdout can fail in old Windows if data block is too large + const size_t MaxSize=0x4000; + for (size_t I=0;ISize && FilePos-Size<=0xffffffff && FilePos+Size>0xffffffff) + ErrHandler.WriteErrorFAT(FileName); +#endif + if (ErrHandler.AskRepeatWrite(FileName,false)) + { +#if !defined(_WIN_ALL) && !defined(FILE_USE_OPEN) + clearerr(hFile); +#endif + if (Written0) + Seek(Tell()-Written,SEEK_SET); + continue; + } + ErrHandler.WriteError(NULL,FileName); + } + break; + } + LastWrite=true; + return Success; // It can return false only if AllowExceptions is disabled. +} + + +int File::Read(void *Data,size_t Size) +{ + int64 FilePos=0; // Initialized only to suppress some compilers warning. + + if (IgnoreReadErrors) + FilePos=Tell(); + int ReadSize; + while (true) + { + ReadSize=DirectRead(Data,Size); + if (ReadSize==-1) + { + ErrorType=FILE_READERROR; + if (AllowExceptions) + if (IgnoreReadErrors) + { + ReadSize=0; + for (size_t I=0;IMaxDeviceRead) +// Size=MaxDeviceRead; + hFile=GetStdHandle(STD_INPUT_HANDLE); +#else +#ifdef FILE_USE_OPEN + hFile=STDIN_FILENO; +#else + hFile=stdin; +#endif +#endif + } +#ifdef _WIN_ALL + // For pipes like 'type file.txt | rar -si arcname' ReadFile may return + // data in small ~4KB blocks. It may slightly reduce the compression ratio. + DWORD Read; + if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL)) + { + if (IsDevice() && Size>MaxDeviceRead) + return DirectRead(Data,MaxDeviceRead); + if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE) + return 0; + + // We had a bug report about failure to archive 1C database lock file + // 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB + // permanently locked. If our first read request uses too large buffer + // and if we are in -dh mode, so we were able to open the file, + // we'll fail with "Read error". So now we use try a smaller buffer size + // in case of lock error. + if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead && + GetLastError()==ERROR_LOCK_VIOLATION) + return DirectRead(Data,MaxLockedRead); + + return -1; + } + return Read; +#else +#ifdef FILE_USE_OPEN + ssize_t ReadSize=read(hFile,Data,Size); + if (ReadSize==-1) + return -1; + return (int)ReadSize; +#else + if (LastWrite) + { + fflush(hFile); + LastWrite=false; + } + clearerr(hFile); + size_t ReadSize=fread(Data,1,Size,hFile); + if (ferror(hFile)) + return -1; + return (int)ReadSize; +#endif +#endif +} + + +void File::Seek(int64 Offset,int Method) +{ + if (!RawSeek(Offset,Method) && AllowExceptions) + ErrHandler.SeekError(FileName); +} + + +bool File::RawSeek(int64 Offset,int Method) +{ + if (hFile==FILE_BAD_HANDLE) + return true; + if (Offset<0 && Method!=SEEK_SET) + { + Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset; + Method=SEEK_SET; + } +#ifdef _WIN_ALL + LONG HighDist=(LONG)(Offset>>32); + if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff && + GetLastError()!=NO_ERROR) + return false; +#else + LastWrite=false; +#ifdef FILE_USE_OPEN + if (lseek(hFile,(off_t)Offset,Method)==-1) + return false; +#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS) + if (fseeko(hFile,Offset,Method)!=0) + return false; +#else + if (fseek(hFile,(long)Offset,Method)!=0) + return false; +#endif +#endif + return true; +} + + +int64 File::Tell() +{ + if (hFile==FILE_BAD_HANDLE) + if (AllowExceptions) + ErrHandler.SeekError(FileName); + else + return -1; +#ifdef _WIN_ALL + LONG HighDist=0; + uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT); + if (LowDist==0xffffffff && GetLastError()!=NO_ERROR) + if (AllowExceptions) + ErrHandler.SeekError(FileName); + else + return -1; + return INT32TO64(HighDist,LowDist); +#else +#ifdef FILE_USE_OPEN + return lseek(hFile,0,SEEK_CUR); +#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) + return ftello(hFile); +#else + return ftell(hFile); +#endif +#endif +} + + +void File::Prealloc(int64 Size) +{ +#ifdef _WIN_ALL + if (RawSeek(Size,SEEK_SET)) + { + Truncate(); + Seek(0,SEEK_SET); + } +#endif + +#if defined(_UNIX) && defined(USE_FALLOCATE) + // fallocate is rather new call. Only latest kernels support it. + // So we are not using it by default yet. + int fd = GetFD(); + if (fd >= 0) + fallocate(fd, 0, 0, Size); +#endif +} + + +byte File::GetByte() +{ + byte Byte=0; + Read(&Byte,1); + return Byte; +} + + +void File::PutByte(byte Byte) +{ + Write(&Byte,1); +} + + +bool File::Truncate() +{ +#ifdef _WIN_ALL + return SetEndOfFile(hFile)==TRUE; +#else + return ftruncate(GetFD(),(off_t)Tell())==0; +#endif +} + + +void File::Flush() +{ +#ifdef _WIN_ALL + FlushFileBuffers(hFile); +#else +#ifndef FILE_USE_OPEN + fflush(hFile); +#endif + fsync(GetFD()); +#endif +} + + +void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta) +{ +#ifdef _WIN_ALL + // Workaround for OpenIndiana NAS time bug. If we cannot create a file + // in write only mode, we need to flush the write buffer before calling + // SetFileTime or file time will not be changed. + if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0) + FlushFileBuffers(hFile); + + bool sm=ftm!=NULL && ftm->IsSet(); + bool sc=ftc!=NULL && ftc->IsSet(); + bool sa=fta!=NULL && fta->IsSet(); + FILETIME fm,fc,fa; + if (sm) + ftm->GetWinFT(&fm); + if (sc) + ftc->GetWinFT(&fc); + if (sa) + fta->GetWinFT(&fa); + SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); +#endif +} + + +void File::SetCloseFileTime(RarTime *ftm,RarTime *fta) +{ +// Android APP_PLATFORM := android-14 does not support futimens and futimes. +// Newer platforms support futimens, but fail on Android 4.2. +// We have to use utime for Android. +// Also we noticed futimens fail to set timestamps on NTFS partition +// mounted to virtual Linux x86 machine, but utimensat worked correctly. +// So we set timestamps for already closed files in Unix. +#ifdef _UNIX + SetCloseFileTimeByName(FileName,ftm,fta); +#endif +} + + +void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta) +{ +#ifdef _UNIX + bool setm=ftm!=NULL && ftm->IsSet(); + bool seta=fta!=NULL && fta->IsSet(); + if (setm || seta) + { + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + +#ifdef UNIX_TIME_NS + timespec times[2]; + times[0].tv_sec=seta ? fta->GetUnix() : 0; + times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW; + times[1].tv_sec=setm ? ftm->GetUnix() : 0; + times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW; + utimensat(AT_FDCWD,NameA,times,0); +#else + utimbuf ut; + if (setm) + ut.modtime=ftm->GetUnix(); + else + ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0. + if (seta) + ut.actime=fta->GetUnix(); + else + ut.actime=ut.modtime; // Need to set something, cannot left it 0. + utime(NameA,&ut); +#endif + } +#endif +} + + +void File::GetOpenFileTime(RarTime *ft) +{ +#ifdef _WIN_ALL + FILETIME FileTime; + GetFileTime(hFile,NULL,NULL,&FileTime); + ft->SetWinFT(&FileTime); +#endif +#if defined(_UNIX) || defined(_EMX) + struct stat st; + fstat(GetFD(),&st); + ft->SetUnix(st.st_mtime); +#endif +} + + +int64 File::FileLength() +{ + SaveFilePos SavePos(*this); + Seek(0,SEEK_END); + return Tell(); +} + + +bool File::IsDevice() +{ + if (hFile==FILE_BAD_HANDLE) + return false; +#ifdef _WIN_ALL + uint Type=GetFileType(hFile); + return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE; +#else + return isatty(GetFD()); +#endif +} + + +#ifndef SFX_MODULE +int64 File::Copy(File &Dest,int64 Length) +{ + Array Buffer(0x40000); + int64 CopySize=0; + bool CopyAll=(Length==INT64NDF); + + while (CopyAll || Length>0) + { + Wait(); + size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size(); + char *Buf=&Buffer[0]; + int ReadSize=Read(Buf,SizeToRead); + if (ReadSize==0) + break; + size_t WriteSize=ReadSize; +#ifdef _WIN_ALL + // For FAT32 USB flash drives in Windows if first write is 4 KB or more, + // write caching is disabled and "write through" is enabled, resulting + // in bad performance, especially for many small files. It happens when + // we create SFX archive on USB drive, because SFX module is written first. + // So we split the first write to small 1 KB followed by rest of data. + if (CopySize==0 && WriteSize>=4096) + { + const size_t FirstWrite=1024; + Dest.Write(Buf,FirstWrite); + Buf+=FirstWrite; + WriteSize-=FirstWrite; + } +#endif + Dest.Write(Buf,WriteSize); + CopySize+=ReadSize; + if (!CopyAll) + Length-=ReadSize; + } + return CopySize; +} +#endif diff --git a/reference/unrar/file.hpp b/reference/unrar/file.hpp new file mode 100644 index 00000000..8a7631e3 --- /dev/null +++ b/reference/unrar/file.hpp @@ -0,0 +1,126 @@ +#ifndef _RAR_FILE_ +#define _RAR_FILE_ + +#define FILE_USE_OPEN + +#ifdef _WIN_ALL + typedef HANDLE FileHandle; + #define FILE_BAD_HANDLE INVALID_HANDLE_VALUE +#elif defined(FILE_USE_OPEN) + typedef off_t FileHandle; + #define FILE_BAD_HANDLE -1 +#else + typedef FILE* FileHandle; + #define FILE_BAD_HANDLE NULL +#endif + +class RAROptions; + +enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD}; + +enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR}; + +enum FILE_MODE_FLAGS { + // Request read only access to file. Default for Open. + FMF_READ=0, + + // Request both read and write access to file. Default for Create. + FMF_UPDATE=1, + + // Request write only access to file. + FMF_WRITE=2, + + // Open files which are already opened for write by other programs. + FMF_OPENSHARED=4, + + // Open files only if no other program is opened it even in shared mode. + FMF_OPENEXCLUSIVE=8, + + // Provide read access to created file for other programs. + FMF_SHAREREAD=16, + + // Use standard NTFS names without trailing dots and spaces. + FMF_STANDARDNAMES=32, + + // Mode flags are not defined yet. + FMF_UNDEFINED=256 +}; + + +class File +{ + private: + FileHandle hFile; + bool LastWrite; + FILE_HANDLETYPE HandleType; + bool SkipClose; + bool IgnoreReadErrors; + bool NewFile; + bool AllowDelete; + bool AllowExceptions; +#ifdef _WIN_ALL + bool NoSequentialRead; + uint CreateMode; +#endif + protected: + bool OpenShared; // Set by 'Archive' class. + public: + wchar FileName[NM]; + + FILE_ERRORTYPE ErrorType; + public: + File(); + virtual ~File(); + void operator = (File &SrcFile); + virtual bool Open(const wchar *Name,uint Mode=FMF_READ); + void TOpen(const wchar *Name); + bool WOpen(const wchar *Name); + bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD); + bool Close(); + bool Delete(); + bool Rename(const wchar *NewName); + bool Write(const void *Data,size_t Size); + virtual int Read(void *Data,size_t Size); + int DirectRead(void *Data,size_t Size); + virtual void Seek(int64 Offset,int Method); + bool RawSeek(int64 Offset,int Method); + virtual int64 Tell(); + void Prealloc(int64 Size); + byte GetByte(); + void PutByte(byte Byte); + bool Truncate(); + void Flush(); + void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL); + void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL); + static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta); + void GetOpenFileTime(RarTime *ft); + bool IsOpened() {return hFile!=FILE_BAD_HANDLE;}; + int64 FileLength(); + void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;} + FILE_HANDLETYPE GetHandleType() {return HandleType;} + bool IsDevice(); + static bool RemoveCreated(); + FileHandle GetHandle() {return hFile;} + void SetHandle(FileHandle Handle) {Close();hFile=Handle;} + void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;} + int64 Copy(File &Dest,int64 Length=INT64NDF); + void SetAllowDelete(bool Allow) {AllowDelete=Allow;} + void SetExceptions(bool Allow) {AllowExceptions=Allow;} +#ifdef _WIN_ALL + void RemoveSequentialFlag() {NoSequentialRead=true;} +#endif +#ifdef _UNIX + int GetFD() + { +#ifdef FILE_USE_OPEN + return hFile; +#else + return fileno(hFile); +#endif + } +#endif +}; + +#endif diff --git a/reference/unrar/filefn.cpp b/reference/unrar/filefn.cpp new file mode 100644 index 00000000..4eb7b9b2 --- /dev/null +++ b/reference/unrar/filefn.cpp @@ -0,0 +1,510 @@ +#include "rar.hpp" + +MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr) +{ +#ifdef _WIN_ALL + // Windows automatically removes dots and spaces in the end of directory + // name. So we detect such names and process them with \\?\ prefix. + wchar *LastChar=PointToLastChar(Name); + bool Special=*LastChar=='.' || *LastChar==' '; + BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL); + if (RetCode==0 && !FileExist(Name)) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + RetCode=CreateDirectory(LongName,NULL); + } + if (RetCode!=0) // Non-zero return code means success for CreateDirectory. + { + if (SetAttr) + SetFileAttr(Name,Attr); + return MKDIR_SUCCESS; + } + int ErrCode=GetLastError(); + if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND) + return MKDIR_BADPATH; + return MKDIR_ERROR; +#elif defined(_UNIX) + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + mode_t uattr=SetAttr ? (mode_t)Attr:0777; + int ErrCode=mkdir(NameA,uattr); + if (ErrCode==-1) + return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR; + return MKDIR_SUCCESS; +#else + return MKDIR_ERROR; +#endif +} + + +bool CreatePath(const wchar *Path,bool SkipLastName) +{ + if (Path==NULL || *Path==0) + return false; + +#if defined(_WIN_ALL) || defined(_EMX) + uint DirAttr=0; +#else + uint DirAttr=0777; +#endif + + bool Success=true; + + for (const wchar *s=Path;*s!=0;s++) + { + wchar DirName[NM]; + if (s-Path>=ASIZE(DirName)) + break; + + // Process all kinds of path separators, so user can enter Unix style + // path in Windows or Windows in Unix. s>Path check avoids attempting + // creating an empty directory for paths starting from path separator. + if (IsPathDiv(*s) && s>Path) + { +#ifdef _WIN_ALL + // We must not attempt to create "D:" directory, because first + // CreateDirectory will fail, so we'll use \\?\D:, which forces Wine + // to create "D:" directory. + if (s==Path+2 && Path[1]==':') + continue; +#endif + wcsncpy(DirName,Path,s-Path); + DirName[s-Path]=0; + + Success=MakeDir(DirName,true,DirAttr)==MKDIR_SUCCESS; + if (Success) + { + mprintf(St(MCreatDir),DirName); + mprintf(L" %s",St(MOk)); + } + } + } + if (!SkipLastName && !IsPathDiv(*PointToLastChar(Path))) + Success=MakeDir(Path,true,DirAttr)==MKDIR_SUCCESS; + return Success; +} + + +void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta) +{ +#if defined(_WIN_ALL) + bool sm=ftm!=NULL && ftm->IsSet(); + bool sc=ftc!=NULL && ftc->IsSet(); + bool sa=fta!=NULL && fta->IsSet(); + + uint DirAttr=GetFileAttr(Name); + bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0); + if (ResetAttr) + SetFileAttr(Name,0); + + HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); + if (hFile==INVALID_HANDLE_VALUE) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL); + } + + if (hFile==INVALID_HANDLE_VALUE) + return; + FILETIME fm,fc,fa; + if (sm) + ftm->GetWinFT(&fm); + if (sc) + ftc->GetWinFT(&fc); + if (sa) + fta->GetWinFT(&fa); + SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL); + CloseHandle(hFile); + if (ResetAttr) + SetFileAttr(Name,DirAttr); +#endif +#if defined(_UNIX) || defined(_EMX) + File::SetCloseFileTimeByName(Name,ftm,fta); +#endif +} + + +bool IsRemovable(const wchar *Name) +{ +#if defined(_WIN_ALL) + wchar Root[NM]; + GetPathRoot(Name,Root,ASIZE(Root)); + int Type=GetDriveType(*Root!=0 ? Root:NULL); + return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM; +#else + return false; +#endif +} + + +#ifndef SFX_MODULE +int64 GetFreeDisk(const wchar *Name) +{ +#ifdef _WIN_ALL + wchar Root[NM]; + GetFilePath(Name,Root,ASIZE(Root)); + + ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree; + uiUserFree.u.LowPart=uiUserFree.u.HighPart=0; + if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) && + uiUserFree.u.HighPart<=uiTotalFree.u.HighPart) + return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart); + return 0; +#elif defined(_UNIX) + wchar Root[NM]; + GetFilePath(Name,Root,ASIZE(Root)); + char RootA[NM]; + WideToChar(Root,RootA,ASIZE(RootA)); + struct statvfs sfs; + if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0) + return 0; + int64 FreeSize=sfs.f_bsize; + FreeSize=FreeSize*sfs.f_bavail; + return FreeSize; +#else + return 0; +#endif +} +#endif + + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) +// Return 'true' for FAT and FAT32, so we can adjust the maximum supported +// file size to 4 GB for these file systems. +bool IsFAT(const wchar *Name) +{ + wchar Root[NM]; + GetPathRoot(Name,Root,ASIZE(Root)); + wchar FileSystem[MAX_PATH+1]; + if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem))) + return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0; + return false; +} +#endif + + +bool FileExist(const wchar *Name) +{ +#ifdef _WIN_ALL + return GetFileAttr(Name)!=0xffffffff; +#elif defined(ENABLE_ACCESS) + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + return access(NameA,0)==0; +#else + FindData FD; + return FindFile::FastFind(Name,&FD); +#endif +} + + +bool WildFileExist(const wchar *Name) +{ + if (IsWildcard(Name)) + { + FindFile Find; + Find.SetMask(Name); + FindData fd; + return Find.Next(&fd); + } + return FileExist(Name); +} + + +bool IsDir(uint Attr) +{ +#ifdef _WIN_ALL + return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0; +#endif +#if defined(_UNIX) + return (Attr & 0xF000)==0x4000; +#endif +} + + +bool IsUnreadable(uint Attr) +{ +#if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR) + return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr); +#endif + return false; +} + + +bool IsLink(uint Attr) +{ +#ifdef _UNIX + return (Attr & 0xF000)==0xA000; +#elif defined(_WIN_ALL) + return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0; +#else + return false; +#endif +} + + + + + + +bool IsDeleteAllowed(uint FileAttr) +{ +#ifdef _WIN_ALL + return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0; +#else + return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR); +#endif +} + + +void PrepareToDelete(const wchar *Name) +{ +#if defined(_WIN_ALL) || defined(_EMX) + SetFileAttr(Name,0); +#endif +#ifdef _UNIX + if (Name!=NULL) + { + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR); + } +#endif +} + + +uint GetFileAttr(const wchar *Name) +{ +#ifdef _WIN_ALL + DWORD Attr=GetFileAttributes(Name); + if (Attr==0xffffffff) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Attr=GetFileAttributes(LongName); + } + return Attr; +#else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + struct stat st; + if (stat(NameA,&st)!=0) + return 0; + return st.st_mode; +#endif +} + + +bool SetFileAttr(const wchar *Name,uint Attr) +{ +#ifdef _WIN_ALL + bool Success=SetFileAttributes(Name,Attr)!=0; + if (!Success) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Success=SetFileAttributes(LongName,Attr)!=0; + } + return Success; +#elif defined(_UNIX) + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + return chmod(NameA,(mode_t)Attr)==0; +#else + return false; +#endif +} + + +#if 0 +wchar *MkTemp(wchar *Name,size_t MaxSize) +{ + size_t Length=wcslen(Name); + + RarTime CurTime; + CurTime.SetCurrentTime(); + + // We cannot use CurTime.GetWin() as is, because its lowest bits can + // have low informational value, like being a zero or few fixed numbers. + uint Random=(uint)(CurTime.GetWin()/100000); + + // Using PID we guarantee that different RAR copies use different temp names + // even if started in exactly the same time. + uint PID=0; +#ifdef _WIN_ALL + PID=(uint)GetCurrentProcessId(); +#elif defined(_UNIX) + PID=(uint)getpid(); +#endif + + for (uint Attempt=0;;Attempt++) + { + uint Ext=Random%50000+Attempt; + wchar RndText[50]; + swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext); + if (Length+wcslen(RndText)>=MaxSize || Attempt==1000) + return NULL; + wcscpy(Name+Length,RndText); + if (!FileExist(Name)) + break; + } + return Name; +} +#endif + + +#if !defined(SFX_MODULE) +void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags) +{ + SaveFilePos SavePos(*SrcFile); +#ifndef SILENT + int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size; +#endif + + if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0) + uiMsg(UIEVENT_FILESUMSTART); + + if ((Flags & CALCFSUM_CURPOS)==0) + SrcFile->Seek(0,SEEK_SET); + + const size_t BufSize=0x100000; + Array Data(BufSize); + + + DataHash HashCRC,HashBlake2; + HashCRC.Init(HASH_CRC32,Threads); + HashBlake2.Init(HASH_BLAKE2,Threads); + + int64 BlockCount=0; + int64 TotalRead=0; + while (true) + { + size_t SizeToRead; + if (Size==INT64NDF) // If we process the entire file. + SizeToRead=BufSize; // Then always attempt to read the entire buffer. + else + SizeToRead=(size_t)Min((int64)BufSize,Size); + int ReadSize=SrcFile->Read(&Data[0],SizeToRead); + if (ReadSize==0) + break; + TotalRead+=ReadSize; + + if ((++BlockCount & 0xf)==0) + { +#ifndef SILENT + if ((Flags & CALCFSUM_SHOWPROGRESS)!=0) + uiExtractProgress(TotalRead,FileLength,TotalRead,FileLength); + else + { + if ((Flags & CALCFSUM_SHOWPERCENT)!=0) + uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength)); + } +#endif + Wait(); + } + + if (CRC32!=NULL) + HashCRC.Update(&Data[0],ReadSize); + if (Blake2!=NULL) + HashBlake2.Update(&Data[0],ReadSize); + + if (Size!=INT64NDF) + Size-=ReadSize; + } + if ((Flags & CALCFSUM_SHOWPERCENT)!=0) + uiMsg(UIEVENT_FILESUMEND); + + if (CRC32!=NULL) + *CRC32=HashCRC.GetCRC32(); + if (Blake2!=NULL) + { + HashValue Result; + HashBlake2.Result(&Result); + memcpy(Blake2,Result.Digest,sizeof(Result.Digest)); + } +} +#endif + + +bool RenameFile(const wchar *SrcName,const wchar *DestName) +{ +#ifdef _WIN_ALL + bool Success=MoveFile(SrcName,DestName)!=0; + if (!Success) + { + wchar LongName1[NM],LongName2[NM]; + if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) && + GetWinLongPath(DestName,LongName2,ASIZE(LongName2))) + Success=MoveFile(LongName1,LongName2)!=0; + } + return Success; +#else + char SrcNameA[NM],DestNameA[NM]; + WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA)); + WideToChar(DestName,DestNameA,ASIZE(DestNameA)); + bool Success=rename(SrcNameA,DestNameA)==0; + return Success; +#endif +} + + +bool DelFile(const wchar *Name) +{ +#ifdef _WIN_ALL + bool Success=DeleteFile(Name)!=0; + if (!Success) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + Success=DeleteFile(LongName)!=0; + } + return Success; +#else + char NameA[NM]; + WideToChar(Name,NameA,ASIZE(NameA)); + bool Success=remove(NameA)==0; + return Success; +#endif +} + + + + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool SetFileCompression(const wchar *Name,bool State) +{ + HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA, + FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); + if (hFile==INVALID_HANDLE_VALUE) + { + wchar LongName[NM]; + if (GetWinLongPath(Name,LongName,ASIZE(LongName))) + hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA, + FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL); + } + if (hFile==INVALID_HANDLE_VALUE) + return false; + SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE; + DWORD Result; + int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState, + sizeof(NewState),NULL,0,&Result,NULL); + CloseHandle(hFile); + return RetCode!=0; +} +#endif + + + + + + + + + + diff --git a/reference/unrar/filefn.hpp b/reference/unrar/filefn.hpp new file mode 100644 index 00000000..1bda9b58 --- /dev/null +++ b/reference/unrar/filefn.hpp @@ -0,0 +1,50 @@ +#ifndef _RAR_FILEFN_ +#define _RAR_FILEFN_ + +enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH}; + +MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr); +bool CreatePath(const wchar *Path,bool SkipLastName); +void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta); +bool IsRemovable(const wchar *Name); + +#ifndef SFX_MODULE +int64 GetFreeDisk(const wchar *Name); +#endif + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT) +bool IsFAT(const wchar *Root); +#endif + +bool FileExist(const wchar *Name); +bool WildFileExist(const wchar *Name); +bool IsDir(uint Attr); +bool IsUnreadable(uint Attr); +bool IsLink(uint Attr); +void SetSFXMode(const wchar *FileName); +void EraseDiskContents(const wchar *FileName); +bool IsDeleteAllowed(uint FileAttr); +void PrepareToDelete(const wchar *Name); +uint GetFileAttr(const wchar *Name); +bool SetFileAttr(const wchar *Name,uint Attr); +#if 0 +wchar* MkTemp(wchar *Name,size_t MaxSize); +#endif + +enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8}; + +void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0); + +bool RenameFile(const wchar *SrcName,const wchar *DestName); +bool DelFile(const wchar *Name); +bool DelDir(const wchar *Name); + +#if defined(_WIN_ALL) && !defined(SFX_MODULE) +bool SetFileCompression(const wchar *Name,bool State); +#endif + + + + + +#endif diff --git a/reference/unrar/filestr.cpp b/reference/unrar/filestr.cpp new file mode 100644 index 00000000..4a53132f --- /dev/null +++ b/reference/unrar/filestr.cpp @@ -0,0 +1,162 @@ +#include "rar.hpp" + +bool ReadTextFile( + const wchar *Name, + StringList *List, + bool Config, + bool AbortOnError, + RAR_CHARSET SrcCharset, + bool Unquote, + bool SkipComments, + bool ExpandEnvStr) +{ + wchar FileName[NM]; + *FileName=0; + + if (Name!=NULL) + if (Config) + GetConfigName(Name,FileName,ASIZE(FileName),true,false); + else + wcsncpyz(FileName,Name,ASIZE(FileName)); + + File SrcFile; + if (*FileName!=0) + { + bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0); + + if (!OpenCode) + { + if (AbortOnError) + ErrHandler.Exit(RARX_OPEN); + return false; + } + } + else + SrcFile.SetHandleType(FILE_HANDLESTD); + + uint DataSize=0,ReadSize; + const int ReadBlock=4096; + + Array Data(ReadBlock); + while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0) + { + DataSize+=ReadSize; + Data.Add(ReadSize); // Always have ReadBlock available for next data. + } + // Set to really read size, so we can zero terminate it correctly. + Data.Alloc(DataSize); + + int LowEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0; + int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0; + bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf; + + if (SrcCharset==RCH_DEFAULT) + { + if (LowEndian || BigEndian) + for (size_t I=2;I DataW; + + if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI) + { + Data.Push(0); // Zero terminate. +#if defined(_WIN_ALL) + if (SrcCharset==RCH_OEM) + OemToCharA((char *)&Data[0],(char *)&Data[0]); +#endif + DataW.Alloc(Data.Size()); + CharToWide((char *)&Data[0],&DataW[0],DataW.Size()); + } + + if (SrcCharset==RCH_UNICODE) + { + size_t Start=2; // Skip byte order mark. + if (!LowEndian && !BigEndian) // No byte order mask. + { + Start=0; + LowEndian=1; + } + + DataW.Alloc(Data.Size()/2+1); + size_t End=Data.Size() & ~1; // We need even bytes number for UTF-16. + for (size_t I=Start;I=CurStr;SpacePtr--) + { + if (*SpacePtr!=' ' && *SpacePtr!='\t') + break; + *SpacePtr=0; + } + + if (Unquote && *CurStr=='\"') + { + size_t Length=wcslen(CurStr); + if (CurStr[Length-1]=='\"') + { + CurStr[Length-1]=0; + CurStr++; + } + } + + bool Expanded=false; +#if defined(_WIN_ALL) + if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows. + { + wchar ExpName[NM]; + *ExpName=0; + DWORD Result=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName)); + Expanded=Result!=0 && ResultAddString(ExpName); + } +#endif + if (!Expanded && *CurStr!=0) + List->AddString(CurStr); + + if (Done) + break; + CurStr=NextStr+1; + while (*CurStr=='\r' || *CurStr=='\n') + CurStr++; + } + return true; +} diff --git a/reference/unrar/filestr.hpp b/reference/unrar/filestr.hpp new file mode 100644 index 00000000..8dfdf64b --- /dev/null +++ b/reference/unrar/filestr.hpp @@ -0,0 +1,15 @@ +#ifndef _RAR_FILESTR_ +#define _RAR_FILESTR_ + +bool ReadTextFile( + const wchar *Name, + StringList *List, + bool Config, + bool AbortOnError=false, + RAR_CHARSET SrcCharset=RCH_DEFAULT, + bool Unquote=false, + bool SkipComments=false, + bool ExpandEnvStr=false +); + +#endif diff --git a/reference/unrar/find.cpp b/reference/unrar/find.cpp new file mode 100644 index 00000000..812af6b3 --- /dev/null +++ b/reference/unrar/find.cpp @@ -0,0 +1,218 @@ +#include "rar.hpp" + +FindFile::FindFile() +{ + *FindMask=0; + FirstCall=true; +#ifdef _WIN_ALL + hFind=INVALID_HANDLE_VALUE; +#else + dirp=NULL; +#endif +} + + +FindFile::~FindFile() +{ +#ifdef _WIN_ALL + if (hFind!=INVALID_HANDLE_VALUE) + FindClose(hFind); +#else + if (dirp!=NULL) + closedir(dirp); +#endif +} + + +void FindFile::SetMask(const wchar *Mask) +{ + wcscpy(FindMask,Mask); + FirstCall=true; +} + + +bool FindFile::Next(FindData *fd,bool GetSymLink) +{ + fd->Error=false; + if (*FindMask==0) + return false; +#ifdef _WIN_ALL + if (FirstCall) + { + if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd))==INVALID_HANDLE_VALUE) + return false; + } + else + if (Win32Find(hFind,FindMask,fd)==INVALID_HANDLE_VALUE) + return false; +#else + if (FirstCall) + { + wchar DirName[NM]; + wcsncpyz(DirName,FindMask,ASIZE(DirName)); + RemoveNameFromPath(DirName); + if (*DirName==0) + wcscpy(DirName,L"."); + char DirNameA[NM]; + WideToChar(DirName,DirNameA,ASIZE(DirNameA)); + if ((dirp=opendir(DirNameA))==NULL) + { + fd->Error=(errno!=ENOENT); + return false; + } + } + while (1) + { + struct dirent *ent=readdir(dirp); + if (ent==NULL) + return false; + if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0) + continue; + wchar Name[NM]; + if (!CharToWide(ent->d_name,Name,ASIZE(Name))) + uiMsg(UIERROR_INVALIDNAME,UINULL,Name); + + if (CmpName(FindMask,Name,MATCH_NAMES)) + { + wchar FullName[NM]; + wcscpy(FullName,FindMask); + *PointToName(FullName)=0; + if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1) + { + uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name); + return false; + } + wcscat(FullName,Name); + if (!FastFind(FullName,fd,GetSymLink)) + { + ErrHandler.OpenErrorMsg(FullName); + continue; + } + wcscpy(fd->Name,FullName); + break; + } + } +#endif + fd->Flags=0; + fd->IsDir=IsDir(fd->FileAttr); + fd->IsLink=IsLink(fd->FileAttr); + + FirstCall=false; + wchar *NameOnly=PointToName(fd->Name); + if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0) + return Next(fd); + return true; +} + + +bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink) +{ + fd->Error=false; +#ifndef _UNIX + if (IsWildcard(FindMask)) + return false; +#endif +#ifdef _WIN_ALL + HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd); + if (hFind==INVALID_HANDLE_VALUE) + return false; + FindClose(hFind); +#else + char FindMaskA[NM]; + WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA)); + + struct stat st; + if (GetSymLink) + { +#ifdef SAVE_LINKS + if (lstat(FindMaskA,&st)!=0) +#else + if (stat(FindMaskA,&st)!=0) +#endif + { + fd->Error=(errno!=ENOENT); + return false; + } + } + else + if (stat(FindMaskA,&st)!=0) + { + fd->Error=(errno!=ENOENT); + return false; + } + fd->FileAttr=st.st_mode; + fd->Size=st.st_size; + +#ifdef UNIX_TIME_NS + fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec); + fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec); + fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec); +#else + fd->mtime.SetUnix(st.st_mtime); + fd->atime.SetUnix(st.st_atime); + fd->ctime.SetUnix(st.st_ctime); +#endif + + wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name)); +#endif + fd->Flags=0; + fd->IsDir=IsDir(fd->FileAttr); + fd->IsLink=IsLink(fd->FileAttr); + + return true; +} + + +#ifdef _WIN_ALL +HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd) +{ + WIN32_FIND_DATA FindData; + if (hFind==INVALID_HANDLE_VALUE) + { + hFind=FindFirstFile(Mask,&FindData); + if (hFind==INVALID_HANDLE_VALUE) + { + wchar LongMask[NM]; + if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask))) + hFind=FindFirstFile(LongMask,&FindData); + } + if (hFind==INVALID_HANDLE_VALUE) + { + int SysErr=GetLastError(); + // We must not issue an error for "file not found" and "path not found", + // because it is normal to not find anything for wildcard mask when + // archiving. Also searching for non-existent file is normal in some + // other modules, like WinRAR scanning for winrar_theme_description.txt + // to check if any themes are available. + fd->Error=SysErr!=ERROR_FILE_NOT_FOUND && + SysErr!=ERROR_PATH_NOT_FOUND && + SysErr!=ERROR_NO_MORE_FILES; + } + } + else + if (!FindNextFile(hFind,&FindData)) + { + hFind=INVALID_HANDLE_VALUE; + fd->Error=GetLastError()!=ERROR_NO_MORE_FILES; + } + + if (hFind!=INVALID_HANDLE_VALUE) + { + wcsncpyz(fd->Name,Mask,ASIZE(fd->Name)); + SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name)); + fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow); + fd->FileAttr=FindData.dwFileAttributes; + fd->ftCreationTime=FindData.ftCreationTime; + fd->ftLastAccessTime=FindData.ftLastAccessTime; + fd->ftLastWriteTime=FindData.ftLastWriteTime; + fd->mtime.SetWinFT(&FindData.ftLastWriteTime); + fd->ctime.SetWinFT(&FindData.ftCreationTime); + fd->atime.SetWinFT(&FindData.ftLastAccessTime); + + + } + fd->Flags=0; + return hFind; +} +#endif + diff --git a/reference/unrar/find.hpp b/reference/unrar/find.hpp new file mode 100644 index 00000000..250637f8 --- /dev/null +++ b/reference/unrar/find.hpp @@ -0,0 +1,49 @@ +#ifndef _RAR_FINDDATA_ +#define _RAR_FINDDATA_ + +enum FINDDATA_FLAGS { + FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode. +}; + +struct FindData +{ + wchar Name[NM]; + uint64 Size; + uint FileAttr; + bool IsDir; + bool IsLink; + RarTime mtime; + RarTime ctime; + RarTime atime; +#ifdef _WIN_ALL + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; +#endif + uint Flags; + bool Error; +}; + +class FindFile +{ + private: +#ifdef _WIN_ALL + static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd); +#endif + + wchar FindMask[NM]; + bool FirstCall; +#ifdef _WIN_ALL + HANDLE hFind; +#else + DIR *dirp; +#endif + public: + FindFile(); + ~FindFile(); + void SetMask(const wchar *Mask); + bool Next(FindData *fd,bool GetSymLink=false); + static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false); +}; + +#endif diff --git a/reference/unrar/getbits.cpp b/reference/unrar/getbits.cpp new file mode 100644 index 00000000..e4db2695 --- /dev/null +++ b/reference/unrar/getbits.cpp @@ -0,0 +1,52 @@ +#include "rar.hpp" + +BitInput::BitInput(bool AllocBuffer) +{ + ExternalBuffer=false; + if (AllocBuffer) + { + // getbits32 attempts to read data from InAddr, ... InAddr+3 positions. + // So let's allocate 3 additional bytes for situation, when we need to + // read only 1 byte from the last position of buffer and avoid a crash + // from access to next 3 bytes, which contents we do not need. + size_t BufSize=MAX_SIZE+3; + InBuf=new byte[BufSize]; + + // Ensure that we get predictable results when accessing bytes in area + // not filled with read data. + memset(InBuf,0,BufSize); + } + else + InBuf=NULL; +} + + +BitInput::~BitInput() +{ + if (!ExternalBuffer) + delete[] InBuf; +} + + +void BitInput::faddbits(uint Bits) +{ + // Function wrapped version of inline addbits to save code size. + addbits(Bits); +} + + +uint BitInput::fgetbits() +{ + // Function wrapped version of inline getbits to save code size. + return getbits(); +} + + +void BitInput::SetExternalBuffer(byte *Buf) +{ + if (InBuf!=NULL && !ExternalBuffer) + delete[] InBuf; + InBuf=Buf; + ExternalBuffer=true; +} + diff --git a/reference/unrar/getbits.hpp b/reference/unrar/getbits.hpp new file mode 100644 index 00000000..2e151da9 --- /dev/null +++ b/reference/unrar/getbits.hpp @@ -0,0 +1,68 @@ +#ifndef _RAR_GETBITS_ +#define _RAR_GETBITS_ + +class BitInput +{ + public: + enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer. + + int InAddr; // Curent byte position in the buffer. + int InBit; // Current bit position in the current byte. + + bool ExternalBuffer; + public: + BitInput(bool AllocBuffer); + ~BitInput(); + + byte *InBuf; // Dynamically allocated input buffer. + + void InitBitInput() + { + InAddr=InBit=0; + } + + // Move forward by 'Bits' bits. + void addbits(uint Bits) + { + Bits+=InBit; + InAddr+=Bits>>3; + InBit=Bits&7; + } + + // Return 16 bits from current position in the buffer. + // Bit at (InAddr,InBit) has the highest position in returning data. + uint getbits() + { + uint BitField=(uint)InBuf[InAddr] << 16; + BitField|=(uint)InBuf[InAddr+1] << 8; + BitField|=(uint)InBuf[InAddr+2]; + BitField >>= (8-InBit); + return BitField & 0xffff; + } + + // Return 32 bits from current position in the buffer. + // Bit at (InAddr,InBit) has the highest position in returning data. + uint getbits32() + { + uint BitField=(uint)InBuf[InAddr] << 24; + BitField|=(uint)InBuf[InAddr+1] << 16; + BitField|=(uint)InBuf[InAddr+2] << 8; + BitField|=(uint)InBuf[InAddr+3]; + BitField <<= InBit; + BitField|=(uint)InBuf[InAddr+4] >> (8-InBit); + return BitField & 0xffffffff; + } + + void faddbits(uint Bits); + uint fgetbits(); + + // Check if buffer has enough space for IncPtr bytes. Returns 'true' + // if buffer will be overflown. + bool Overflow(uint IncPtr) + { + return InAddr+IncPtr>=MAX_SIZE; + } + + void SetExternalBuffer(byte *Buf); +}; +#endif diff --git a/reference/unrar/global.cpp b/reference/unrar/global.cpp new file mode 100644 index 00000000..3975813a --- /dev/null +++ b/reference/unrar/global.cpp @@ -0,0 +1,7 @@ +#define INCLUDEGLOBAL + +#if defined(__BORLANDC__) || defined(_MSC_VER) +#pragma hdrstop +#endif + +#include "rar.hpp" diff --git a/reference/unrar/global.hpp b/reference/unrar/global.hpp new file mode 100644 index 00000000..35c6cf91 --- /dev/null +++ b/reference/unrar/global.hpp @@ -0,0 +1,14 @@ +#ifndef _RAR_GLOBAL_ +#define _RAR_GLOBAL_ + +#ifdef INCLUDEGLOBAL + #define EXTVAR +#else + #define EXTVAR extern +#endif + +EXTVAR ErrorHandler ErrHandler; + + + +#endif diff --git a/reference/unrar/hardlinks.cpp b/reference/unrar/hardlinks.cpp new file mode 100644 index 00000000..cf0b25a9 --- /dev/null +++ b/reference/unrar/hardlinks.cpp @@ -0,0 +1,34 @@ +bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize) +{ + SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives. + + if (!FileExist(NameExisting)) + return false; + CreatePath(NameNew,true); + +#ifdef _WIN_ALL + bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0; + if (!Success) + { + uiMsg(UIERROR_HLINKCREATE,NameNew); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CREATE); + } + return Success; +#elif defined(_UNIX) + char NameExistingA[NM],NameNewA[NM]; + WideToChar(NameExisting,NameExistingA,ASIZE(NameExistingA)); + WideToChar(NameNew,NameNewA,ASIZE(NameNewA)); + bool Success=link(NameExistingA,NameNewA)==0; + if (!Success) + { + uiMsg(UIERROR_HLINKCREATE,NameNew); + ErrHandler.SysErrMsg(); + ErrHandler.SetErrorCode(RARX_CREATE); + } + return Success; +#else + return false; +#endif +} + diff --git a/reference/unrar/hash.cpp b/reference/unrar/hash.cpp new file mode 100644 index 00000000..42791f4f --- /dev/null +++ b/reference/unrar/hash.cpp @@ -0,0 +1,135 @@ +#include "rar.hpp" + +void HashValue::Init(HASH_TYPE Type) +{ + HashValue::Type=Type; + + // Zero length data CRC32 is 0. It is important to set it when creating + // headers with no following data like directories or symlinks. + if (Type==HASH_RAR14 || Type==HASH_CRC32) + CRC32=0; + if (Type==HASH_BLAKE2) + { + // dd0e891776933f43c7d032b08a917e25741f8aa9a12c12e1cac8801500f2ca4f + // is BLAKE2sp hash of empty data. We init the structure to this value, + // so if we create a file or service header with no following data like + // "file copy" or "symlink", we set the checksum to proper value avoiding + // additional header type or size checks when extracting. + static byte EmptyHash[32]={ + 0xdd, 0x0e, 0x89, 0x17, 0x76, 0x93, 0x3f, 0x43, + 0xc7, 0xd0, 0x32, 0xb0, 0x8a, 0x91, 0x7e, 0x25, + 0x74, 0x1f, 0x8a, 0xa9, 0xa1, 0x2c, 0x12, 0xe1, + 0xca, 0xc8, 0x80, 0x15, 0x00, 0xf2, 0xca, 0x4f + }; + memcpy(Digest,EmptyHash,sizeof(Digest)); + } +} + + +bool HashValue::operator == (const HashValue &cmp) +{ + if (Type==HASH_NONE || cmp.Type==HASH_NONE) + return true; + if (Type==HASH_RAR14 && cmp.Type==HASH_RAR14 || + Type==HASH_CRC32 && cmp.Type==HASH_CRC32) + return CRC32==cmp.CRC32; + if (Type==HASH_BLAKE2 && cmp.Type==HASH_BLAKE2) + return memcmp(Digest,cmp.Digest,sizeof(Digest))==0; + return false; +} + + +DataHash::DataHash() +{ + blake2ctx=NULL; + HashType=HASH_NONE; +#ifdef RAR_SMP + ThPool=NULL; + MaxThreads=0; +#endif +} + + +DataHash::~DataHash() +{ +#ifdef RAR_SMP + DestroyThreadPool(ThPool); +#endif + cleandata(&CurCRC32, sizeof(CurCRC32)); + if (blake2ctx!=NULL) + { + cleandata(blake2ctx, sizeof(blake2sp_state)); + delete blake2ctx; + } +} + + +void DataHash::Init(HASH_TYPE Type,uint MaxThreads) +{ + if (blake2ctx==NULL) + blake2ctx=new blake2sp_state; + HashType=Type; + if (Type==HASH_RAR14) + CurCRC32=0; + if (Type==HASH_CRC32) + CurCRC32=0xffffffff; // Initial CRC32 value. + if (Type==HASH_BLAKE2) + blake2sp_init(blake2ctx); +#ifdef RAR_SMP + DataHash::MaxThreads=Min(MaxThreads,MaxHashThreads); +#endif +} + + +void DataHash::Update(const void *Data,size_t DataSize) +{ +#ifndef SFX_MODULE + if (HashType==HASH_RAR14) + CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize); +#endif + if (HashType==HASH_CRC32) + CurCRC32=CRC32(CurCRC32,Data,DataSize); + + if (HashType==HASH_BLAKE2) + { +#ifdef RAR_SMP + if (MaxThreads>1 && ThPool==NULL) + ThPool=CreateThreadPool(); + blake2ctx->ThPool=ThPool; + blake2ctx->MaxThreads=MaxThreads; +#endif + blake2sp_update( blake2ctx, (byte *)Data, DataSize); + } +} + + +void DataHash::Result(HashValue *Result) +{ + Result->Type=HashType; + if (HashType==HASH_RAR14) + Result->CRC32=CurCRC32; + if (HashType==HASH_CRC32) + Result->CRC32=CurCRC32^0xffffffff; + if (HashType==HASH_BLAKE2) + { + // Preserve the original context, so we can continue hashing if necessary. + blake2sp_state res=*blake2ctx; + blake2sp_final(&res,Result->Digest); + } +} + + +uint DataHash::GetCRC32() +{ + return HashType==HASH_CRC32 ? CurCRC32^0xffffffff : 0; +} + + +bool DataHash::Cmp(HashValue *CmpValue,byte *Key) +{ + HashValue Final; + Result(&Final); + if (Key!=NULL) + ConvertHashToMAC(&Final,Key); + return Final==*CmpValue; +} diff --git a/reference/unrar/hash.hpp b/reference/unrar/hash.hpp new file mode 100644 index 00000000..b7d879f6 --- /dev/null +++ b/reference/unrar/hash.hpp @@ -0,0 +1,52 @@ +#ifndef _RAR_DATAHASH_ +#define _RAR_DATAHASH_ + +enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2}; + +struct HashValue +{ + void Init(HASH_TYPE Type); + bool operator == (const HashValue &cmp); + bool operator != (const HashValue &cmp) {return !(*this==cmp);} + + HASH_TYPE Type; + union + { + uint CRC32; + byte Digest[SHA256_DIGEST_SIZE]; + }; +}; + + +#ifdef RAR_SMP +class ThreadPool; +class DataHash; +#endif + + +class DataHash +{ + private: + HASH_TYPE HashType; + uint CurCRC32; + blake2sp_state *blake2ctx; + +#ifdef RAR_SMP + ThreadPool *ThPool; + + uint MaxThreads; + // Upper limit for maximum threads to prevent wasting threads in pool. + static const uint MaxHashThreads=8; +#endif + public: + DataHash(); + ~DataHash(); + void Init(HASH_TYPE Type,uint MaxThreads); + void Update(const void *Data,size_t DataSize); + void Result(HashValue *Result); + uint GetCRC32(); + bool Cmp(HashValue *CmpValue,byte *Key); + HASH_TYPE Type() {return HashType;} +}; + +#endif diff --git a/reference/unrar/headers.cpp b/reference/unrar/headers.cpp new file mode 100644 index 00000000..b042dc39 --- /dev/null +++ b/reference/unrar/headers.cpp @@ -0,0 +1,61 @@ +#include "rar.hpp" + +void FileHeader::Reset(size_t SubDataSize) +{ + SubData.Alloc(SubDataSize); + BaseBlock::Reset(); + FileHash.Init(HASH_NONE); + mtime.Reset(); + atime.Reset(); + ctime.Reset(); + SplitBefore=false; + SplitAfter=false; + + UnknownUnpSize=0; + + SubFlags=0; // Important for RAR 3.0 subhead. + + CryptMethod=CRYPT_NONE; + Encrypted=false; + SaltSet=false; + UsePswCheck=false; + UseHashKey=false; + Lg2Count=0; + + Solid=false; + Dir=false; + WinSize=0; + Inherited=false; + SubBlock=false; + CommentInHeader=false; + Version=false; + LargeFile=false; + + RedirType=FSREDIR_NONE; + DirTarget=false; + UnixOwnerSet=false; +} + + +FileHeader& FileHeader::operator = (FileHeader &hd) +{ + SubData.Reset(); + memcpy(this,&hd,sizeof(*this)); + SubData.CleanData(); + SubData=hd.SubData; + return *this; +} + + +void MainHeader::Reset() +{ + HighPosAV=0; + PosAV=0; + CommentInHeader=false; + PackComment=false; + Locator=false; + QOpenOffset=0; + QOpenMaxSize=0; + RROffset=0; + RRMaxSize=0; +} diff --git a/reference/unrar/headers.hpp b/reference/unrar/headers.hpp new file mode 100644 index 00000000..94864789 --- /dev/null +++ b/reference/unrar/headers.hpp @@ -0,0 +1,380 @@ +#ifndef _RAR_HEADERS_ +#define _RAR_HEADERS_ + +#define SIZEOF_MARKHEAD3 7 // Size of RAR 4.x archive mark header. +#define SIZEOF_MAINHEAD14 7 // Size of RAR 1.4 main archive header. +#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header. +#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header. +#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header. +#define SIZEOF_SHORTBLOCKHEAD 7 +#define SIZEOF_LONGBLOCKHEAD 11 +#define SIZEOF_SUBBLOCKHEAD 14 +#define SIZEOF_COMMHEAD 13 +#define SIZEOF_PROTECTHEAD 26 +#define SIZEOF_AVHEAD 14 +#define SIZEOF_SIGNHEAD 15 +#define SIZEOF_UOHEAD 18 +#define SIZEOF_MACHEAD 22 +#define SIZEOF_EAHEAD 24 +#define SIZEOF_BEEAHEAD 24 +#define SIZEOF_STREAMHEAD 26 + +#define VER_PACK 29 +#define VER_PACK5 50 // It is stored as 0, but we subtract 50 when saving an archive. +#define VER_UNPACK 29 +#define VER_UNPACK5 50 // It is stored as 0, but we add 50 when reading an archive. + +#define MHD_VOLUME 0x0001U + +// Old style main archive comment embed into main archive header. Must not +// be used in new archives anymore. +#define MHD_COMMENT 0x0002U + +#define MHD_LOCK 0x0004U +#define MHD_SOLID 0x0008U +#define MHD_PACK_COMMENT 0x0010U +#define MHD_NEWNUMBERING 0x0010U +#define MHD_AV 0x0020U +#define MHD_PROTECT 0x0040U +#define MHD_PASSWORD 0x0080U +#define MHD_FIRSTVOLUME 0x0100U + +#define LHD_SPLIT_BEFORE 0x0001U +#define LHD_SPLIT_AFTER 0x0002U +#define LHD_PASSWORD 0x0004U + +// Old style file comment embed into file header. Must not be used +// in new archives anymore. +#define LHD_COMMENT 0x0008U + +// For non-file subheaders it denotes 'subblock having a parent file' flag. +#define LHD_SOLID 0x0010U + + +#define LHD_WINDOWMASK 0x00e0U +#define LHD_WINDOW64 0x0000U +#define LHD_WINDOW128 0x0020U +#define LHD_WINDOW256 0x0040U +#define LHD_WINDOW512 0x0060U +#define LHD_WINDOW1024 0x0080U +#define LHD_WINDOW2048 0x00a0U +#define LHD_WINDOW4096 0x00c0U +#define LHD_DIRECTORY 0x00e0U + +#define LHD_LARGE 0x0100U +#define LHD_UNICODE 0x0200U +#define LHD_SALT 0x0400U +#define LHD_VERSION 0x0800U +#define LHD_EXTTIME 0x1000U + +#define SKIP_IF_UNKNOWN 0x4000U +#define LONG_BLOCK 0x8000U + +#define EARC_NEXT_VOLUME 0x0001U // Not last volume. +#define EARC_DATACRC 0x0002U // Store CRC32 of RAR archive (now is used only in volumes). +#define EARC_REVSPACE 0x0004U // Reserve space for end of REV file 7 byte record. +#define EARC_VOLNUMBER 0x0008U // Store a number of current volume. + +enum HEADER_TYPE { + // RAR 5.0 header types. + HEAD_MARK=0x00, HEAD_MAIN=0x01, HEAD_FILE=0x02, HEAD_SERVICE=0x03, + HEAD_CRYPT=0x04, HEAD_ENDARC=0x05, HEAD_UNKNOWN=0xff, + + // RAR 1.5 - 4.x header types. + HEAD3_MARK=0x72,HEAD3_MAIN=0x73,HEAD3_FILE=0x74,HEAD3_CMT=0x75, + HEAD3_AV=0x76,HEAD3_OLDSERVICE=0x77,HEAD3_PROTECT=0x78,HEAD3_SIGN=0x79, + HEAD3_SERVICE=0x7a,HEAD3_ENDARC=0x7b +}; + +enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103, + NTACL_HEAD=0x104,STREAM_HEAD=0x105 }; + + +// Internal implementation, depends on archive format version. +enum HOST_SYSTEM { + // RAR 5.0 host OS + HOST5_WINDOWS=0,HOST5_UNIX=1, + + // RAR 3.0 host OS. + HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4, + HOST_BEOS=5,HOST_MAX +}; + +// Unified archive format independent implementation. +enum HOST_SYSTEM_TYPE { + HSYS_WINDOWS, HSYS_UNIX, HSYS_UNKNOWN +}; + + +// We also use these values in extra field, so do not modify them. +enum FILE_SYSTEM_REDIRECT { + FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION, + FSREDIR_HARDLINK, FSREDIR_FILECOPY +}; + + +#define SUBHEAD_TYPE_CMT L"CMT" +#define SUBHEAD_TYPE_QOPEN L"QO" +#define SUBHEAD_TYPE_ACL L"ACL" +#define SUBHEAD_TYPE_STREAM L"STM" +#define SUBHEAD_TYPE_UOWNER L"UOW" +#define SUBHEAD_TYPE_AV L"AV" +#define SUBHEAD_TYPE_RR L"RR" +#define SUBHEAD_TYPE_OS2EA L"EA2" + +/* new file inherits a subblock when updating a host file */ +#define SUBHEAD_FLAGS_INHERITED 0x80000000 + +#define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001 + + +struct MarkHeader +{ + byte Mark[8]; + + // Following fields are virtual and not present in real blocks. + uint HeadSize; +}; + + +struct BaseBlock +{ + uint HeadCRC; // 'ushort' for RAR 1.5. + HEADER_TYPE HeaderType; // 1 byte for RAR 1.5. + uint Flags; // 'ushort' for RAR 1.5. + uint HeadSize; // 'ushort' for RAR 1.5, up to 2 MB for RAR 5.0. + + bool SkipIfUnknown; + + void Reset() + { + SkipIfUnknown=false; + } +}; + + +struct BlockHeader:BaseBlock +{ + uint DataSize; +}; + + +struct MainHeader:BaseBlock +{ + ushort HighPosAV; + uint PosAV; + bool CommentInHeader; + bool PackComment; // For RAR 1.4 archive format only. + bool Locator; + uint64 QOpenOffset; // Offset of quick list record. + uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field. + uint64 RROffset; // Offset of recovery record. + uint64 RRMaxSize; // Maximum size of RR offset in locator extra field. + void Reset(); +}; + + +struct FileHeader:BlockHeader +{ + byte HostOS; + byte UnpVer; + byte Method; + union { + uint FileAttr; + uint SubFlags; + }; + wchar FileName[NM]; + + Array SubData; + + RarTime mtime; + RarTime ctime; + RarTime atime; + + int64 PackSize; + int64 UnpSize; + int64 MaxSize; // Reserve size bytes for vint of this size. + + HashValue FileHash; + + uint FileFlags; + + bool SplitBefore; + bool SplitAfter; + + bool UnknownUnpSize; + + bool Encrypted; + CRYPT_METHOD CryptMethod; + bool SaltSet; + byte Salt[SIZE_SALT50]; + byte InitV[SIZE_INITV]; + bool UsePswCheck; + byte PswCheck[SIZE_PSWCHECK]; + + // Use HMAC calculated from HashKey and checksum instead of plain checksum. + bool UseHashKey; + + // Key to convert checksum to HMAC. Derived from password with PBKDF2 + // using additional iterations. + byte HashKey[SHA256_DIGEST_SIZE]; + + uint Lg2Count; // Log2 of PBKDF2 repetition count. + + bool Solid; + bool Dir; + bool CommentInHeader; // RAR 2.0 file comment. + bool Version; // name.ext;ver file name containing the version number. + size_t WinSize; + bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only). + + // 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0. + bool LargeFile; + + // 'true' for HEAD_SERVICE block, which is a child of preceding file block. + // RAR 4.x uses 'solid' flag to indicate child subheader blocks in archives. + bool SubBlock; + + HOST_SYSTEM_TYPE HSType; + + FILE_SYSTEM_REDIRECT RedirType; + wchar RedirName[NM]; + bool DirTarget; + + bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric; + char UnixOwnerName[256],UnixGroupName[256]; +#ifdef _UNIX + uid_t UnixOwnerID; + gid_t UnixGroupID; +#else // Need these Unix fields in Windows too for 'list' command. + uint UnixOwnerID; + uint UnixGroupID; +#endif + + void Reset(size_t SubDataSize=0); + + bool CmpName(const wchar *Name) + { + return(wcscmp(FileName,Name)==0); + } + + FileHeader& operator = (FileHeader &hd); +}; + + +struct EndArcHeader:BaseBlock +{ + // Optional CRC32 of entire archive up to start of EndArcHeader block. + // Present in RAR 4.x archives if EARC_DATACRC flag is set. + uint ArcDataCRC; + + uint VolNumber; // Optional number of current volume. + + // 7 additional zero bytes can be stored here if EARC_REVSPACE is set. + + bool NextVolume; // Not last volume. + bool DataCRC; + bool RevSpace; + bool StoreVolNumber; + void Reset() + { + BaseBlock::Reset(); + NextVolume=false; + DataCRC=false; + RevSpace=false; + StoreVolNumber=false; + } +}; + + +struct CryptHeader:BaseBlock +{ + bool UsePswCheck; + uint Lg2Count; // Log2 of PBKDF2 repetition count. + byte Salt[SIZE_SALT50]; + byte PswCheck[SIZE_PSWCHECK]; +}; + + +// SubBlockHeader and its successors were used in RAR 2.x format. +// RAR 4.x uses FileHeader with HEAD_SERVICE HeaderType for subblocks. +struct SubBlockHeader:BlockHeader +{ + ushort SubType; + byte Level; +}; + + +struct CommentHeader:BaseBlock +{ + ushort UnpSize; + byte UnpVer; + byte Method; + ushort CommCRC; +}; + + +struct ProtectHeader:BlockHeader +{ + byte Version; + ushort RecSectors; + uint TotalBlocks; + byte Mark[8]; +}; + + +struct AVHeader:BaseBlock +{ + byte UnpVer; + byte Method; + byte AVVer; + uint AVInfoCRC; +}; + + +struct SignHeader:BaseBlock +{ + uint CreationTime; + ushort ArcNameSize; + ushort UserNameSize; +}; + + +struct UnixOwnersHeader:SubBlockHeader +{ + ushort OwnerNameSize; + ushort GroupNameSize; +/* dummy */ + char OwnerName[256]; + char GroupName[256]; +}; + + +struct EAHeader:SubBlockHeader +{ + uint UnpSize; + byte UnpVer; + byte Method; + uint EACRC; +}; + + +struct StreamHeader:SubBlockHeader +{ + uint UnpSize; + byte UnpVer; + byte Method; + uint StreamCRC; + ushort StreamNameSize; + char StreamName[260]; +}; + + +struct MacFInfoHeader:SubBlockHeader +{ + uint fileType; + uint fileCreator; +}; + + +#endif diff --git a/reference/unrar/headers5.hpp b/reference/unrar/headers5.hpp new file mode 100644 index 00000000..9ea8d979 --- /dev/null +++ b/reference/unrar/headers5.hpp @@ -0,0 +1,100 @@ +#ifndef _RAR_HEADERS5_ +#define _RAR_HEADERS5_ + +#define SIZEOF_MARKHEAD5 8 // RAR 5.0 signature length. +#define SIZEOF_SHORTBLOCKHEAD5 7 // Smallest RAR 5.0 block size. + +// RAR 5.0 block flags common for all blocks. + +// Additional extra area is present in the end of block header. +#define HFL_EXTRA 0x0001 +// Additional data area is present in the end of block header. +#define HFL_DATA 0x0002 +// Unknown blocks with this flag must be skipped when updating an archive. +#define HFL_SKIPIFUNKNOWN 0x0004 +// Data area of this block is continuing from previous volume. +#define HFL_SPLITBEFORE 0x0008 +// Data area of this block is continuing in next volume. +#define HFL_SPLITAFTER 0x0010 +// Block depends on preceding file block. +#define HFL_CHILD 0x0020 +// Preserve a child block if host is modified. +#define HFL_INHERITED 0x0040 + +// RAR 5.0 main archive header specific flags. +#define MHFL_VOLUME 0x0001 // Volume. +#define MHFL_VOLNUMBER 0x0002 // Volume number field is present. True for all volumes except first. +#define MHFL_SOLID 0x0004 // Solid archive. +#define MHFL_PROTECT 0x0008 // Recovery record is present. +#define MHFL_LOCK 0x0010 // Locked archive. + +// RAR 5.0 file header specific flags. +#define FHFL_DIRECTORY 0x0001 // Directory. +#define FHFL_UTIME 0x0002 // Time field in Unix format is present. +#define FHFL_CRC32 0x0004 // CRC32 field is present. +#define FHFL_UNPUNKNOWN 0x0008 // Unknown unpacked size. + +// RAR 5.0 end of archive header specific flags. +#define EHFL_NEXTVOLUME 0x0001 // Not last volume. + +// RAR 5.0 archive encryption header specific flags. +#define CHFL_CRYPT_PSWCHECK 0x0001 // Password check data is present. + + +// RAR 5.0 file compression flags. +#define FCI_ALGO_BIT0 0x0001 // Version of compression algorithm. +#define FCI_ALGO_BIT1 0x0002 // 0 .. 63. +#define FCI_ALGO_BIT2 0x0004 +#define FCI_ALGO_BIT3 0x0008 +#define FCI_ALGO_BIT4 0x0010 +#define FCI_ALGO_BIT5 0x0020 +#define FCI_SOLID 0x0040 // Solid flag. +#define FCI_METHOD_BIT0 0x0080 // Compression method. +#define FCI_METHOD_BIT1 0x0100 // 0 .. 5 (6 and 7 are not used). +#define FCI_METHOD_BIT2 0x0200 +#define FCI_DICT_BIT0 0x0400 // Dictionary size. +#define FCI_DICT_BIT1 0x0800 // 128 KB .. 4 GB. +#define FCI_DICT_BIT2 0x1000 +#define FCI_DICT_BIT3 0x2000 + +// Main header extra field values. +#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks. + +// Flags for MHEXTRA_LOCATOR. +#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present. +#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present. + +// File and service header extra field values. +#define FHEXTRA_CRYPT 0x01 // Encryption parameters. +#define FHEXTRA_HASH 0x02 // File hash. +#define FHEXTRA_HTIME 0x03 // High precision file time. +#define FHEXTRA_VERSION 0x04 // File version information. +#define FHEXTRA_REDIR 0x05 // File system redirection (links, etc.). +#define FHEXTRA_UOWNER 0x06 // Unix owner and group information. +#define FHEXTRA_SUBDATA 0x07 // Service header subdata array. + + +// Hash type values for FHEXTRA_HASH. +#define FHEXTRA_HASH_BLAKE2 0x00 + +// Flags for FHEXTRA_HTIME. +#define FHEXTRA_HTIME_UNIXTIME 0x01 // Use Unix time_t format. +#define FHEXTRA_HTIME_MTIME 0x02 // mtime is present. +#define FHEXTRA_HTIME_CTIME 0x04 // ctime is present. +#define FHEXTRA_HTIME_ATIME 0x08 // atime is present. +#define FHEXTRA_HTIME_UNIX_NS 0x10 // Unix format with nanosecond precision. + +// Flags for FHEXTRA_CRYPT. +#define FHEXTRA_CRYPT_PSWCHECK 0x01 // Store password check data. +#define FHEXTRA_CRYPT_HASHMAC 0x02 // Use MAC for unpacked data checksums. + +// Flags for FHEXTRA_REDIR. +#define FHEXTRA_REDIR_DIR 0x01 // Link target is directory. + +// Flags for FHEXTRA_UOWNER. +#define FHEXTRA_UOWNER_UNAME 0x01 // User name string is present. +#define FHEXTRA_UOWNER_GNAME 0x02 // Group name string is present. +#define FHEXTRA_UOWNER_NUMUID 0x04 // Numeric user ID is present. +#define FHEXTRA_UOWNER_NUMGID 0x08 // Numeric group ID is present. + +#endif diff --git a/reference/unrar/isnt.cpp b/reference/unrar/isnt.cpp new file mode 100644 index 00000000..6fadec04 --- /dev/null +++ b/reference/unrar/isnt.cpp @@ -0,0 +1,24 @@ +#include "rar.hpp" + +#ifdef _WIN_ALL +DWORD WinNT() +{ + static int dwPlatformId=-1; + static DWORD dwMajorVersion,dwMinorVersion; + if (dwPlatformId==-1) + { + OSVERSIONINFO WinVer; + WinVer.dwOSVersionInfoSize=sizeof(WinVer); + GetVersionEx(&WinVer); + dwPlatformId=WinVer.dwPlatformId; + dwMajorVersion=WinVer.dwMajorVersion; + dwMinorVersion=WinVer.dwMinorVersion; + } + DWORD Result=0; + if (dwPlatformId==VER_PLATFORM_WIN32_NT) + Result=dwMajorVersion*0x100+dwMinorVersion; + + + return Result; +} +#endif diff --git a/reference/unrar/isnt.hpp b/reference/unrar/isnt.hpp new file mode 100644 index 00000000..85790da4 --- /dev/null +++ b/reference/unrar/isnt.hpp @@ -0,0 +1,13 @@ +#ifndef _RAR_ISNT_ +#define _RAR_ISNT_ + +enum WINNT_VERSION { + WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500, + WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601, + WNT_W8=0x0602,WNT_W81=0x0603,WNT_W10=0x0a00 +}; + +DWORD WinNT(); + + +#endif diff --git a/reference/unrar/license.txt b/reference/unrar/license.txt new file mode 100644 index 00000000..0811276a --- /dev/null +++ b/reference/unrar/license.txt @@ -0,0 +1,42 @@ + ****** ***** ****** UnRAR - free utility for RAR archives + ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ****** ******* ****** License for use and distribution of + ** ** ** ** ** ** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ** ** ** ** ** ** FREE portable version + ~~~~~~~~~~~~~~~~~~~~~ + + The source code of UnRAR utility is freeware. This means: + + 1. All copyrights to RAR and the utility UnRAR are exclusively + owned by the author - Alexander Roshal. + + 2. UnRAR source code may be used in any software to handle + RAR archives without limitations free of charge, but cannot be + used to develop RAR (WinRAR) compatible archiver and to + re-create RAR compression algorithm, which is proprietary. + Distribution of modified UnRAR source code in separate form + or as a part of other software is permitted, provided that + full text of this paragraph, starting from "UnRAR source code" + words, is included in license, or in documentation if license + is not available, and in source code comments of resulting package. + + 3. The UnRAR utility may be freely distributed. It is allowed + to distribute UnRAR inside of other software packages. + + 4. THE RAR ARCHIVER AND THE UnRAR UTILITY ARE DISTRIBUTED "AS IS". + NO WARRANTY OF ANY KIND IS EXPRESSED OR IMPLIED. YOU USE AT + YOUR OWN RISK. THE AUTHOR WILL NOT BE LIABLE FOR DATA LOSS, + DAMAGES, LOSS OF PROFITS OR ANY OTHER KIND OF LOSS WHILE USING + OR MISUSING THIS SOFTWARE. + + 5. Installing and using the UnRAR utility signifies acceptance of + these terms and conditions of the license. + + 6. If you don't agree with terms of the license you must remove + UnRAR files from your storage devices and cease to use the + utility. + + Thank you for your interest in RAR and UnRAR. + + + Alexander L. Roshal diff --git a/reference/unrar/list.cpp b/reference/unrar/list.cpp new file mode 100644 index 00000000..561122b4 --- /dev/null +++ b/reference/unrar/list.cpp @@ -0,0 +1,472 @@ +#include "rar.hpp" + +static void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare); +static void ListSymLink(Archive &Arc); +static void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize); +static void ListOldSubHeader(Archive &Arc); +static void ListNewSubHeader(CommandData *Cmd,Archive &Arc); + +void ListArchive(CommandData *Cmd) +{ + int64 SumPackSize=0,SumUnpSize=0; + uint ArcCount=0,SumFileCount=0; + bool Technical=(Cmd->Command[1]=='T'); + bool ShowService=Technical && Cmd->Command[2]=='A'; + bool Bare=(Cmd->Command[1]=='B'); + bool Verbose=(Cmd->Command[0]=='V'); + + wchar ArcName[NM]; + while (Cmd->GetArcName(ArcName,ASIZE(ArcName))) + { + if (Cmd->ManualPassword) + Cmd->Password.Clean(); // Clean user entered password before processing next archive. + + Archive Arc(Cmd); +#ifdef _WIN_ALL + Arc.RemoveSequentialFlag(); +#endif + if (!Arc.WOpen(ArcName)) + continue; + bool FileMatched=true; + while (1) + { + int64 TotalPackSize=0,TotalUnpSize=0; + uint FileCount=0; + if (Arc.IsArchive(true)) + { + bool TitleShown=false; + if (!Bare) + { + Arc.ViewComment(); + mprintf(L"\n%s: %s",St(MListArchive),Arc.FileName); + mprintf(L"\n%s: ",St(MListDetails)); + uint SetCount=0; + const wchar *Fmt=Arc.Format==RARFMT14 ? L"RAR 1.4":(Arc.Format==RARFMT15 ? L"RAR 4":L"RAR 5"); + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", Fmt); + if (Arc.Solid) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSolid)); + if (Arc.SFXSize>0) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListSFX)); + if (Arc.Volume) + if (Arc.Format==RARFMT50) + { + // RAR 5.0 archives store the volume number in main header, + // so it is already available now. + if (SetCount++ > 0) + mprintf(L", "); + mprintf(St(MVolumeNumber),Arc.VolNumber+1); + } + else + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListVolume)); + if (Arc.Protected) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListRR)); + if (Arc.Locked) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListLock)); + if (Arc.Encrypted) + mprintf(L"%s%s", SetCount++ > 0 ? L", ":L"", St(MListEncHead)); + mprintf(L"\n"); + } + + wchar VolNumText[50]; + *VolNumText=0; + while(Arc.ReadHeader()>0) + { + HEADER_TYPE HeaderType=Arc.GetHeaderType(); + if (HeaderType==HEAD_ENDARC) + { +#ifndef SFX_MODULE + // Only RAR 1.5 archives store the volume number in end record. + if (Arc.EndArcHead.StoreVolNumber && Arc.Format==RARFMT15) + swprintf(VolNumText,ASIZE(VolNumText),L"%.10ls %u",St(MListVolume),Arc.VolNumber+1); +#endif + if (Technical && ShowService) + { + mprintf(L"\n%12ls: %ls",St(MListService),L"EOF"); + if (*VolNumText!=0) + mprintf(L"\n%12ls: %ls",St(MListFlags),VolNumText); + mprintf(L"\n"); + } + break; + } + switch(HeaderType) + { + case HEAD_FILE: + FileMatched=Cmd->IsProcessFile(Arc.FileHead)!=0; + if (FileMatched) + { + ListFileHeader(Arc,Arc.FileHead,TitleShown,Verbose,Technical,Bare); + if (!Arc.FileHead.SplitBefore) + { + TotalUnpSize+=Arc.FileHead.UnpSize; + FileCount++; + } + TotalPackSize+=Arc.FileHead.PackSize; + } + break; + case HEAD_SERVICE: + if (FileMatched && !Bare) + { + if (Technical && ShowService) + ListFileHeader(Arc,Arc.SubHead,TitleShown,Verbose,true,false); + } + break; + } + Arc.SeekToNext(); + } + if (!Bare && !Technical) + if (TitleShown) + { + wchar UnpSizeText[20]; + itoa(TotalUnpSize,UnpSizeText,ASIZE(UnpSizeText)); + + wchar PackSizeText[20]; + itoa(TotalPackSize,PackSizeText,ASIZE(PackSizeText)); + + if (Verbose) + { + mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); + mprintf(L"\n%21ls %9ls %3d%% %-27ls %u",UnpSizeText, + PackSizeText,ToPercentUnlim(TotalPackSize,TotalUnpSize), + VolNumText,FileCount); + } + else + { + mprintf(L"\n----------- --------- ---------- ----- ----"); + mprintf(L"\n%21ls %-16ls %u",UnpSizeText,VolNumText,FileCount); + } + + SumFileCount+=FileCount; + SumUnpSize+=TotalUnpSize; + SumPackSize+=TotalPackSize; + mprintf(L"\n"); + } + else + mprintf(St(MListNoFiles)); + + ArcCount++; + +#ifndef NOVOLUME + if (Cmd->VolSize!=0 && (Arc.FileHead.SplitAfter || + Arc.GetHeaderType()==HEAD_ENDARC && Arc.EndArcHead.NextVolume) && + MergeArchive(Arc,NULL,false,Cmd->Command[0])) + Arc.Seek(0,SEEK_SET); + else +#endif + break; + } + else + { + if (Cmd->ArcNames.ItemsCount()<2 && !Bare) + mprintf(St(MNotRAR),Arc.FileName); + break; + } + } + } + + // Clean user entered password. Not really required, just for extra safety. + if (Cmd->ManualPassword) + Cmd->Password.Clean(); + + if (ArcCount>1 && !Bare && !Technical) + { + wchar UnpSizeText[20],PackSizeText[20]; + itoa(SumUnpSize,UnpSizeText,ASIZE(UnpSizeText)); + itoa(SumPackSize,PackSizeText,ASIZE(PackSizeText)); + + if (Verbose) + mprintf(L"%21ls %9ls %3d%% %28ls %u",UnpSizeText,PackSizeText, + ToPercentUnlim(SumPackSize,SumUnpSize),L"",SumFileCount); + else + mprintf(L"%21ls %18s %lu",UnpSizeText,L"",SumFileCount); + } +} + + +enum LISTCOL_TYPE { + LCOL_NAME,LCOL_ATTR,LCOL_SIZE,LCOL_PACKED,LCOL_RATIO,LCOL_CSUM,LCOL_ENCR +}; + + +void ListFileHeader(Archive &Arc,FileHeader &hd,bool &TitleShown,bool Verbose,bool Technical,bool Bare) +{ + wchar *Name=hd.FileName; + RARFORMAT Format=Arc.Format; + + if (Bare) + { + mprintf(L"%s\n",Name); + return; + } + + if (!TitleShown && !Technical) + { + if (Verbose) + { + mprintf(L"\n%ls",St(MListTitleV)); + mprintf(L"\n----------- --------- -------- ----- ---------- ----- -------- ----"); + } + else + { + mprintf(L"\n%ls",St(MListTitleL)); + mprintf(L"\n----------- --------- ---------- ----- ----"); + } + TitleShown=true; + } + + wchar UnpSizeText[30],PackSizeText[30]; + if (hd.UnpSize==INT64NDF) + wcscpy(UnpSizeText,L"?"); + else + itoa(hd.UnpSize,UnpSizeText,ASIZE(UnpSizeText)); + itoa(hd.PackSize,PackSizeText,ASIZE(PackSizeText)); + + wchar AttrStr[30]; + if (hd.HeaderType==HEAD_SERVICE) + swprintf(AttrStr,ASIZE(AttrStr),L"%cB",hd.Inherited ? 'I' : '.'); + else + ListFileAttr(hd.FileAttr,hd.HSType,AttrStr,ASIZE(AttrStr)); + + wchar RatioStr[10]; + + if (hd.SplitBefore && hd.SplitAfter) + wcscpy(RatioStr,L"<->"); + else + if (hd.SplitBefore) + wcscpy(RatioStr,L"<--"); + else + if (hd.SplitAfter) + wcscpy(RatioStr,L"-->"); + else + swprintf(RatioStr,ASIZE(RatioStr),L"%d%%",ToPercentUnlim(hd.PackSize,hd.UnpSize)); + + wchar DateStr[50]; + hd.mtime.GetText(DateStr,ASIZE(DateStr),Technical); + + if (Technical) + { + mprintf(L"\n%12s: %s",St(MListName),Name); + + bool FileBlock=hd.HeaderType==HEAD_FILE; + + if (!FileBlock && Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM)) + { + mprintf(L"\n%12ls: %ls",St(MListType),St(MListStream)); + wchar StreamName[NM]; + GetStreamNameNTFS(Arc,StreamName,ASIZE(StreamName)); + mprintf(L"\n%12ls: %ls",St(MListTarget),StreamName); + } + else + { + const wchar *Type=St(FileBlock ? (hd.Dir ? MListDir:MListFile):MListService); + + if (hd.RedirType!=FSREDIR_NONE) + switch(hd.RedirType) + { + case FSREDIR_UNIXSYMLINK: + Type=St(MListUSymlink); break; + case FSREDIR_WINSYMLINK: + Type=St(MListWSymlink); break; + case FSREDIR_JUNCTION: + Type=St(MListJunction); break; + case FSREDIR_HARDLINK: + Type=St(MListHardlink); break; + case FSREDIR_FILECOPY: + Type=St(MListCopy); break; + } + mprintf(L"\n%12ls: %ls",St(MListType),Type); + if (hd.RedirType!=FSREDIR_NONE) + if (Format==RARFMT15) + { + char LinkTargetA[NM]; + if (Arc.FileHead.Encrypted) + { + // Link data are encrypted. We would need to ask for password + // and initialize decryption routine to display the link target. + strncpyz(LinkTargetA,"*<-?->",ASIZE(LinkTargetA)); + } + else + { + int DataSize=(int)Min(hd.PackSize,ASIZE(LinkTargetA)-1); + Arc.Read(LinkTargetA,DataSize); + LinkTargetA[DataSize > 0 ? DataSize : 0] = 0; + } + wchar LinkTarget[NM]; + CharToWide(LinkTargetA,LinkTarget,ASIZE(LinkTarget)); + mprintf(L"\n%12ls: %ls",St(MListTarget),LinkTarget); + } + else + mprintf(L"\n%12ls: %ls",St(MListTarget),hd.RedirName); + } + if (!hd.Dir) + { + mprintf(L"\n%12ls: %ls",St(MListSize),UnpSizeText); + mprintf(L"\n%12ls: %ls",St(MListPacked),PackSizeText); + mprintf(L"\n%12ls: %ls",St(MListRatio),RatioStr); + } + if (hd.mtime.IsSet()) + mprintf(L"\n%12ls: %ls",St(MListMtime),DateStr); + if (hd.ctime.IsSet()) + { + hd.ctime.GetText(DateStr,ASIZE(DateStr),true); + mprintf(L"\n%12ls: %ls",St(MListCtime),DateStr); + } + if (hd.atime.IsSet()) + { + hd.atime.GetText(DateStr,ASIZE(DateStr),true); + mprintf(L"\n%12ls: %ls",St(MListAtime),DateStr); + } + mprintf(L"\n%12ls: %ls",St(MListAttr),AttrStr); + if (hd.FileHash.Type==HASH_CRC32) + mprintf(L"\n%12ls: %8.8X", + hd.UseHashKey ? L"CRC32 MAC":hd.SplitAfter ? L"Pack-CRC32":L"CRC32", + hd.FileHash.CRC32); + if (hd.FileHash.Type==HASH_BLAKE2) + { + wchar BlakeStr[BLAKE2_DIGEST_SIZE*2+1]; + BinToHex(hd.FileHash.Digest,BLAKE2_DIGEST_SIZE,NULL,BlakeStr,ASIZE(BlakeStr)); + mprintf(L"\n%12ls: %ls", + hd.UseHashKey ? L"BLAKE2 MAC":hd.SplitAfter ? L"Pack-BLAKE2":L"BLAKE2", + BlakeStr); + } + + const wchar *HostOS=L""; + if (Format==RARFMT50 && hd.HSType!=HSYS_UNKNOWN) + HostOS=hd.HSType==HSYS_WINDOWS ? L"Windows":L"Unix"; + if (Format==RARFMT15) + { + static const wchar *RarOS[]={ + L"DOS",L"OS/2",L"Windows",L"Unix",L"Mac OS",L"BeOS",L"WinCE",L"",L"",L"" + }; + if (hd.HostOS=0x100000 ? hd.WinSize/0x100000:hd.WinSize/0x400, + hd.WinSize>=0x100000 ? L"M":L"K"); + + if (hd.Solid || hd.Encrypted) + { + mprintf(L"\n%12ls: ",St(MListFlags)); + if (hd.Solid) + mprintf(L"%ls ",St(MListSolid)); + if (hd.Encrypted) + mprintf(L"%ls ",St(MListEnc)); + } + + if (hd.Version) + { + uint Version=ParseVersionFileName(Name,false); + if (Version!=0) + mprintf(L"\n%12ls: %u",St(MListFileVer),Version); + } + + if (hd.UnixOwnerSet) + { + mprintf(L"\n%12ls: ",L"Unix owner"); + if (*hd.UnixOwnerName!=0) + mprintf(L"%ls:",GetWide(hd.UnixOwnerName)); + if (*hd.UnixGroupName!=0) + mprintf(L"%ls",GetWide(hd.UnixGroupName)); + if ((*hd.UnixOwnerName!=0 || *hd.UnixGroupName!=0) && (hd.UnixOwnerNumeric || hd.UnixGroupNumeric)) + mprintf(L" "); + if (hd.UnixOwnerNumeric) + mprintf(L"#%d:",hd.UnixOwnerID); + if (hd.UnixGroupNumeric) + mprintf(L"#%d:",hd.UnixGroupID); + } + + mprintf(L"\n"); + return; + } + + mprintf(L"\n%c%10ls %9ls ",hd.Encrypted ? '*' : ' ',AttrStr,UnpSizeText); + + if (Verbose) + mprintf(L"%9ls %4ls ",PackSizeText,RatioStr); + + mprintf(L" %ls ",DateStr); + + if (Verbose) + { + if (hd.FileHash.Type==HASH_CRC32) + mprintf(L"%8.8X ",hd.FileHash.CRC32); + else + if (hd.FileHash.Type==HASH_BLAKE2) + { + byte *S=hd.FileHash.Digest; + mprintf(L"%02x%02x..%02x ",S[0],S[1],S[31]); + } + else + mprintf(L"???????? "); + } + mprintf(L"%ls",Name); +} + +/* +void ListSymLink(Archive &Arc) +{ + if (Arc.FileHead.HSType==HSYS_UNIX && (Arc.FileHead.FileAttr & 0xF000)==0xA000) + if (Arc.FileHead.Encrypted) + { + // Link data are encrypted. We would need to ask for password + // and initialize decryption routine to display the link target. + mprintf(L"\n%22ls %ls",L"-->",L"*<-?->"); + } + else + { + char FileName[NM]; + uint DataSize=(uint)Min(Arc.FileHead.PackSize,sizeof(FileName)-1); + Arc.Read(FileName,DataSize); + FileName[DataSize]=0; + mprintf(L"\n%22ls %ls",L"-->",GetWide(FileName)); + } +} +*/ + +void ListFileAttr(uint A,HOST_SYSTEM_TYPE HostType,wchar *AttrStr,size_t AttrSize) +{ + switch(HostType) + { + case HSYS_WINDOWS: + swprintf(AttrStr,AttrSize,L"%c%c%c%c%c%c%c", + (A & 0x2000)!=0 ? 'I' : '.', // Not content indexed. + (A & 0x0800)!=0 ? 'C' : '.', // Compressed. + (A & 0x0020)!=0 ? 'A' : '.', // Archive. + (A & 0x0010)!=0 ? 'D' : '.', // Directory. + (A & 0x0004)!=0 ? 'S' : '.', // System. + (A & 0x0002)!=0 ? 'H' : '.', // Hidden. + (A & 0x0001)!=0 ? 'R' : '.'); // Read-only. + break; + case HSYS_UNIX: + switch (A & 0xF000) + { + case 0x4000: + AttrStr[0]='d'; + break; + case 0xA000: + AttrStr[0]='l'; + break; + default: + AttrStr[0]='-'; + break; + } + swprintf(AttrStr+1,AttrSize-1,L"%c%c%c%c%c%c%c%c%c", + (A & 0x0100) ? 'r' : '-', + (A & 0x0080) ? 'w' : '-', + (A & 0x0040) ? ((A & 0x0800)!=0 ? 's':'x'):((A & 0x0800)!=0 ? 'S':'-'), + (A & 0x0020) ? 'r' : '-', + (A & 0x0010) ? 'w' : '-', + (A & 0x0008) ? ((A & 0x0400)!=0 ? 's':'x'):((A & 0x0400)!=0 ? 'S':'-'), + (A & 0x0004) ? 'r' : '-', + (A & 0x0002) ? 'w' : '-', + (A & 0x0001) ? ((A & 0x200)!=0 ? 't' : 'x') : '-'); + break; + case HSYS_UNKNOWN: + wcscpy(AttrStr,L"?"); + break; + } +} diff --git a/reference/unrar/list.hpp b/reference/unrar/list.hpp new file mode 100644 index 00000000..7721ae52 --- /dev/null +++ b/reference/unrar/list.hpp @@ -0,0 +1,6 @@ +#ifndef _RAR_LIST_ +#define _RAR_LIST_ + +void ListArchive(CommandData *Cmd); + +#endif diff --git a/reference/unrar/loclang.hpp b/reference/unrar/loclang.hpp new file mode 100644 index 00000000..427128b4 --- /dev/null +++ b/reference/unrar/loclang.hpp @@ -0,0 +1,381 @@ +#define MYesNo L"_Yes_No" +#define MYesNoAll L"_Yes_No_All" +#define MYesNoAllQ L"_Yes_No_All_nEver_Quit" +#define MYesNoAllRenQ L"_Yes_No_All_nEver_Rename_Quit" +#define MContinueQuit L"_Continue_Quit" +#define MRetryAbort L"_Retry_Abort" +#define MCopyright L"\nRAR %s Copyright (c) 1993-%d Alexander Roshal %d %s %d" +#define MRegTo L"\nRegistered to %s\n" +#define MShare L"\nTrial version Type 'rar -?' for help\n" +#define MRegKeyWarning L"\nAvailable license key is valid only for %s\n" +#define MUCopyright L"\nUNRAR %s freeware Copyright (c) 1993-%d Alexander Roshal\n" +#define MBeta L"beta" +#define Mx86 L"x86" +#define Mx64 L"x64" +#define MMonthJan L"Jan" +#define MMonthFeb L"Feb" +#define MMonthMar L"Mar" +#define MMonthApr L"Apr" +#define MMonthMay L"May" +#define MMonthJun L"Jun" +#define MMonthJul L"Jul" +#define MMonthAug L"Aug" +#define MMonthSep L"Sep" +#define MMonthOct L"Oct" +#define MMonthNov L"Nov" +#define MMonthDec L"Dec" +#define MRARTitle1 L"\nUsage: rar - - " +#define MUNRARTitle1 L"\nUsage: unrar - - " +#define MRARTitle2 L"\n <@listfiles...> " +#define MCHelpCmd L"\n\n" +#define MCHelpCmdA L"\n a Add files to archive" +#define MCHelpCmdC L"\n c Add archive comment" +#define MCHelpCmdCH L"\n ch Change archive parameters" +#define MCHelpCmdCW L"\n cw Write archive comment to file" +#define MCHelpCmdD L"\n d Delete files from archive" +#define MCHelpCmdE L"\n e Extract files without archived paths" +#define MCHelpCmdF L"\n f Freshen files in archive" +#define MCHelpCmdI L"\n i[par]= Find string in archives" +#define MCHelpCmdK L"\n k Lock archive" +#define MCHelpCmdL L"\n l[t[a],b] List archive contents [technical[all], bare]" +#define MCHelpCmdM L"\n m[f] Move to archive [files only]" +#define MCHelpCmdP L"\n p Print file to stdout" +#define MCHelpCmdR L"\n r Repair archive" +#define MCHelpCmdRC L"\n rc Reconstruct missing volumes" +#define MCHelpCmdRN L"\n rn Rename archived files" +#define MCHelpCmdRR L"\n rr[N] Add data recovery record" +#define MCHelpCmdRV L"\n rv[N] Create recovery volumes" +#define MCHelpCmdS L"\n s[name|-] Convert archive to or from SFX" +#define MCHelpCmdT L"\n t Test archive files" +#define MCHelpCmdU L"\n u Update files in archive" +#define MCHelpCmdV L"\n v[t[a],b] Verbosely list archive contents [technical[all],bare]" +#define MCHelpCmdX L"\n x Extract files with full path" +#define MCHelpSw L"\n\n" +#define MCHelpSwm L"\n - Stop switches scanning" +#define MCHelpSwAT L"\n @[+] Disable [enable] file lists" +#define MCHelpSwAC L"\n ac Clear Archive attribute after compression or extraction" +#define MCHelpSwAD L"\n ad Append archive name to destination path" +#define MCHelpSwAG L"\n ag[format] Generate archive name using the current date" +#define MCHelpSwAI L"\n ai Ignore file attributes" +#define MCHelpSwAO L"\n ao Add files with Archive attribute set" +#define MCHelpSwAP L"\n ap Set path inside archive" +#define MCHelpSwAS L"\n as Synchronize archive contents" +#define MCHelpSwCm L"\n c- Disable comments show" +#define MCHelpSwCFGm L"\n cfg- Disable read configuration" +#define MCHelpSwCL L"\n cl Convert names to lower case" +#define MCHelpSwCU L"\n cu Convert names to upper case" +#define MCHelpSwDF L"\n df Delete files after archiving" +#define MCHelpSwDH L"\n dh Open shared files" +#define MCHelpSwDR L"\n dr Delete files to Recycle Bin" +#define MCHelpSwDS L"\n ds Disable name sort for solid archive" +#define MCHelpSwDW L"\n dw Wipe files after archiving" +#define MCHelpSwEa L"\n e[+] Set file exclude and include attributes" +#define MCHelpSwED L"\n ed Do not add empty directories" +#define MCHelpSwEN L"\n en Do not put 'end of archive' block" +#define MCHelpSwEP L"\n ep Exclude paths from names" +#define MCHelpSwEP1 L"\n ep1 Exclude base directory from names" +#define MCHelpSwEP2 L"\n ep2 Expand paths to full" +#define MCHelpSwEP3 L"\n ep3 Expand paths to full including the drive letter" +#define MCHelpSwF L"\n f Freshen files" +#define MCHelpSwHP L"\n hp[password] Encrypt both file data and headers" +#define MCHelpSwHT L"\n ht[b|c] Select hash type [BLAKE2,CRC32] for file checksum" +#define MCHelpSwIDP L"\n id[c,d,p,q] Disable messages" +#define MCHelpSwIEML L"\n ieml[addr] Send archive by email" +#define MCHelpSwIERR L"\n ierr Send all messages to stderr" +#define MCHelpSwILOG L"\n ilog[name] Log errors to file (registered versions only)" +#define MCHelpSwINUL L"\n inul Disable all messages" +#define MCHelpSwIOFF L"\n ioff Turn PC off after completing an operation" +#define MCHelpSwISND L"\n isnd Enable sound" +#define MCHelpSwIVER L"\n iver Display the version number" +#define MCHelpSwK L"\n k Lock archive" +#define MCHelpSwKB L"\n kb Keep broken extracted files" +#define MCHelpSwLog L"\n log[f][=name] Write names to log file" +#define MCHelpSwMn L"\n m<0..5> Set compression level (0-store...3-default...5-maximal)" +#define MCHelpSwMA L"\n ma[4|5] Specify a version of archiving format" +#define MCHelpSwMC L"\n mc Set advanced compression parameters" +#define MCHelpSwMD L"\n md[k,m,g] Dictionary size in KB, MB or GB" +#define MCHelpSwMS L"\n ms[ext;ext] Specify file types to store" +#define MCHelpSwMT L"\n mt Set the number of threads" +#define MCHelpSwN L"\n n Additionally filter included files" +#define MCHelpSwNa L"\n n@ Read additional filter masks from stdin" +#define MCHelpSwNal L"\n n@ Read additional filter masks from list file" +#define MCHelpSwO L"\n o[+|-] Set the overwrite mode" +#define MCHelpSwOC L"\n oc Set NTFS Compressed attribute" +#define MCHelpSwOH L"\n oh Save hard links as the link instead of the file" +#define MCHelpSwOI L"\n oi[0-4][:min] Save identical files as references" +#define MCHelpSwOL L"\n ol[a] Process symbolic links as the link [absolute paths]" +#define MCHelpSwONI L"\n oni Allow potentially incompatible names" +#define MCHelpSwOR L"\n or Rename files automatically" +#define MCHelpSwOS L"\n os Save NTFS streams" +#define MCHelpSwOW L"\n ow Save or restore file owner and group" +#define MCHelpSwP L"\n p[password] Set password" +#define MCHelpSwPm L"\n p- Do not query password" +#define MCHelpSwQO L"\n qo[-|+] Add quick open information [none|force]" +#define MCHelpSwR L"\n r Recurse subdirectories" +#define MCHelpSwRm L"\n r- Disable recursion" +#define MCHelpSwR0 L"\n r0 Recurse subdirectories for wildcard names only" +#define MCHelpSwRI L"\n ri

[:] Set priority (0-default,1-min..15-max) and sleep time in ms" +#define MCHelpSwRR L"\n rr[N] Add data recovery record" +#define MCHelpSwRV L"\n rv[N] Create recovery volumes" +#define MCHelpSwS L"\n s[,v[-],e] Create solid archive" +#define MCHelpSwSm L"\n s- Disable solid archiving" +#define MCHelpSwSC L"\n sc[obj] Specify the character set" +#define MCHelpSwSFX L"\n sfx[name] Create SFX archive" +#define MCHelpSwSI L"\n si[name] Read data from standard input (stdin)" +#define MCHelpSwSL L"\n sl Process files with size less than specified" +#define MCHelpSwSM L"\n sm Process files with size more than specified" +#define MCHelpSwT L"\n t Test files after archiving" +#define MCHelpSwTK L"\n tk Keep original archive time" +#define MCHelpSwTL L"\n tl Set archive time to latest file" +#define MCHelpSwTN L"\n tn

/// Entry is split among multiple volumes /// - public abstract bool IsSplit { get; } + public abstract bool IsSplitAfter { get; } internal abstract IEnumerable Parts { get; } internal bool IsSolid { get; set; } diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs index 6c80ddff..849d4741 100644 --- a/src/SharpCompress/Common/GZip/GZipEntry.cs +++ b/src/SharpCompress/Common/GZip/GZipEntry.cs @@ -36,7 +36,7 @@ namespace SharpCompress.Common.GZip public override bool IsDirectory => false; - public override bool IsSplit => false; + public override bool IsSplitAfter => false; internal override IEnumerable Parts => filePart.AsEnumerable(); diff --git a/src/SharpCompress/Common/IEntry.cs b/src/SharpCompress/Common/IEntry.cs index ac15828c..e8f13a30 100644 --- a/src/SharpCompress/Common/IEntry.cs +++ b/src/SharpCompress/Common/IEntry.cs @@ -12,7 +12,7 @@ namespace SharpCompress.Common string Key { get; } bool IsDirectory { get; } bool IsEncrypted { get; } - bool IsSplit { get; } + bool IsSplitAfter { get; } DateTime? LastAccessedTime { get; } DateTime? LastModifiedTime { get; } long Size { get; } diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index b6181c5b..0d5524c8 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -31,7 +31,8 @@ namespace SharpCompress.Common.Rar.Headers Flags = reader.ReadRarVIntUInt16(); var lvalue = checked((long)reader.ReadRarVInt()); -//!!! rar5 TODO: this may cause problems with user code, Unpack20 seems to use this field to decompress + + // long.MaxValue causes the unpack code to finish when the input stream is exhausted UncompressedSize = HasFlag(FileFlagsV5.UnpackedSizeUnknown) ? long.MaxValue : lvalue; FileAttributes = reader.ReadRarVIntUInt32(); @@ -56,6 +57,7 @@ namespace SharpCompress.Common.Rar.Headers // 7th bit (0x0040) defines the solid flag. If it is set, RAR continues to use the compression dictionary left after processing preceding files. // It can be set only for file headers and is never set for service headers. IsSolid = (us & 0x40) == 0x40; + if (IsSolid != HasHeaderFlag(HeaderFlagsV5.Solid_TESTME)) throw new InvalidFormatException("rar solid flag base header != file header"); // Bits 8 - 10 (0x0380 mask) define the compression method. Currently only values 0 - 5 are used. 0 means no compression. CompressionMethod = (byte)((us >> 7) & 0x7); @@ -63,6 +65,7 @@ namespace SharpCompress.Common.Rar.Headers // Bits 11 - 14 (0x3c00) define the minimum size of dictionary size required to extract data. Value 0 means 128 KB, 1 - 256 KB, ..., 14 - 2048 MB, 15 - 4096 MB. // 2 ^ (17 + x) R5DictSize_2_17plusX = (byte)((us >> 10) & 0xf); + //WindowSize = hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5) HostOs = reader.ReadRarVIntByte(); @@ -393,8 +396,14 @@ namespace SharpCompress.Common.Rar.Headers // 5 - best compression internal byte CompressionMethod { get; private set; } - // see unpack.cs + // eg (see DoUnpack()) + //case 15: // rar 1.5 compression + //case 20: // rar 2.x compression + //case 26: // files larger than 2GB + //case 29: // rar 3.x compression + //case 50: // RAR 5.0 compression algorithm. internal byte CompressionAlgorithm { get; private set; } + public bool IsSolid { get; private set; } internal byte[] R4Salt { get; private set; } @@ -410,8 +419,7 @@ namespace SharpCompress.Common.Rar.Headers internal long DataStartPosition { get; set; } public Stream PackedStream { get; set; } -//!!! TODO rar5 - public bool IsSplit => HasFlag(FileFlagsV4.SplitAfter); + public bool IsSplitAfter => IsRar5 ? HasHeaderFlag(HeaderFlagsV5.SplitAfter) : HasFlag(FileFlagsV4.SplitAfter); public bool IsDirectory => HasFlag(IsRar5 ? FileFlagsV5.Directory : FileFlagsV4.Directory); diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index a3811740..746b9018 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -45,10 +45,10 @@ namespace SharpCompress.Common.Rar.Headers public const ushort HasExtra = 0x0001; public const ushort HasData = 0x0002; public const ushort Keep = 0x0004; // block must be kept during an update - public const ushort VolumeContinuation = 0x0008; - public const ushort VolumeIncomplete = 0x0010; - public const ushort Solid = 0x0020; // ??? - public const ushort PreserveChild = 0x0040; + public const ushort SplitBefore = 0x0008; + public const ushort SplitAfter = 0x0010; + public const ushort Solid_TESTME = 0x0020; // ??? Block depends on preceding file block. + public const ushort PreserveChild = 0x0040; // ???? Preserve a child block if host block is modified } internal static class ArchiveFlagsV4 diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index 8abf8670..d4bead44 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -35,11 +35,6 @@ namespace SharpCompress.Common.Rar.Headers HeaderCode = reader.ReadRarVIntByte(); HeaderFlags = reader.ReadRarVIntUInt16(2); -// if (HasHeaderFlag(HeaderFlagsV5.VolumeContinuation)) throw new NotImplementedException(); -// if (HasHeaderFlag(HeaderFlagsV5.VolumeIncomplete)) throw new NotImplementedException(); -// if (HasHeaderFlag(HeaderFlagsV5.Solid)) throw new NotImplementedException(); -// if (HasHeaderFlag(HeaderFlagsV5.PreserveChild)) throw new NotImplementedException(); - if (HasHeaderFlag(HeaderFlagsV5.HasExtra)) { ExtraSize = reader.ReadRarVIntUInt32(); @@ -111,7 +106,7 @@ namespace SharpCompress.Common.Rar.Headers protected ushort HeaderFlags { get; } - private bool HasHeaderFlag(ushort flag) + protected bool HasHeaderFlag(ushort flag) { return (HeaderFlags & flag) == flag; } diff --git a/src/SharpCompress/Common/Rar/RarEntry.cs b/src/SharpCompress/Common/Rar/RarEntry.cs index 105a306a..cec1eb44 100644 --- a/src/SharpCompress/Common/Rar/RarEntry.cs +++ b/src/SharpCompress/Common/Rar/RarEntry.cs @@ -47,7 +47,7 @@ namespace SharpCompress.Common.Rar ///
public override bool IsDirectory => FileHeader.IsDirectory; - public override bool IsSplit => FileHeader.IsSplit; + public override bool IsSplitAfter => FileHeader.IsSplitAfter; public override string ToString() { diff --git a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs index dd23d629..584bfb95 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipEntry.cs @@ -34,7 +34,7 @@ namespace SharpCompress.Common.SevenZip public override bool IsDirectory => FilePart.Header.IsDir; - public override bool IsSplit => false; + public override bool IsSplitAfter => false; public override int? Attrib => (int)FilePart.Header.Attrib; diff --git a/src/SharpCompress/Common/Tar/TarEntry.cs b/src/SharpCompress/Common/Tar/TarEntry.cs index 5208bce1..6ff6e19f 100644 --- a/src/SharpCompress/Common/Tar/TarEntry.cs +++ b/src/SharpCompress/Common/Tar/TarEntry.cs @@ -39,7 +39,7 @@ namespace SharpCompress.Common.Tar public override bool IsDirectory => filePart.Header.EntryType == EntryType.Directory; - public override bool IsSplit => false; + public override bool IsSplitAfter => false; internal override IEnumerable Parts => filePart.AsEnumerable(); diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index 8b8c7337..b721d57f 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -72,7 +72,7 @@ namespace SharpCompress.Common.Zip public override bool IsDirectory => filePart.Header.IsDirectory; - public override bool IsSplit => false; + public override bool IsSplitAfter => false; internal override IEnumerable Parts => filePart.AsEnumerable(); } diff --git a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs index 69e7734d..57e9fd6f 100644 --- a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs +++ b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs @@ -89,7 +89,7 @@ namespace SharpCompress.Compressors.Rar currentCount -= read; totalRead += read; if (((maxPosition - currentPosition) == 0) - && filePartEnumerator.Current.FileHeader.IsSplit) + && filePartEnumerator.Current.FileHeader.IsSplitAfter) { if (filePartEnumerator.Current.FileHeader.R4Salt != null) { diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs index 0fd6bd48..0eb0b64a 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs @@ -5,19 +5,15 @@ internal partial class BitInput { public const int MAX_SIZE=0x8000; // Size of input buffer. - public - int InAddr; // Curent byte position in the buffer. - public - int InBit; // Current bit position in the current byte. + public int InAddr; // Curent byte position in the buffer. + public int InBit; // Current bit position in the current byte. - public - bool ExternalBuffer; + public bool ExternalBuffer; //BitInput(bool AllocBuffer); //~BitInput(); - public - byte[] InBuf; // Dynamically allocated input buffer. + public byte[] InBuf; // Dynamically allocated input buffer. public void InitBitInput() @@ -26,8 +22,7 @@ internal partial class BitInput } // Move forward by 'Bits' bits. - public - void addbits(uint _Bits) + public void addbits(uint _Bits) { var Bits = checked((int)_Bits); Bits+=InBit; @@ -37,8 +32,7 @@ internal partial class BitInput // Return 16 bits from current position in the buffer. // Bit at (InAddr,InBit) has the highest position in returning data. - public - uint getbits() + public uint getbits() { uint BitField=(uint)InBuf[InAddr] << 16; BitField|=(uint)InBuf[InAddr+1] << 8; @@ -49,8 +43,7 @@ internal partial class BitInput // Return 32 bits from current position in the buffer. // Bit at (InAddr,InBit) has the highest position in returning data. - public - uint getbits32() + public uint getbits32() { uint BitField=(uint)InBuf[InAddr] << 24; BitField|=(uint)InBuf[InAddr+1] << 16; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index 65ec8690..f7c8f536 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -13,15 +13,17 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) { + // may be long.MaxValue which could indicate unknown size (not present in header) DestUnpSize = fileHeader.UncompressedSize; this.fileHeader = fileHeader; this.readStream = readStream; this.writeStream = writeStream; - if (!fileHeader.IsSolid) - { - Init(null); - Init(size_t WinSize,bool Solid) - } +// if (!fileHeader.IsSolid) +// { +// Init(null); +// } +fileHeader. + Init(size_t WinSize, fileHeader.IsSolid) Suspended = false; DoUnpack(); } @@ -31,23 +33,53 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 if (this.fileHeader.CompressionMethod == 0) { UnstoreFile(); - return; + } else { + DoUnpack(uint Method,bool Solid); } - DoUnpack(uint Method,bool Solid); } + private void UnstoreFile() + { + var b = new byte[0x10000]; + while (true) + { + int n = this.readStream.Read(b, 0, (int)Math.Min(b.Length, DestUnpSize)); + if (n == 0) + { + break; + } + + n = n < DestUnpSize ? n : (int)DestUnpSize; + this.writeStream.Write(b, 0, n); + if (DestUnpSize >= 0) + { + DestUnpSize -= n; + } + if (Suspended) + { + return; + } + } + } + public bool Suspended { get; set; } public long DestSize { get => DestUnpSize; } - public int Char { - get { throw new NotImplementedException(); } + public int Char + { + get + { + // TODO: coderb: not sure where the "MAXSIZE-30" comes from, ported from V1 code + if (InAddr > MAX_SIZE - 30) + { + UnpReadBuf(); + } + return InBuf[InAddr++]; + } } - public int PpmEscChar { - get { throw new NotImplementedException(); } - set { throw new NotImplementedException(); } - } + public int PpmEscChar { get => this.PPMEscChar; set => this.PPMEscChar = value; } } } #endif \ No newline at end of file diff --git a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs index 068d7572..cfbdc5c0 100644 --- a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs +++ b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs @@ -89,7 +89,7 @@ namespace SharpCompress.Readers.Rar return true; } - if (!reader.Entry.IsSplit) + if (!reader.Entry.IsSplitAfter) { return false; } From 093a5fdf31b6cead6dfe1047968870247d5a74e0 Mon Sep 17 00:00:00 2001 From: coderb Date: Mon, 18 Dec 2017 09:25:55 -0500 Subject: [PATCH 047/124] rar5: oops --- src/SharpCompress/Archives/Rar/RarArchive.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index ad41cb55..ab341e0c 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -41,6 +41,7 @@ namespace SharpCompress.Archives.Rar internal RarArchive(IEnumerable streams, ReaderOptions options) : base(ArchiveType.Rar, streams, options) { + Unpack = new SharpCompress.Compressors.Rar.UnpackV1.Unpack(); } protected override IEnumerable LoadEntries(IEnumerable volumes) From c7700944259cdf9e344ccd14b6712a31b5281cdf Mon Sep 17 00:00:00 2001 From: coderb Date: Mon, 18 Dec 2017 09:46:18 -0500 Subject: [PATCH 048/124] rar5: fileheader fixes --- .../Common/Rar/Headers/FileHeader.cs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index 0d5524c8..b1c4137f 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -1,3 +1,13 @@ +#if !Rar2017_64bit +using nint = System.Int32; +using nuint = System.UInt32; +using size_t = System.UInt32; +#else +using nint = System.Int64; +using nuint = System.UInt64; +using size_t = System.UInt64; +#endif + using SharpCompress.IO; using System; using System.IO; @@ -63,9 +73,7 @@ namespace SharpCompress.Common.Rar.Headers CompressionMethod = (byte)((us >> 7) & 0x7); // Bits 11 - 14 (0x3c00) define the minimum size of dictionary size required to extract data. Value 0 means 128 KB, 1 - 256 KB, ..., 14 - 2048 MB, 15 - 4096 MB. - // 2 ^ (17 + x) - R5DictSize_2_17plusX = (byte)((us >> 10) & 0xf); - //WindowSize = hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5) + WindowSize = IsDirectory ? 0 : ((size_t)0x20000) << ((us>>10) & 0xf); HostOs = reader.ReadRarVIntByte(); @@ -170,6 +178,7 @@ namespace SharpCompress.Common.Rar.Headers } } + private static DateTime ReadExtendedTimeV5(MarkingBinaryReader reader, bool isWindowsTime) { if (isWindowsTime) @@ -202,6 +211,7 @@ namespace SharpCompress.Common.Rar.Headers { Flags = HeaderFlags; IsSolid = HasFlag(FileFlagsV4.Solid); + WindowSize = IsDirectory ? 0U : ((size_t)0x10000) << ((Flags & FileFlagsV4.WindowMask) >> 5); uint lowUncompressedSize = reader.ReadUInt32(); @@ -406,8 +416,10 @@ namespace SharpCompress.Common.Rar.Headers public bool IsSolid { get; private set; } + // unused for UnpackV1 implementation (limitation) + internal size_t WindowSize { get; private set; } + internal byte[] R4Salt { get; private set; } - internal byte R5DictSize_2_17plusX { get; private set; } private byte HostOs { get; set; } internal uint FileAttributes { get; private set; } From 22b008f72380b5853dadb353871df01fc62b09e7 Mon Sep 17 00:00:00 2001 From: coderb Date: Mon, 18 Dec 2017 09:51:24 -0500 Subject: [PATCH 049/124] rar5: unpack wip --- src/SharpCompress/Common/Rar/Headers/FileHeader.cs | 1 + .../Compressors/Rar/UnpackV2017/Unpack.cs | 13 +++++-------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index b1c4137f..48d2f3e0 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -405,6 +405,7 @@ namespace SharpCompress.Common.Rar.Headers // 4 - good compression // 5 - best compression internal byte CompressionMethod { get; private set; } + internal bool IsStored { get { return CompressionMethod == 0; } } // eg (see DoUnpack()) //case 15: // rar 1.5 compression diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index f7c8f536..bbba9d8a 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -18,23 +18,20 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 this.fileHeader = fileHeader; this.readStream = readStream; this.writeStream = writeStream; -// if (!fileHeader.IsSolid) -// { -// Init(null); -// } -fileHeader. - Init(size_t WinSize, fileHeader.IsSolid) + if (!fileHeader.IsStored) { + Init(fileHeader.WindowSize, fileHeader.IsSolid); + } Suspended = false; DoUnpack(); } public void DoUnpack() { - if (this.fileHeader.CompressionMethod == 0) + if (this.fileHeader.IsStored) { UnstoreFile(); } else { - DoUnpack(uint Method,bool Solid); + DoUnpack(this.fileHeader.CompressionMethod, this.fileHeader.IsSolid); } } From 59e4383209754182fa334b6fb7183f9322440cf8 Mon Sep 17 00:00:00 2001 From: coderb Date: Mon, 18 Dec 2017 10:02:34 -0500 Subject: [PATCH 050/124] rar5: unpack wip --- src/SharpCompress/Compressors/Rar/IRarUnpack.cs | 3 +++ .../Compressors/Rar/UnpackV2017/Unpack.cs | 16 +++------------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/IRarUnpack.cs b/src/SharpCompress/Compressors/Rar/IRarUnpack.cs index 256fa08f..ecd70a47 100644 --- a/src/SharpCompress/Compressors/Rar/IRarUnpack.cs +++ b/src/SharpCompress/Compressors/Rar/IRarUnpack.cs @@ -7,7 +7,10 @@ namespace SharpCompress.Compressors.Rar { void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream); void DoUnpack(); + + // eg u/i pause/resume button bool Suspended { get; set; } + long DestSize { get; } int Char { get; } int PpmEscChar { get; set; } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index bbba9d8a..7079e0d9 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -38,25 +38,15 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 private void UnstoreFile() { var b = new byte[0x10000]; - while (true) - { + do { int n = this.readStream.Read(b, 0, (int)Math.Min(b.Length, DestUnpSize)); if (n == 0) { break; } - - n = n < DestUnpSize ? n : (int)DestUnpSize; this.writeStream.Write(b, 0, n); - if (DestUnpSize >= 0) - { - DestUnpSize -= n; - } - if (Suspended) - { - return; - } - } + DestUnpSize -= n; + } while (!Suspended); } public bool Suspended { get; set; } From 122a732206fb623e6bca71b7f188ae7b6446846e Mon Sep 17 00:00:00 2001 From: coderb Date: Mon, 18 Dec 2017 10:18:17 -0500 Subject: [PATCH 051/124] unrar5: pull in fast memset code --- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 15 ++++--- src/SharpCompress/Utility.cs | 42 +++++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index 54309de4..334bde62 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using SharpCompress.Common; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; @@ -73,7 +74,8 @@ void Init(size_t WinSize,bool Solid) // If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size // will be 0 because of size_t overflow. Let's issue the memory error. if (WinSize==0) - ErrHandler.MemoryError(); + //ErrHandler.MemoryError(); + throw new InvalidFormatException("invalid window size (possibly due to a rar file with a 4GB being unpacked on a 32-bit platform)"); // Minimum window size must be at least twice more than maximum possible // size of filter block, which is 0x10000 in RAR now. If window size is @@ -98,22 +100,24 @@ void Init(size_t WinSize,bool Solid) // We do not handle growth for existing fragmented window. if (Grow && Fragmented) - throw std::bad_alloc(); + //throw std::bad_alloc(); + throw new InvalidFormatException("Grow && Fragmented"); - byte *NewWindow=Fragmented ? null : (byte *)malloc(WinSize); + byte[] NewWindow=Fragmented ? null : new byte[WinSize]; if (NewWindow==null) if (Grow || WinSize<0x1000000) { // We do not support growth for new fragmented window. // Also exclude RAR4 and small dictionaries. - throw std::bad_alloc(); + //throw std::bad_alloc(); + throw new InvalidFormatException("Grow || WinSize<0x1000000"); } else { if (Window!=null) // If allocated by preceding files. { - free(Window); + //free(Window); Window=null; } FragWindow.Init(WinSize); @@ -126,6 +130,7 @@ void Init(size_t WinSize,bool Solid) // RAR files, which may access unused areas of sliding dictionary. memset(NewWindow,0,WinSize); + // If Window is not NULL, it means that window size has grown. // In solid streams we need to copy data to a new window in such case. // RAR archiving code does not allow it in solid streams now, diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index a77360fb..8700d4af 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -2,6 +2,9 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; #if NETCORE using SharpCompress.Buffers; #endif @@ -73,6 +76,45 @@ namespace SharpCompress } } +#if NET45 + // super fast memset, up to 40x faster than for loop on large arrays + // see https://stackoverflow.com/questions/1897555/what-is-the-equivalent-of-memset-in-c + private static readonly Action MemsetDelegate = CreateMemsetDelegate(); + + private static Action CreateMemsetDelegate() { + var dynamicMethod = new DynamicMethod( + "Memset", + MethodAttributes.Public | MethodAttributes.Static, + CallingConventions.Standard, + null, + new[] { typeof(IntPtr), typeof(byte), typeof(int) }, + typeof(Utility), + true); + var generator = dynamicMethod.GetILGenerator(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldarg_2); + generator.Emit(OpCodes.Initblk); + generator.Emit(OpCodes.Ret); + return (Action)dynamicMethod.CreateDelegate(typeof(Action)); + } + + public static void Memset(byte[] array, byte what, int length) + { + var gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned); + MemsetDelegate(gcHandle.AddrOfPinnedObject(), what, length); + gcHandle.Free(); + } +#else + public static void Memset(byte[] array, byte what, int length) + { + for(var i = 0; i < length; i++) + { + array[i] = what; + } + } +#endif + /// /// Fills the array with an specific value. /// From b8a308012ff940f3d4638215918c7aee5c27f62a Mon Sep 17 00:00:00 2001 From: coderb Date: Mon, 18 Dec 2017 11:01:37 -0500 Subject: [PATCH 052/124] rar5: unpack work --- .../FragmentedWindow.unpack50frag_cpp.cs | 11 ++++++----- .../Compressors/Rar/UnpackV2017/Unpack.cs | 5 +++++ .../Rar/UnpackV2017/Unpack.unpack20_cpp.cs | 2 +- .../Rar/UnpackV2017/Unpack.unpack30_cpp.cs | 4 ++-- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 7 ++++--- .../Compressors/Rar/UnpackV2017/notes.txt | 14 ++++++++++++++ .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 2 +- src/SharpCompress/Utility.cs | 11 ++++++----- 8 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs index 8c425713..6bff0202 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs @@ -20,10 +20,11 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 { partial class FragmentedWindow { + public FragmentedWindow() { - memset(Mem,0,sizeof(Mem)); - memset(MemSize,0,sizeof(MemSize)); + //memset(Mem,0,sizeof(Mem)); + //memset(MemSize,0,sizeof(MemSize)); } @@ -60,10 +61,10 @@ public void Init(size_t WinSize) // smaller than some arbitrary constant. size_t MinSize=Math.Max(Size/(ASIZE(Mem)-BlockNum), 0x400000); - byte *NewMem=null; + byte[] NewMem=null; while (Size>=MinSize) { - NewMem=(byte *)malloc(Size); + NewMem=new byte[Size]; if (NewMem!=null) break; Size-=Size/32; @@ -73,7 +74,7 @@ public void Init(size_t WinSize) // Clean the window to generate the same output when unpacking corrupt // RAR files, which may access to unused areas of sliding dictionary. - memset(NewMem,0,Size); + Utility.Memset(NewMem,0,Size); Mem[BlockNum]=NewMem; TotalSize+=Size; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index 7079e0d9..e50efcfa 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -13,6 +13,11 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) { + // as of 12/2017 .NET limits array indexing to using a signed integer + // MaxWinSize causes unpack to use a fragmented window when the file + // window size exceeds MaxWinSize + MaxWinSize = ((uint)int.MaxValue) + 1; + // may be long.MaxValue which could indicate unknown size (not present in header) DestUnpSize = fileHeader.UncompressedSize; this.fileHeader = fileHeader; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs index 8d389d70..6e54b90c 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs @@ -198,7 +198,7 @@ bool ReadTables20() UnpAudioBlock=(BitField & 0x8000)!=0; if (!(BitField & 0x4000)) - memset(UnpOldTable20,0,sizeof(UnpOldTable20)); + Utility.Memset(UnpOldTable20,0,UnpOldTable20.Length); Inp.addbits(2); uint TableSize; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs index 6e915734..ff4252fb 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs @@ -671,7 +671,7 @@ bool ReadTables30() LowDistRepCount=0; if ((BitField & 0x4000) == 0) - memset(UnpOldTable,0,sizeof(UnpOldTable)); + Utility.Memset(UnpOldTable,0,UnpOldTable.Length); Inp.faddbits(2); for (uint I=0;I ulong (x64) [size_t] -> uint (x86) + +size_t is an unsigned data type defined by several C/C++ standards, e.g. the C99 ISO/IEC 9899 standard, that is defined +in stddef.h.1 It can be further imported by inclusion of stdlib.h as this file internally sub includes stddef.h. +This type is used to represent the size of an object. Library functions that take or return sizes expect them to be of type or +have the return type of size_t. Further, the most frequently used compiler-based operator sizeof should evaluate to a constant +value that is compatible with size_t. + + +20171218 +urggh, this allows things like new int[int.MaxValue] but NOT new byte[uint.MaxValue] +currently arrays are limited to being indexed by an int hence int.MaxValue entries. weak. +To get arrays > 2GB on x64 we need to configure + +https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/file-schema/runtime/gcallowverylargeobjects-element \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index 35a3de44..3873835a 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -366,7 +366,7 @@ internal partial class Unpack ModelPPM PPM; int PPMEscChar; - byte UnpOldTable[HUFF_TABLE_SIZE30]; + readonly byte [] UnpOldTable = new byte[HUFF_TABLE_SIZE30]; int UnpBlockType; // If we already read decoding tables for Unpack v2,v3,v5. diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 8700d4af..f24528c8 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -79,15 +79,15 @@ namespace SharpCompress #if NET45 // super fast memset, up to 40x faster than for loop on large arrays // see https://stackoverflow.com/questions/1897555/what-is-the-equivalent-of-memset-in-c - private static readonly Action MemsetDelegate = CreateMemsetDelegate(); + private static readonly Action MemsetDelegate = CreateMemsetDelegate(); - private static Action CreateMemsetDelegate() { + private static Action CreateMemsetDelegate() { var dynamicMethod = new DynamicMethod( "Memset", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, null, - new[] { typeof(IntPtr), typeof(byte), typeof(int) }, + new[] { typeof(IntPtr), typeof(byte), typeof(uint) }, typeof(Utility), true); var generator = dynamicMethod.GetILGenerator(); @@ -96,13 +96,13 @@ namespace SharpCompress generator.Emit(OpCodes.Ldarg_2); generator.Emit(OpCodes.Initblk); generator.Emit(OpCodes.Ret); - return (Action)dynamicMethod.CreateDelegate(typeof(Action)); + return (Action)dynamicMethod.CreateDelegate(typeof(Action)); } public static void Memset(byte[] array, byte what, int length) { var gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned); - MemsetDelegate(gcHandle.AddrOfPinnedObject(), what, length); + MemsetDelegate(gcHandle.AddrOfPinnedObject(), what, (uint)length); gcHandle.Free(); } #else @@ -115,6 +115,7 @@ namespace SharpCompress } #endif + /// /// Fills the array with an specific value. /// From 11c1b45335c1ed421ac55ffe234d701621f0316d Mon Sep 17 00:00:00 2001 From: coderb Date: Tue, 19 Dec 2017 13:52:57 -0500 Subject: [PATCH 053/124] rar5: unpack wip --- .../Common/Rar/Headers/RarHeader.cs | 2 ++ .../FragmentedWindow.unpack50frag_cpp.cs | 17 ++++++++++------- .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 6 +++--- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index d4bead44..a3fe6a91 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -4,6 +4,8 @@ using SharpCompress.IO; namespace SharpCompress.Common.Rar.Headers { + // http://www.forensicswiki.org/w/images/5/5b/RARFileStructure.txt + // https://www.rarlab.com/technote.htm internal class RarHeader : IRarHeader { private readonly HeaderType headerType; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs index 6bff0202..bbd10c84 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs @@ -36,10 +36,10 @@ public FragmentedWindow() void Reset() { - for (uint I=0;I=MinSize) @@ -70,11 +70,13 @@ public void Init(size_t WinSize) Size-=Size/32; } if (NewMem==null) - throw std::bad_alloc(); + //throw std::bad_alloc(); + throw new InvalidOperationException(); // Clean the window to generate the same output when unpacking corrupt // RAR files, which may access to unused areas of sliding dictionary. - Utility.Memset(NewMem,0,Size); + // sharpcompress: don't need this, freshly allocated above + //Utility.Memset(NewMem,0,Size); Mem[BlockNum]=NewMem; TotalSize+=Size; @@ -82,7 +84,8 @@ public void Init(size_t WinSize) BlockNum++; } if (TotalSize Date: Tue, 19 Dec 2017 14:03:53 -0500 Subject: [PATCH 054/124] rar5: unpack wip --- .../Rar/UnpackV2017/Unpack.unpack20_cpp.cs | 2 +- .../Rar/UnpackV2017/Unpack.unpack30_cpp.cs | 19 ++++++++++------- .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 3 ++- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 3 +-- .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 21 ++++++++++--------- src/SharpCompress/Utility.cs | 8 +++++++ 6 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs index 6e54b90c..7ed33a4b 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs @@ -303,7 +303,7 @@ void UnpInitData20(bool Solid) UnpChannels=1; memset(AudV,0,sizeof(AudV)); - memset(UnpOldTable20,0,sizeof(UnpOldTable20)); + Utility.Memset(UnpOldTable20, 0, UnpOldTable20.Length); memset(MD,0,sizeof(MD)); } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs index ff4252fb..6ccdeb6a 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs @@ -766,7 +766,7 @@ void UnpInitData30(bool Solid) if (!Solid) { TablesRead3=false; - Utility.Memset(UnpOldTable,0,(uint)UnpOldTable.Length); + Utility.Memset(UnpOldTable, 0, UnpOldTable.Length); PPMEscChar=2; UnpBlockType=BLOCK_LZ; } @@ -778,16 +778,19 @@ void InitFilters30(bool Solid) { if (!Solid) { - OldFilterLengths.SoftReset(); + //OldFilterLengths.SoftReset(); + OldFilterLengths.Clear(); LastFilter=0; - for (size_t I=0;I(this.OldDist, 0, this.OldDist.Length); OldDistPtr=0; LastDist=LastLength=0; // memset(Window,0,MaxWinSize); diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index 498847eb..909830c9 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -264,13 +264,14 @@ internal partial class Unpack byte *ReadBufMT; #endif - List FilterSrcMemory = new List(); - List FilterDstMemory = new List(); + readonly List FilterSrcMemory = new List(); + readonly List FilterDstMemory = new List(); // Filters code, one entry per filter. - List Filters = new List(); + readonly List Filters = new List(); - uint OldDist[4],OldDistPtr; + readonly uint[] OldDist = new uint[4]; + uint OldDistPtr; uint LastLength; // LastDist is necessary only for RAR2 and older with circular OldDist @@ -294,7 +295,7 @@ internal partial class Unpack byte[] Window; - FragmentedWindow FragWindow = new FragmentedWindow(); + readonly FragmentedWindow FragWindow = new FragmentedWindow(); bool Fragmented; @@ -331,7 +332,7 @@ internal partial class Unpack DecodeTable MD[4]; // Decode multimedia data, up to 4 channels. - byte UnpOldTable20[MC20*4]; + readonly byte[] UnpOldTable20 = new byte[MC20*4]; bool UnpAudioBlock; uint UnpChannels,UnpCurChannel; int UnpChannelDelta; @@ -381,18 +382,18 @@ internal partial class Unpack // Buffer to read VM filters code. We moved it here from AddVMCode // function to reduce time spent in BitInput constructor. - BitInput VMCodeInp; + readonly BitInput VMCodeInp = new BitInput(true); // Filters code, one entry per filter. - List Filters30 = new List(); + readonly List Filters30 = new List(); // Filters stack, several entrances of same filter are possible. - List PrgStack = new List(); + readonly List PrgStack = new List(); // Lengths of preceding data blocks, one length of one last block // for every filter. Used to reduce the size required to write // the data block length if lengths are repeating. - List OldFilterLengths = new List(); + readonly List OldFilterLengths = new List(); int LastFilter; /***************************** Unpack v 3.0 *********************************/ diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index f24528c8..a334af06 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -115,6 +115,14 @@ namespace SharpCompress } #endif + public static void Memset(T[] array, T what, int length) + { + for(var i = 0; i < length; i++) + { + array[i] = what; + } + } + /// /// Fills the array with an specific value. From c3c2fcf4d0d5acc345a3ed749b42ed4679626859 Mon Sep 17 00:00:00 2001 From: coderb Date: Tue, 19 Dec 2017 21:41:36 -0500 Subject: [PATCH 055/124] rar5: unpack wip --- .../Compressors/Rar/Decode/AudioVariables.cs | 26 --- .../Compressors/Rar/Decode/BitDecode.cs | 10 - .../Compressors/Rar/Decode/CodeType.cs | 15 -- .../Compressors/Rar/Decode/Decode.cs | 34 --- .../Compressors/Rar/Decode/DistDecode.cs | 10 - .../Compressors/Rar/Decode/FilterType.cs | 10 - .../Compressors/Rar/Decode/LitDecode.cs | 10 - .../Compressors/Rar/Decode/LowDistDecode.cs | 10 - .../Compressors/Rar/Decode/MultDecode.cs | 10 - .../Compressors/Rar/Decode/PackDef.cs | 48 ---- .../Compressors/Rar/Decode/RepDecode.cs | 10 - .../Compressors/Rar/PPM/BlockTypes.cs | 8 - .../Compressors/Rar/UnpackUtility.cs | 220 ------------------ .../Compressors/Rar/UnpackV1/Unpack.cs | 4 +- .../Compressors/Rar/UnpackV1/Unpack15.cs | 2 +- .../Compressors/Rar/UnpackV1/Unpack20.cs | 2 +- .../Compressors/Rar/UnpackV1/Unpack50.cs | 9 +- .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 15 +- 18 files changed, 14 insertions(+), 439 deletions(-) delete mode 100644 src/SharpCompress/Compressors/Rar/Decode/AudioVariables.cs delete mode 100644 src/SharpCompress/Compressors/Rar/Decode/BitDecode.cs delete mode 100644 src/SharpCompress/Compressors/Rar/Decode/CodeType.cs delete mode 100644 src/SharpCompress/Compressors/Rar/Decode/Decode.cs delete mode 100644 src/SharpCompress/Compressors/Rar/Decode/DistDecode.cs delete mode 100644 src/SharpCompress/Compressors/Rar/Decode/FilterType.cs delete mode 100644 src/SharpCompress/Compressors/Rar/Decode/LitDecode.cs delete mode 100644 src/SharpCompress/Compressors/Rar/Decode/LowDistDecode.cs delete mode 100644 src/SharpCompress/Compressors/Rar/Decode/MultDecode.cs delete mode 100644 src/SharpCompress/Compressors/Rar/Decode/PackDef.cs delete mode 100644 src/SharpCompress/Compressors/Rar/Decode/RepDecode.cs delete mode 100644 src/SharpCompress/Compressors/Rar/PPM/BlockTypes.cs delete mode 100644 src/SharpCompress/Compressors/Rar/UnpackUtility.cs diff --git a/src/SharpCompress/Compressors/Rar/Decode/AudioVariables.cs b/src/SharpCompress/Compressors/Rar/Decode/AudioVariables.cs deleted file mode 100644 index cb70b1fd..00000000 --- a/src/SharpCompress/Compressors/Rar/Decode/AudioVariables.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace SharpCompress.Compressors.Rar.Decode -{ - internal class AudioVariables - { - internal AudioVariables() - { - Dif = new int[11]; - } - - internal int[] Dif { get; } - internal int ByteCount { get; set; } - internal int D1 { get; set; } - - internal int D2 { get; set; } - internal int D3 { get; set; } - internal int D4 { get; set; } - - internal int K1 { get; set; } - internal int K2 { get; set; } - internal int K3 { get; set; } - internal int K4 { get; set; } - internal int K5 { get; set; } - internal int LastChar { get; set; } - internal int LastDelta { get; set; } - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Decode/BitDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/BitDecode.cs deleted file mode 100644 index eef2007f..00000000 --- a/src/SharpCompress/Compressors/Rar/Decode/BitDecode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SharpCompress.Compressors.Rar.Decode -{ - internal class BitDecode : Decode - { - internal BitDecode() - : base(new int[PackDef.BC]) - { - } - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Decode/CodeType.cs b/src/SharpCompress/Compressors/Rar/Decode/CodeType.cs deleted file mode 100644 index acbd4aac..00000000 --- a/src/SharpCompress/Compressors/Rar/Decode/CodeType.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace SharpCompress.Compressors.Rar.Decode -{ - internal enum CodeType - { - CODE_HUFFMAN, - CODE_LZ, - CODE_LZ2, - CODE_REPEATLZ, - CODE_CACHELZ, - CODE_STARTFILE, - CODE_ENDFILE, - CODE_VM, - CODE_VMDATA - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Decode/Decode.cs b/src/SharpCompress/Compressors/Rar/Decode/Decode.cs deleted file mode 100644 index dc5dacee..00000000 --- a/src/SharpCompress/Compressors/Rar/Decode/Decode.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace SharpCompress.Compressors.Rar.Decode -{ - internal class Decode - { - internal Decode() - : this(new int[2]) - { - } - - protected Decode(int[] customDecodeNum) - { - DecodeLen = new int[16]; - DecodePos = new int[16]; - DecodeNum = customDecodeNum; - } - - /// returns the decode Length array - /// decodeLength - /// - internal int[] DecodeLen { get; } - - /// returns the decode num array - /// decodeNum - /// - internal int[] DecodeNum { get; } - - /// returns the decodePos array - /// decodePos - /// - internal int[] DecodePos { get; } - - internal int MaxNum { get; set; } - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Decode/DistDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/DistDecode.cs deleted file mode 100644 index b5127e2d..00000000 --- a/src/SharpCompress/Compressors/Rar/Decode/DistDecode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SharpCompress.Compressors.Rar.Decode -{ - internal class DistDecode : Decode - { - internal DistDecode() - : base(new int[PackDef.DC]) - { - } - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Decode/FilterType.cs b/src/SharpCompress/Compressors/Rar/Decode/FilterType.cs deleted file mode 100644 index cb64f783..00000000 --- a/src/SharpCompress/Compressors/Rar/Decode/FilterType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SharpCompress.Compressors.Rar.Decode -{ - internal enum FilterType : byte - { - // These values must not be changed, because we use them directly - // in RAR5 compression and decompression code. - FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM, - FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Decode/LitDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/LitDecode.cs deleted file mode 100644 index 0c58184e..00000000 --- a/src/SharpCompress/Compressors/Rar/Decode/LitDecode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SharpCompress.Compressors.Rar.Decode -{ - internal class LitDecode : Decode - { - internal LitDecode() - : base(new int[PackDef.NC]) - { - } - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Decode/LowDistDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/LowDistDecode.cs deleted file mode 100644 index bafb0c2f..00000000 --- a/src/SharpCompress/Compressors/Rar/Decode/LowDistDecode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SharpCompress.Compressors.Rar.Decode -{ - internal class LowDistDecode : Decode - { - internal LowDistDecode() - : base(new int[PackDef.LDC]) - { - } - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Decode/MultDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/MultDecode.cs deleted file mode 100644 index 6c21dc83..00000000 --- a/src/SharpCompress/Compressors/Rar/Decode/MultDecode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SharpCompress.Compressors.Rar.Decode -{ - internal class MultDecode : Decode - { - internal MultDecode() - : base(new int[PackDef.MC20]) - { - } - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs b/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs deleted file mode 100644 index 472e7e5b..00000000 --- a/src/SharpCompress/Compressors/Rar/Decode/PackDef.cs +++ /dev/null @@ -1,48 +0,0 @@ -namespace SharpCompress.Compressors.Rar.Decode -{ - internal static class PackDef - { - // 20171217 NOTE: these contants are gone from unrar src code - // seems to be more dynamic - public const int MAXWINSIZE = 0x400000; - public const int MAXWINMASK = MAXWINSIZE - 1; - - public const uint MAX_LZ_MATCH = 0x1001; - public const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. - public const int LOW_DIST_REP_COUNT = 16; - - public const int NC = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ - public const int DC = 60; - public const int LDC = 17; - public const int RC = 28; -// 20171217: NOTE: these constants seem to have been updated in the unrar src code -// at some unknown point. updating causes decompression failure, not sure why. -// public const int NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ -// public const int DC = 64; -// public const int LDC = 16; -// public const int RC = 44; - public const int HUFF_TABLE_SIZE = NC + DC + RC + LDC; - public const int BC = 20; - - public const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ - public const uint DC30 = 60; - public const uint LDC30 = 17; - public const uint RC30 = 28; - public const uint BC30 = 20; - public const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30; - - public const int NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ - public const int DC20 = 48; - public const int RC20 = 28; - public const int BC20 = 19; - public const int MC20 = 257; - - // Largest alphabet size among all values listed above. - public const uint LARGEST_TABLE_SIZE = 306; - - //public enum { - // CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, - // CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA - //} - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/Decode/RepDecode.cs b/src/SharpCompress/Compressors/Rar/Decode/RepDecode.cs deleted file mode 100644 index 1f4a42ef..00000000 --- a/src/SharpCompress/Compressors/Rar/Decode/RepDecode.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SharpCompress.Compressors.Rar.Decode -{ - internal class RepDecode : Decode - { - internal RepDecode() - : base(new int[PackDef.RC]) - { - } - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/PPM/BlockTypes.cs b/src/SharpCompress/Compressors/Rar/PPM/BlockTypes.cs deleted file mode 100644 index 2550c47a..00000000 --- a/src/SharpCompress/Compressors/Rar/PPM/BlockTypes.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace SharpCompress.Compressors.Rar.PPM -{ - internal enum BlockTypes - { - BLOCK_LZ = 0, - BLOCK_PPM = 1 - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackUtility.cs b/src/SharpCompress/Compressors/Rar/UnpackUtility.cs deleted file mode 100644 index 3c7a0692..00000000 --- a/src/SharpCompress/Compressors/Rar/UnpackUtility.cs +++ /dev/null @@ -1,220 +0,0 @@ -using SharpCompress.Compressors.Rar.VM; - -namespace SharpCompress.Compressors.Rar -{ - internal static class UnpackUtility - { -//!!! TODO rename methods - internal static uint DecodeNumber(this BitInput input, Decode.Decode dec) { - return (uint)input.decodeNumber(dec); - } - - internal static int decodeNumber(this BitInput input, Decode.Decode dec) - { - int bits; - long bitField = input.GetBits() & 0xfffe; - - // if (bitField < dec.getDecodeLen()[8]) { - // if (bitField < dec.getDecodeLen()[4]) { - // if (bitField < dec.getDecodeLen()[2]) { - // if (bitField < dec.getDecodeLen()[1]) { - // bits = 1; - // } else { - // bits = 2; - // } - // } else { - // if (bitField < dec.getDecodeLen()[3]) { - // bits = 3; - // } else { - // bits = 4; - // } - // } - // } else { - // if (bitField < dec.getDecodeLen()[6]) { - // if (bitField < dec.getDecodeLen()[5]) - // bits = 5; - // else - // bits = 6; - // } else { - // if (bitField < dec.getDecodeLen()[7]) { - // bits = 7; - // } else { - // bits = 8; - // } - // } - // } - // } else { - // if (bitField < dec.getDecodeLen()[12]) { - // if (bitField < dec.getDecodeLen()[10]) - // if (bitField < dec.getDecodeLen()[9]) - // bits = 9; - // else - // bits = 10; - // else if (bitField < dec.getDecodeLen()[11]) - // bits = 11; - // else - // bits = 12; - // } else { - // if (bitField < dec.getDecodeLen()[14]) { - // if (bitField < dec.getDecodeLen()[13]) { - // bits = 13; - // } else { - // bits = 14; - // } - // } else { - // bits = 15; - // } - // } - // } - // addbits(bits); - // int N = dec.getDecodePos()[bits] - // + (((int) bitField - dec.getDecodeLen()[bits - 1]) >>> (16 - bits)); - // if (N >= dec.getMaxNum()) { - // N = 0; - // } - // return (dec.getDecodeNum()[N]); - int[] decodeLen = dec.DecodeLen; - if (bitField < decodeLen[8]) - { - if (bitField < decodeLen[4]) - { - if (bitField < decodeLen[2]) - { - if (bitField < decodeLen[1]) - { - bits = 1; - } - else - { - bits = 2; - } - } - else - { - if (bitField < decodeLen[3]) - { - bits = 3; - } - else - { - bits = 4; - } - } - } - else - { - if (bitField < decodeLen[6]) - { - if (bitField < decodeLen[5]) - { - bits = 5; - } - else - { - bits = 6; - } - } - else - { - if (bitField < decodeLen[7]) - { - bits = 7; - } - else - { - bits = 8; - } - } - } - } - else - { - if (bitField < decodeLen[12]) - { - if (bitField < decodeLen[10]) - { - if (bitField < decodeLen[9]) - { - bits = 9; - } - else - { - bits = 10; - } - } - else if (bitField < decodeLen[11]) - { - bits = 11; - } - else - { - bits = 12; - } - } - else - { - if (bitField < decodeLen[14]) - { - if (bitField < decodeLen[13]) - { - bits = 13; - } - else - { - bits = 14; - } - } - else - { - bits = 15; - } - } - } - input.AddBits(bits); - int N = dec.DecodePos[bits] + (Utility.URShift(((int)bitField - decodeLen[bits - 1]), (16 - bits))); - if (N >= dec.MaxNum) - { - N = 0; - } - return (dec.DecodeNum[N]); - } - - internal static void makeDecodeTables(byte[] lenTab, int offset, Decode.Decode dec, int size) - { - int[] lenCount = new int[16]; - int[] tmpPos = new int[16]; - int i; - long M, N; - - Utility.Fill(lenCount, 0); // memset(LenCount,0,sizeof(LenCount)); - - Utility.Fill(dec.DecodeNum, 0); // memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum)); - - for (i = 0; i < size; i++) - { - lenCount[lenTab[offset + i] & 0xF]++; - } - lenCount[0] = 0; - for (tmpPos[0] = 0, dec.DecodePos[0] = 0, dec.DecodeLen[0] = 0, N = 0, i = 1; i < 16; i++) - { - N = 2 * (N + lenCount[i]); - M = N << (15 - i); - if (M > 0xFFFF) - { - M = 0xFFFF; - } - dec.DecodeLen[i] = (int)M; - tmpPos[i] = dec.DecodePos[i] = dec.DecodePos[i - 1] + lenCount[i - 1]; - } - - for (i = 0; i < size; i++) - { - if (lenTab[offset + i] != 0) - { - dec.DecodeNum[tmpPos[lenTab[offset + i] & 0xF]++] = i; - } - } - dec.MaxNum = size; - } - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs index 9d11fe67..b576361a 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs @@ -4,8 +4,8 @@ using System.IO; using SharpCompress.Common; using SharpCompress.Common.Rar.Headers; using SharpCompress.Compressors.PPMd.H; -using SharpCompress.Compressors.Rar.Decode; -using SharpCompress.Compressors.Rar.PPM; +using SharpCompress.Compressors.Rar.UnpackV1.Decode; +using SharpCompress.Compressors.Rar.UnpackV1.PPM; using SharpCompress.Compressors.Rar.VM; namespace SharpCompress.Compressors.Rar.UnpackV1 diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs index 1aa00556..6c0e09f3 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack15.cs @@ -9,7 +9,7 @@ using System; using System.IO; -using SharpCompress.Compressors.Rar.Decode; +using SharpCompress.Compressors.Rar.UnpackV1.Decode; namespace SharpCompress.Compressors.Rar.UnpackV1 { diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs index cde11395..db8e8cd7 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack20.cs @@ -8,7 +8,7 @@ */ using System; -using SharpCompress.Compressors.Rar.Decode; +using SharpCompress.Compressors.Rar.UnpackV1.Decode; namespace SharpCompress.Compressors.Rar.UnpackV1 { diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs index e4104197..01d3604f 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs @@ -1,13 +1,8 @@ #if true using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using SharpCompress.Compressors.Filters; -using SharpCompress.Compressors.Rar.Decode; +using SharpCompress.Compressors.Rar.UnpackV1.Decode; using SharpCompress.Compressors.Rar.VM; -using static SharpCompress.Compressors.Rar.Decode.PackDef; using size_t=System.UInt32; using UnpackBlockHeader = SharpCompress.Compressors.Rar.UnpackV1; @@ -180,7 +175,7 @@ public bool TablePresent; break; } - if (((WriteBorder-UnpPtr) & MaxWinMask)DestUnpSize) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index 909830c9..8db5c9bc 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -15,7 +15,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using static SharpCompress.Compressors.Rar.Decode.PackDef; +using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; +using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; namespace SharpCompress.Compressors.Rar.UnpackV2017 { @@ -51,7 +52,7 @@ public const int UNPACK_MAX_WRITE =0x400000; } // Decode compressed bit fields to alphabet numbers. -struct DecodeTable +sealed class DecodeTable { // Real size of DecodeNum table. uint MaxNum; @@ -61,11 +62,11 @@ struct DecodeTable // range for bit length and DecodeLen[BitLength] defines next code // after the end of range or in other words the upper limit code // for specified bit length. - uint DecodeLen[16]; + readonly uint[] DecodeLen = new uint[16]; // Every item of this array contains the sum of all preceding items. // So it contains the start position in code list for every bit length. - uint DecodePos[16]; + readonly uint[] DecodePos = new uint[16]; // Number of compressed bits processed in quick mode. // Must not exceed MAX_QUICK_DECODE_BITS. @@ -73,13 +74,13 @@ struct DecodeTable // Translates compressed bits (up to QuickBits length) // to bit length in quick mode. - byte QuickLen[1< Date: Wed, 20 Dec 2017 00:17:47 -0500 Subject: [PATCH 056/124] rar5: wip --- .../Compressors/Rar/UnpackV2017/Unpack.cs | 7 ++ .../Rar/UnpackV2017/Unpack.unpack20_cpp.cs | 118 +++++++++--------- .../Rar/UnpackV2017/Unpack.unpack30_cpp.cs | 10 +- .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 62 ++++----- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 24 ++-- .../UnpackV2017/Unpack.unpackinline_cpp.cs | 22 ++-- .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 52 ++++---- 7 files changed, 153 insertions(+), 142 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index e50efcfa..029a8e5c 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -11,6 +11,13 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 private Stream readStream; private Stream writeStream; + private void _UnpackCtor() { + BlockTables.Init(); + for (int i = 0; i < this.AudV.Length; i++) { + this.AudV[i] = new AudioVariables(); + } + } + public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) { // as of 12/2017 .NET limits array indexing to using a signed integer diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs index 7ed33a4b..648385b2 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs @@ -71,7 +71,7 @@ void Unpack20(bool Solid) } if (UnpAudioBlock) { - uint AudioNumber=DecodeNumber(Inp,&MD[UnpCurChannel]); + uint AudioNumber=DecodeNumber(Inp,MD[UnpCurChannel]); if (AudioNumber==256) { @@ -86,7 +86,7 @@ void Unpack20(bool Solid) continue; } - uint Number=DecodeNumber(Inp,&BlockTables.LD); + uint Number=DecodeNumber(Inp,BlockTables.LD); if (Number<256) { Window[UnpPtr++]=(byte)Number; @@ -102,7 +102,7 @@ void Unpack20(bool Solid) Inp.addbits(Bits); } - uint DistNumber=DecodeNumber(Inp,&BlockTables.DD); + uint DistNumber=DecodeNumber(Inp,BlockTables.DD); uint Distance=DDecode[DistNumber]+1; if ((Bits=DBits[DistNumber])>0) { @@ -134,7 +134,7 @@ void Unpack20(bool Solid) if (Number<261) { uint Distance=OldDist[(OldDistPtr-(Number-256)) & 3]; - uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); + uint LengthNumber=DecodeNumber(Inp,BlockTables.RD); uint Length=LDecode[LengthNumber]+2; if ((Bits=LBits[LengthNumber])>0) { @@ -197,7 +197,7 @@ bool ReadTables20() uint BitField=Inp.getbits(); UnpAudioBlock=(BitField & 0x8000)!=0; - if (!(BitField & 0x4000)) + if ((BitField & 0x4000) != 0) Utility.Memset(UnpOldTable20,0,UnpOldTable20.Length); Inp.addbits(2); @@ -218,13 +218,13 @@ bool ReadTables20() BitLength[I]=(byte)(Inp.getbits() >> 12); Inp.addbits(4); } - MakeDecodeTables(BitLength,&BlockTables.BD,BC20); + MakeDecodeTables(BitLength,BlockTables.BD,BC20); for (uint I=0;IReadTop-5) if (!UnpReadBuf()) return false; - uint Number=DecodeNumber(Inp,&BlockTables.BD); + uint Number=DecodeNumber(Inp,BlockTables.BD); if (Number<16) { Table[I]=(Number+UnpOldTable20[I]) & 0xf; @@ -269,9 +269,9 @@ bool ReadTables20() MakeDecodeTables(&Table[I*MC20],&MD[I],MC20); else { - MakeDecodeTables(&Table[0],&BlockTables.LD,NC20); - MakeDecodeTables(&Table[NC20],&BlockTables.DD,DC20); - MakeDecodeTables(&Table[NC20+DC20],&BlockTables.RD,RC20); + MakeDecodeTables(&Table[0],BlockTables.LD,NC20); + MakeDecodeTables(&Table[NC20],BlockTables.DD,DC20); + MakeDecodeTables(&Table[NC20+DC20],BlockTables.RD,RC20); } memcpy(UnpOldTable20,Table,sizeof(UnpOldTable20)); return true; @@ -283,11 +283,11 @@ void ReadLastTables() if (ReadTop>=Inp.InAddr+5) if (UnpAudioBlock) { - if (DecodeNumber(Inp,&MD[UnpCurChannel])==256) + if (DecodeNumber(Inp,MD[UnpCurChannel])==256) ReadTables20(); } else - if (DecodeNumber(Inp,&BlockTables.LD)==269) + if (DecodeNumber(Inp,BlockTables.LD)==269) ReadTables20(); } @@ -311,13 +311,13 @@ void UnpInitData20(bool Solid) byte DecodeAudio(int Delta) { - struct AudioVariables *V=&AudV[UnpCurChannel]; - V->ByteCount++; - V->D4=V->D3; - V->D3=V->D2; - V->D2=V->LastDelta-V->D1; - V->D1=V->LastDelta; - int PCh=8*V->LastChar+V->K1*V->D1+V->K2*V->D2+V->K3*V->D3+V->K4*V->D4+V->K5*UnpChannelDelta; + AudioVariables V=AudV[UnpCurChannel]; + V.ByteCount++; + V.D4=V.D3; + V.D3=V.D2; + V.D2=V.LastDelta-V.D1; + V.D1=V.LastDelta; + int PCh=8*V.LastChar+V.K1*V.D1+V.K2*V.D2+V.K3*V.D3+V.K4*V.D4+V.K5*UnpChannelDelta; PCh=(PCh>>3) & 0xFF; uint Ch=PCh-Delta; @@ -327,75 +327,75 @@ byte DecodeAudio(int Delta) // so we cast it to unsigned to follow the standard. D=(uint)D<<3; - V->Dif[0]+=abs(D); - V->Dif[1]+=abs(D-V->D1); - V->Dif[2]+=abs(D+V->D1); - V->Dif[3]+=abs(D-V->D2); - V->Dif[4]+=abs(D+V->D2); - V->Dif[5]+=abs(D-V->D3); - V->Dif[6]+=abs(D+V->D3); - V->Dif[7]+=abs(D-V->D4); - V->Dif[8]+=abs(D+V->D4); - V->Dif[9]+=abs(D-UnpChannelDelta); - V->Dif[10]+=abs(D+UnpChannelDelta); + V.Dif[0]+=Math.Abs(D); + V.Dif[1]+=Math.Abs(D-V.D1); + V.Dif[2]+=Math.Abs(D+V.D1); + V.Dif[3]+=Math.Abs(D-V.D2); + V.Dif[4]+=Math.Abs(D+V.D2); + V.Dif[5]+=Math.Abs(D-V.D3); + V.Dif[6]+=Math.Abs(D+V.D3); + V.Dif[7]+=Math.Abs(D-V.D4); + V.Dif[8]+=Math.Abs(D+V.D4); + V.Dif[9]+=Math.Abs(D-UnpChannelDelta); + V.Dif[10]+=Math.Abs(D+UnpChannelDelta); - UnpChannelDelta=V->LastDelta=(sbyte)(Ch-V->LastChar); - V->LastChar=Ch; + UnpChannelDelta=V.LastDelta=(sbyte)(Ch-V.LastChar); + V.LastChar=Ch; - if ((V->ByteCount & 0x1F)==0) + if ((V.ByteCount & 0x1F)==0) { - uint MinDif=V->Dif[0],NumMinDif=0; - V->Dif[0]=0; - for (uint I=1;IDif);I++) + uint MinDif=V.Dif[0],NumMinDif=0; + V.Dif[0]=0; + for (uint I=1;IDif[I]Dif[I]; + MinDif=V.Dif[I]; NumMinDif=I; } - V->Dif[I]=0; + V.Dif[I]=0; } switch(NumMinDif) { case 1: - if (V->K1>=-16) - V->K1--; + if (V.K1>=-16) + V.K1--; break; case 2: - if (V->K1<16) - V->K1++; + if (V.K1<16) + V.K1++; break; case 3: - if (V->K2>=-16) - V->K2--; + if (V.K2>=-16) + V.K2--; break; case 4: - if (V->K2<16) - V->K2++; + if (V.K2<16) + V.K2++; break; case 5: - if (V->K3>=-16) - V->K3--; + if (V.K3>=-16) + V.K3--; break; case 6: - if (V->K3<16) - V->K3++; + if (V.K3<16) + V.K3++; break; case 7: - if (V->K4>=-16) - V->K4--; + if (V.K4>=-16) + V.K4--; break; case 8: - if (V->K4<16) - V->K4++; + if (V.K4<16) + V.K4++; break; case 9: - if (V->K5>=-16) - V->K5--; + if (V.K5>=-16) + V.K5--; break; case 10: - if (V->K5<16) - V->K5++; + if (V.K5<16) + V.K5++; break; } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs index 6ccdeb6a..43291ffc 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs @@ -159,7 +159,7 @@ void Unpack29(bool Solid) continue; } - uint Number=DecodeNumber(Inp,&BlockTables.LD); + uint Number=DecodeNumber(Inp,BlockTables.LD); if (Number<256) { Window[UnpPtr++]=(byte)Number; @@ -174,7 +174,7 @@ void Unpack29(bool Solid) Inp.addbits(Bits); } - uint DistNumber=DecodeNumber(Inp,&BlockTables.DD); + uint DistNumber=DecodeNumber(Inp,BlockTables.DD); uint Distance=DDecode[DistNumber]+1; if ((Bits=DBits[DistNumber])>0) { @@ -192,7 +192,7 @@ void Unpack29(bool Solid) } else { - uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); + uint LowDist=DecodeNumber(Inp,BlockTables.LDD); if (LowDist==16) { LowDistRepCount=LOW_DIST_REP_COUNT-1; @@ -250,7 +250,7 @@ void Unpack29(bool Solid) OldDist[I]=OldDist[I-1]; OldDist[0]=Distance; - uint LengthNumber=DecodeNumber(Inp,&BlockTables.RD); + uint LengthNumber=DecodeNumber(Inp,BlockTables.RD); int Length=LDecode[LengthNumber]+2; if ((Bits=LBits[LengthNumber])>0) { @@ -703,7 +703,7 @@ bool ReadTables30() if (Inp.InAddr>ReadTop-5) if (!UnpReadBuf30()) return(false); - uint Number=DecodeNumber(Inp,&BlockTables.BD); + uint Number=DecodeNumber(Inp,BlockTables.BD); if (Number<16) { Table[I]=(Number+UnpOldTable[I]) & 0xf; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index 5fce0695..42b0f82e 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -11,10 +11,6 @@ using size_t = System.UInt64; using int64 = System.Int64; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; @@ -35,8 +31,8 @@ void Unpack5(bool Solid) // Check TablesRead5 to be sure that we read tables at least once // regardless of current block header TablePresent flag. // So we can safefly use these tables below. - if (!ReadBlockHeader(Inp,BlockHeader) || - !ReadTables(Inp,BlockHeader,BlockTables) || !TablesRead5) + if (!ReadBlockHeader(Inp,ref BlockHeader) || + !ReadTables(Inp,ref BlockHeader, ref BlockTables) || !TablesRead5) return; } @@ -59,7 +55,7 @@ void Unpack5(bool Solid) FileDone=true; break; } - if (!ReadBlockHeader(Inp,BlockHeader) || !ReadTables(Inp,BlockHeader,BlockTables)) + if (!ReadBlockHeader(Inp,ref BlockHeader) || !ReadTables(Inp, ref BlockHeader, ref BlockTables)) return; } if (FileDone || !UnpReadBuf()) @@ -78,7 +74,7 @@ void Unpack5(bool Solid) } } - uint MainSlot=DecodeNumber(Inp,&BlockTables.LD); + uint MainSlot=DecodeNumber(Inp,BlockTables.LD); if (MainSlot<256) { if (Fragmented) @@ -91,7 +87,7 @@ void Unpack5(bool Solid) { uint Length=SlotToLength(Inp,MainSlot-262); - uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,&BlockTables.DD); + uint DBits,Distance=1,DistSlot=DecodeNumber(Inp,BlockTables.DD); if (DistSlot<4) { DBits=0; @@ -112,7 +108,7 @@ void Unpack5(bool Solid) Distance+=((Inp.getbits32()>>(36-DBits))<<4); Inp.addbits(DBits-4); } - uint LowDist=DecodeNumber(Inp,&BlockTables.LDD); + uint LowDist=DecodeNumber(Inp,BlockTables.LDD); Distance+=LowDist; } else @@ -165,7 +161,7 @@ void Unpack5(bool Solid) OldDist[I]=OldDist[I-1]; OldDist[0]=Distance; - uint LengthSlot=DecodeNumber(Inp,&BlockTables.RD); + uint LengthSlot=DecodeNumber(Inp,BlockTables.RD); uint Length=SlotToLength(Inp,LengthSlot); LastLength=Length; if (Fragmented) @@ -232,7 +228,7 @@ bool AddFilter(UnpackFilter Filter) // flag and process this filter only after processing that older data. Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart; - Filter.BlockStart=uint((Filter.BlockStart+UnpPtr)&MaxWinMask); + Filter.BlockStart=(uint)((Filter.BlockStart+UnpPtr)&MaxWinMask); Filters.Push(Filter); return true; } @@ -282,7 +278,9 @@ void UnpWriteBuf() size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; size_t WriteSizeLeft=FullWriteSize; bool NotAllFiltersProcessed=false; - for (size_t I=0;I int + for (int I=0;I int + for (int J=I;J>8); @@ -552,7 +552,7 @@ void UnpInitData50(bool Solid) } -bool ReadBlockHeader(BitInput Inp,UnpackBlockHeader &Header) +bool ReadBlockHeader(BitInput Inp,ref UnpackBlockHeader Header) { Header.HeaderSize=0; @@ -583,12 +583,12 @@ bool ReadBlockHeader(BitInput Inp,UnpackBlockHeader &Header) } Header.BlockSize=BlockSize; - byte CheckSum=byte(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16)); + byte CheckSum=(byte)(0x5a^BlockFlags^BlockSize^(BlockSize>>8)^(BlockSize>>16)); if (CheckSum!=SavedCheckSum) return false; Header.BlockStart=Inp.InAddr; - ReadBorder=Min(ReadBorder,Header.BlockStart+Header.BlockSize-1); + ReadBorder=Math.Min(ReadBorder,Header.BlockStart+Header.BlockSize-1); Header.LastBlockInFile=(BlockFlags & 0x40)!=0; Header.TablePresent=(BlockFlags & 0x80)!=0; @@ -596,7 +596,7 @@ bool ReadBlockHeader(BitInput Inp,UnpackBlockHeader &Header) } -bool ReadTables(BitInput Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables) +bool ReadTables(BitInput Inp,ref UnpackBlockHeader Header, ref UnpackBlockTables Tables) { if (!Header.TablePresent) return true; @@ -605,7 +605,7 @@ bool ReadTables(BitInput Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables if (!UnpReadBuf()) return false; - byte BitLength[BC]; + byte[] BitLength = new byte[BC]; for (uint I=0;I> 12); @@ -619,7 +619,7 @@ bool ReadTables(BitInput Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables else { ZeroCount+=2; - while (ZeroCount-- > 0 && I 0 && IReadTop-5) if (!UnpReadBuf()) return false; - uint Number=DecodeNumber(Inp,&Tables.BD); + uint Number=DecodeNumber(Inp,Tables.BD); if (Number<16) { Table[I]=Number; @@ -693,10 +693,10 @@ bool ReadTables(BitInput Inp,UnpackBlockHeader &Header,UnpackBlockTables &Tables TablesRead5=true; if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop) return false; - MakeDecodeTables(&Table[0],&Tables.LD,NC); - MakeDecodeTables(&Table[NC],&Tables.DD,DC); - MakeDecodeTables(&Table[NC+DC],&Tables.LDD,LDC); - MakeDecodeTables(&Table[NC+DC+LDC],&Tables.RD,RC); + MakeDecodeTables(&Table[0],Tables.LD,NC); + MakeDecodeTables(&Table[NC],Tables.DD,DC); + MakeDecodeTables(&Table[NC+DC],Tables.LDD,LDC); + MakeDecodeTables(&Table[NC+DC+LDC],Tables.RD,RC); return true; } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index 86953ccc..f6c92bc9 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -27,6 +27,8 @@ public Unpack(/* ComprDataIO *DataIO */) //:Inp(true),VMCodeInp(true) : base(true) { + _UnpackCtor(); + UnpIO=DataIO; Window=null; Fragmented=false; @@ -227,14 +229,14 @@ void UnpInitData(bool Solid) // LengthTable contains the length in bits for every element of alphabet. // Dec is the structure to decode Huffman code/ // Size is size of length table and DecodeNum field in Dec structure, -void MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size) +void MakeDecodeTables(byte *LengthTable,DecodeTable Dec,uint Size) { // Size of alphabet and DecodePos array. - Dec->MaxNum=Size; + Dec.MaxNum=Size; // Calculate how many entries for every bit length in LengthTable we have. - uint LengthCount[16]; - memset(LengthCount,0,sizeof(LengthCount)); + uint[] LengthCount = new uint[16]; + //memset(LengthCount,0,sizeof(LengthCount)); for (size_t I=0;IDecodeNum,0,Size*sizeof(*Dec->DecodeNum)); // Initialize not really used entry for zero length code. - Dec->DecodePos[0]=0; + Dec.DecodePos[0]=0; // Start code for bit length 1 is 0. - Dec->DecodeLen[0]=0; + Dec.DecodeLen[0]=0; // Right aligned upper limit code for current bit length. uint UpperLimit=0; @@ -265,17 +267,17 @@ void MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size) UpperLimit*=2; // Store the left aligned upper limit code. - Dec->DecodeLen[I]=(uint)LeftAligned; + Dec.DecodeLen[I]=(uint)LeftAligned; // Every item of this array contains the sum of all preceding items. // So it contains the start position in code list for every bit length. - Dec->DecodePos[I]=Dec->DecodePos[I-1]+LengthCount[I-1]; + Dec.DecodePos[I]=Dec.DecodePos[I-1]+LengthCount[I-1]; } // Prepare the copy of DecodePos. We'll modify this copy below, // so we cannot use the original DecodePos. - uint CopyDecodePos[ASIZE(Dec->DecodePos)]; - memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos)); + uint[] CopyDecodePos = new uint[Dec.DecodePos.Length]; + //memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos)); // For every bit length in the bit length table and so for every item // of alphabet. @@ -291,7 +293,7 @@ void MakeDecodeTables(byte *LengthTable,DecodeTable *Dec,uint Size) // Prepare the decode table, so this position in code list will be // decoded to current alphabet item number. - Dec->DecodeNum[LastPos]=(ushort)I; + Dec.DecodeNum[LastPos]=(ushort)I; // We'll use next position number for this bit length next time. // So we pass through the entire range of positions available diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs index f3af2602..31f3dbce 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs @@ -103,22 +103,22 @@ void CopyString(uint Length,uint Distance) } -uint DecodeNumber(BitInput Inp,DecodeTable *Dec) +uint DecodeNumber(BitInput Inp,DecodeTable Dec) { // Left aligned 15 bit length raw bit field. uint BitField=Inp.getbits() & 0xfffe; - if (BitFieldDecodeLen[Dec->QuickBits]) + if (BitField>(16-Dec->QuickBits); - Inp.addbits(Dec->QuickLen[Code]); - return Dec->QuickNum[Code]; + uint Code=BitField>>(16-Dec.QuickBits); + Inp.addbits(Dec.QuickLen[Code]); + return Dec.QuickNum[Code]; } // Detect the real bit length for current code. uint Bits=15; - for (uint I=Dec->QuickBits+1;I<15;I++) - if (BitFieldDecodeLen[I]) + for (uint I=Dec.QuickBits+1;I<15;I++) + if (BitFieldDecodeLen[Bits-1]; + uint Dist=BitField-Dec.DecodeLen[Bits-1]; // Start codes are left aligned, but we need the normal right aligned // number. So we shift the distance to the right. @@ -136,15 +136,15 @@ uint DecodeNumber(BitInput Inp,DecodeTable *Dec) // Now we can calculate the position in the code list. It is the sum // of first position for current bit length and right aligned distance // between our bit field and start code for current bit length. - uint Pos=Dec->DecodePos[Bits]+Dist; + uint Pos=Dec.DecodePos[Bits]+Dist; // Out of bounds safety check required for damaged archives. - if (Pos>=Dec->MaxNum) + if (Pos>=Dec.MaxNum) Pos=0; // Convert the position in the code list to position in alphabet // and return it. - return Dec->DecodeNum[Pos]; + return Dec.DecodeNum[Pos]; } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index 8db5c9bc..ce35a154 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -10,11 +10,7 @@ using size_t = System.UInt64; #endif using int64 = System.Int64; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; @@ -55,32 +51,32 @@ public const int UNPACK_MAX_WRITE =0x400000; sealed class DecodeTable { // Real size of DecodeNum table. - uint MaxNum; + public uint MaxNum; // Left aligned start and upper limit codes defining code space // ranges for bit lengths. DecodeLen[BitLength-1] defines the start of // range for bit length and DecodeLen[BitLength] defines next code // after the end of range or in other words the upper limit code // for specified bit length. - readonly uint[] DecodeLen = new uint[16]; + public readonly uint[] DecodeLen = new uint[16]; // Every item of this array contains the sum of all preceding items. // So it contains the start position in code list for every bit length. - readonly uint[] DecodePos = new uint[16]; + public readonly uint[] DecodePos = new uint[16]; // Number of compressed bits processed in quick mode. // Must not exceed MAX_QUICK_DECODE_BITS. - uint QuickBits; + public uint QuickBits; // Translates compressed bits (up to QuickBits length) // to bit length in quick mode. - readonly byte[] QuickLen = new byte[1< Date: Wed, 20 Dec 2017 00:29:07 -0500 Subject: [PATCH 057/124] rar5: wip --- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 39 ++++++++++--------- src/SharpCompress/Utility.cs | 17 ++++++++ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index f6c92bc9..96897814 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -29,7 +29,7 @@ public Unpack(/* ComprDataIO *DataIO */) { _UnpackCtor(); - UnpIO=DataIO; + //UnpIO=DataIO; Window=null; Fragmented=false; Suspended=false; @@ -203,9 +203,10 @@ void UnpInitData(bool Solid) OldDistPtr=0; LastDist=LastLength=0; // memset(Window,0,MaxWinSize); - memset(&BlockTables,0,sizeof(BlockTables)); + //memset(&BlockTables,0,sizeof(BlockTables)); + BlockTables = new UnpackBlockTables(); UnpPtr=WrPtr=0; - WriteBorder=Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; + WriteBorder=Math.Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; } // Filters never share several solid files, so we can safely reset them // even in solid archive. @@ -216,7 +217,8 @@ void UnpInitData(bool Solid) ReadTop=0; ReadBorder=0; - memset(&BlockHeader,0,sizeof(BlockHeader)); + //memset(&BlockHeader,0,sizeof(BlockHeader)); + BlockHeader = new UnpackBlockHeader(); BlockHeader.BlockSize=-1; // '-1' means not defined yet. #if !RarV2017_SFX_MODULE UnpInitData20(Solid); @@ -229,7 +231,7 @@ void UnpInitData(bool Solid) // LengthTable contains the length in bits for every element of alphabet. // Dec is the structure to decode Huffman code/ // Size is size of length table and DecodeNum field in Dec structure, -void MakeDecodeTables(byte *LengthTable,DecodeTable Dec,uint Size) +void MakeDecodeTables(byte[] LengthTable,DecodeTable Dec,uint Size) { // Size of alphabet and DecodePos array. Dec.MaxNum=Size; @@ -244,7 +246,8 @@ void MakeDecodeTables(byte *LengthTable,DecodeTable Dec,uint Size) LengthCount[0]=0; // Set the entire DecodeNum to zero. - memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum)); + //memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum)); + Utility.FillFast(Dec.DecodeNum, 0); // Initialize not really used entry for zero length code. Dec.DecodePos[0]=0; @@ -284,7 +287,7 @@ void MakeDecodeTables(byte *LengthTable,DecodeTable Dec,uint Size) for (uint I=0;IQuickBits=MAX_QUICK_DECODE_BITS; + Dec.QuickBits=MAX_QUICK_DECODE_BITS; break; default: - Dec->QuickBits=MAX_QUICK_DECODE_BITS-3; + Dec.QuickBits=MAX_QUICK_DECODE_BITS-3; break; } // Size of tables for quick mode. - uint QuickDataSize=1<QuickBits; + uint QuickDataSize=1<QuickBits); + uint BitField=Code<<(16-Dec.QuickBits); // Prepare the table for quick decoding of bit lengths. // Find the upper limit for current bit field and adjust the bit length // accordingly if necessary. - while (CurBitLengthDecodeLen) && BitField>=Dec->DecodeLen[CurBitLength]) + while (CurBitLength=Dec.DecodeLen[CurBitLength]) CurBitLength++; // Translation of right aligned bit string to bit length. - Dec->QuickLen[Code]=CurBitLength; + Dec.QuickLen[Code]=CurBitLength; // Prepare the table for quick translation of position in code list // to position in alphabet. // Calculate the distance from the start code for current bit length. - uint Dist=BitField-Dec->DecodeLen[CurBitLength-1]; + uint Dist=BitField-Dec.DecodeLen[CurBitLength-1]; // Right align the distance. Dist>>=(16-CurBitLength); @@ -355,16 +358,16 @@ void MakeDecodeTables(byte *LengthTable,DecodeTable Dec,uint Size) // of first position for current bit length and right aligned distance // between our bit field and start code for current bit length. uint Pos; - if (CurBitLengthDecodePos) && - (Pos=Dec->DecodePos[CurBitLength]+Dist)QuickNum[Code]=Dec->DecodeNum[Pos]; + Dec.QuickNum[Code]=Dec.DecodeNum[Pos]; } else { // Can be here for length table filled with zeroes only (empty). - Dec->QuickNum[Code]=0; + Dec.QuickNum[Code]=0; } } } diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index a334af06..d5dd62d3 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -123,6 +123,23 @@ namespace SharpCompress } } + public static void FillFast(T[] array, T val) where T : struct + { + for (int i = 0; i < array.Length; i++) + { + array[i] = val; + } + } + + public static void FillFast(T[] array, int start, int length, T val) where T : struct + { + int toIndex = start + length; + for (int i = start; i < toIndex; i++) + { + array[i] = val; + } + } + /// /// Fills the array with an specific value. From bdaa060003c5b367df59a65b73d46c45a728b968 Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 01:21:53 -0500 Subject: [PATCH 058/124] rar5: unpack wip --- .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 4 ++-- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 21 ++++++++----------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index 42b0f82e..388916ea 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -139,7 +139,7 @@ void Unpack5(bool Solid) } if (MainSlot==256) { - UnpackFilter Filter; + UnpackFilter Filter = new UnpackFilter(); if (!ReadFilter(Inp,Filter) || !AddFilter(Filter)) break; continue; @@ -229,7 +229,7 @@ bool AddFilter(UnpackFilter Filter) Filter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=Filter.BlockStart; Filter.BlockStart=(uint)((Filter.BlockStart+UnpPtr)&MaxWinMask); - Filters.Push(Filter); + Filters.Add(Filter); return true; } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index 96897814..f7c9217f 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -10,10 +10,6 @@ using size_t = System.UInt64; #endif using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using SharpCompress.Common; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; @@ -258,7 +254,7 @@ void MakeDecodeTables(byte[] LengthTable,DecodeTable Dec,uint Size) // Right aligned upper limit code for current bit length. uint UpperLimit=0; - for (size_t I=1;I<16;I++) + for (int I=1;I<16;I++) { // Adjust the upper limit code. UpperLimit+=LengthCount[I]; @@ -287,12 +283,12 @@ void MakeDecodeTables(byte[] LengthTable,DecodeTable Dec,uint Size) for (uint I=0;I Date: Wed, 20 Dec 2017 01:39:13 -0500 Subject: [PATCH 059/124] rar5: unpack wip --- .../Compressors/Rar/UnpackV2017/Unpack.cs | 12 ++ .../UnpackV2017/Unpack.unpackinline_cpp.cs | 117 +++++++++--------- 2 files changed, 72 insertions(+), 57 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index 029a8e5c..8508b784 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -1,7 +1,19 @@ #if RARWIP +#if !Rar2017_64bit +using nint = System.Int32; +using nuint = System.UInt32; +using size_t = System.UInt32; +#else +using nint = System.Int64; +using nuint = System.UInt64; +using size_t = System.UInt64; +#endif +using int64 = System.Int64; + using System; using System.IO; using SharpCompress.Common.Rar.Headers; +using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; namespace SharpCompress.Compressors.Rar.UnpackV2017 { diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs index 31f3dbce..0f839584 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs @@ -10,11 +10,6 @@ using size_t = System.UInt64; #endif using int64 = System.Int64; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; namespace SharpCompress.Compressors.Rar.UnpackV2017 @@ -37,60 +32,68 @@ void InsertOldDist(uint Distance) void CopyString(uint Length,uint Distance) { size_t SrcPtr=UnpPtr-Distance; + if (SrcPtr 0) + { + Window[UnpPtr++] = Window[SrcPtr++]; + } -#if FAST_MEMCPY - if (Distance=8) - { - Dest[0]=Src[0]; - Dest[1]=Src[1]; - Dest[2]=Src[2]; - Dest[3]=Src[3]; - Dest[4]=Src[4]; - Dest[5]=Src[5]; - Dest[6]=Src[6]; - Dest[7]=Src[7]; - - Src+=8; - Dest+=8; - Length-=8; - } -#if FAST_MEMCPY - else - while (Length>=8) - { - // In theory we still could overlap here. - // Supposing Distance == MaxWinSize - 1 we have memcpy(Src, Src + 1, 8). - // But for real RAR archives Distance <= MaxWinSize - MAX_LZ_MATCH - // always, so overlap here is impossible. - - // This memcpy expanded inline by MSVC. We could also use uint64 - // assignment, which seems to provide about the same speed. - memcpy(Dest,Src,8); - - Src+=8; - Dest+=8; - Length-=8; - } -#endif - - // Unroll the loop for 0 - 7 bytes left. Note that we use nested "if"s. - if (Length>0) { Dest[0]=Src[0]; - if (Length>1) { Dest[1]=Src[1]; - if (Length>2) { Dest[2]=Src[2]; - if (Length>3) { Dest[3]=Src[3]; - if (Length>4) { Dest[4]=Src[4]; - if (Length>5) { Dest[5]=Src[5]; - if (Length>6) { Dest[6]=Src[6]; } } } } } } } // Close all nested "if"s. +// byte *Src=Window+SrcPtr; +// byte *Dest=Window+UnpPtr; +// UnpPtr+=Length; +// +//#if FAST_MEMCPY +// if (Distance=8) +// { +// Dest[0]=Src[0]; +// Dest[1]=Src[1]; +// Dest[2]=Src[2]; +// Dest[3]=Src[3]; +// Dest[4]=Src[4]; +// Dest[5]=Src[5]; +// Dest[6]=Src[6]; +// Dest[7]=Src[7]; +// +// Src+=8; +// Dest+=8; +// Length-=8; +// } +//#if FAST_MEMCPY +// else +// while (Length>=8) +// { +// // In theory we still could overlap here. +// // Supposing Distance == MaxWinSize - 1 we have memcpy(Src, Src + 1, 8). +// // But for real RAR archives Distance <= MaxWinSize - MAX_LZ_MATCH +// // always, so overlap here is impossible. +// +// // This memcpy expanded inline by MSVC. We could also use uint64 +// // assignment, which seems to provide about the same speed. +// memcpy(Dest,Src,8); +// +// Src+=8; +// Dest+=8; +// Length-=8; +// } +//#endif +// +// // Unroll the loop for 0 - 7 bytes left. Note that we use nested "if"s. +// if (Length>0) { Dest[0]=Src[0]; +// if (Length>1) { Dest[1]=Src[1]; +// if (Length>2) { Dest[2]=Src[2]; +// if (Length>3) { Dest[3]=Src[3]; +// if (Length>4) { Dest[4]=Src[4]; +// if (Length>5) { Dest[5]=Src[5]; +// if (Length>6) { Dest[6]=Src[6]; } } } } } } } // Close all nested "if"s. } else while (Length-- > 0) // Slow copying with all possible precautions. @@ -110,7 +113,7 @@ uint DecodeNumber(BitInput Inp,DecodeTable Dec) if (BitField>(16-Dec.QuickBits); + uint Code=BitField>>(int)(16-Dec.QuickBits); Inp.addbits(Dec.QuickLen[Code]); return Dec.QuickNum[Code]; } @@ -131,7 +134,7 @@ uint DecodeNumber(BitInput Inp,DecodeTable Dec) // Start codes are left aligned, but we need the normal right aligned // number. So we shift the distance to the right. - Dist>>=(16-Bits); + Dist>>=(int)(16-Bits); // Now we can calculate the position in the code list. It is the sum // of first position for current bit length and right aligned distance @@ -159,12 +162,12 @@ uint SlotToLength(BitInput Inp,uint Slot) else { LBits=Slot/4-1; - Length+=(4 | (Slot & 3)) << LBits; + Length+=(4 | (Slot & 3)) << (int)LBits; } if (LBits>0) { - Length+=Inp.getbits()>>(16-LBits); + Length+=Inp.getbits()>>(int)(16-LBits); Inp.addbits(LBits); } return Length; From aaeaa44c0bf1a65a53f4c2bf7e7f116aa6d58218 Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 01:55:54 -0500 Subject: [PATCH 060/124] rar5: unpack wip --- .../FragmentedWindow.unpack50frag_cpp.cs | 27 ++++++++++++++----- .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 6 ++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs index bbd10c84..f8f3d6dc 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs @@ -89,23 +89,36 @@ public void Init(size_t WinSize) } -byte& operator [](size_t Item) -{ +byte this[size_t Item] { +get { if (Item 0) { - (*this)[UnpPtr]=(*this)[SrcPtr++ & MaxWinMask]; + this[UnpPtr]=this[SrcPtr++ & MaxWinMask]; // We need to have masked UnpPtr after quit from loop, so it must not // be replaced with '(*this)[UnpPtr++ & MaxWinMask]' UnpPtr=(UnpPtr+1) & MaxWinMask; @@ -116,13 +129,13 @@ public void CopyString(uint Length,uint Distance,size_t &UnpPtr,size_t MaxWinMas void CopyData(byte *Dest,size_t WinPos,size_t Size) { for (size_t I=0;I0) @@ -105,7 +105,7 @@ void Unpack5(bool Solid) { if (DBits>4) { - Distance+=((Inp.getbits32()>>(36-DBits))<<4); + Distance+=((Inp.getbits32()>>(int)(36-DBits))<<4); Inp.addbits(DBits-4); } uint LowDist=DecodeNumber(Inp,BlockTables.LDD); @@ -113,7 +113,7 @@ void Unpack5(bool Solid) } else { - Distance+=Inp.getbits32()>>(32-DBits); + Distance+=Inp.getbits32()>>(int)(32-DBits); Inp.addbits(DBits); } } From 2d0319f779a83f9691e2c645995a14b101c177c8 Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 02:08:31 -0500 Subject: [PATCH 061/124] rar5: unpack wip --- .../FragmentedWindow.unpack50frag_cpp.cs | 4 +-- .../Rar/UnpackV2017/Unpack.unpack15_cpp.cs | 2 ++ .../Rar/UnpackV2017/Unpack.unpack20_cpp.cs | 2 ++ .../Rar/UnpackV2017/Unpack.unpack30_cpp.cs | 6 ++-- .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 28 +++++++++---------- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 6 ++++ .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 6 ++++ 7 files changed, 36 insertions(+), 18 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs index f8f3d6dc..32ebd57b 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs @@ -89,7 +89,7 @@ public void Init(size_t WinSize) } -byte this[size_t Item] { +public byte this[size_t Item] { get { if (Item 0 && I 0 && I>8)<<(I*8); + Data+=(Inp.fgetbits()>>8)<<(int)(I*8); Inp.addbits(8); } return Data; @@ -201,12 +201,12 @@ bool ReadFilter(BitInput Inp,UnpackFilter Filter) if (Filter.BlockLength>MAX_FILTER_BLOCK_SIZE) Filter.BlockLength=0; - Filter.Type=Inp.fgetbits()>>13; + Filter.Type=(byte)(Inp.fgetbits()>>13); Inp.faddbits(3); if (Filter.Type==FILTER_DELTA) { - Filter.Channels=(Inp.fgetbits()>>11)+1; + Filter.Channels=(byte)((Inp.fgetbits()>>11)+1); Inp.faddbits(5); } @@ -559,26 +559,26 @@ bool ReadBlockHeader(BitInput Inp,ref UnpackBlockHeader Header) if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-7) if (!UnpReadBuf()) return false; - Inp.faddbits((8-Inp.InBit)&7); + Inp.faddbits((uint)((8-Inp.InBit)&7)); - byte BlockFlags=Inp.fgetbits()>>8; + byte BlockFlags=(byte)(Inp.fgetbits()>>8); Inp.faddbits(8); - uint ByteCount=((BlockFlags>>3)&3)+1; // Block size byte count. + uint ByteCount=(uint)(((BlockFlags>>3)&3)+1); // Block size byte count. if (ByteCount==4) return false; - Header.HeaderSize=2+ByteCount; + Header.HeaderSize=(int)(2+ByteCount); Header.BlockBitSize=(BlockFlags&7)+1; - byte SavedCheckSum=Inp.fgetbits()>>8; + byte SavedCheckSum=(byte)(Inp.fgetbits()>>8); Inp.faddbits(8); int BlockSize=0; for (uint I=0;I>8)<<(I*8); + BlockSize+=(int)((Inp.fgetbits()>>8)<<(int)(I*8)); Inp.addbits(8); } @@ -625,7 +625,7 @@ bool ReadTables(BitInput Inp,ref UnpackBlockHeader Header, ref UnpackBlockTables } } else - BitLength[I]=Length; + BitLength[I]=(byte)Length; } MakeDecodeTables(BitLength,Tables.BD,BC); @@ -640,7 +640,7 @@ bool ReadTables(BitInput Inp,ref UnpackBlockHeader Header, ref UnpackBlockTables uint Number=DecodeNumber(Inp,Tables.BD); if (Number<16) { - Table[I]=Number; + Table[I]=(byte)Number; I++; } else diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index f7c9217f..352da170 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -44,11 +44,13 @@ public Unpack(/* ComprDataIO *DataIO */) // It prevents crash if first DoUnpack call is later made with wrong // (true) 'Solid' value. UnpInitData(false); +#if !RarV2017_RAR5ONLY #if !RarV2017_SFX_MODULE // RAR 1.5 decompression initialization UnpInitData15(false); InitHuff(); #endif +#endif } // later: may need Dispose() if we support thread pool @@ -154,6 +156,7 @@ void DoUnpack(uint Method,bool Solid) // just for extra safety. switch(Method) { +#if !RarV2017_RAR5ONLY #if !RarV2017_SFX_MODULE case 15: // rar 1.5 compression if (!Fragmented) @@ -169,6 +172,7 @@ void DoUnpack(uint Method,bool Solid) if (!Fragmented) Unpack29(Solid); break; +#endif case 50: // RAR 5.0 compression algorithm. #if RarV2017_RAR_SMP if (MaxUserThreads>1) @@ -216,10 +220,12 @@ void UnpInitData(bool Solid) //memset(&BlockHeader,0,sizeof(BlockHeader)); BlockHeader = new UnpackBlockHeader(); BlockHeader.BlockSize=-1; // '-1' means not defined yet. +#if !RarV2017_RAR5ONLY #if !RarV2017_SFX_MODULE UnpInitData20(Solid); #endif UnpInitData30(Solid); +#endif UnpInitData50(Solid); } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index ce35a154..893c6d2f 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -193,7 +193,9 @@ class UnpackFilter30 // in PrgStack array. Not defined for filters in Filters array. public uint ParentFilter; +#if !RarV2017_RAR5ONLY public VM_PreparedProgram Prg; +#endif }; @@ -369,7 +371,9 @@ internal partial class Unpack int PrevLowDist,LowDistRepCount; +#if !RarV2017_RAR5ONLY ModelPPM PPM; +#endif int PPMEscChar; readonly byte [] UnpOldTable = new byte[HUFF_TABLE_SIZE30]; @@ -383,7 +387,9 @@ internal partial class Unpack bool TablesRead2,TablesRead3,TablesRead5; // Virtual machine to execute filters code. +#if !RarV2017_RAR5ONLY RarVM VM; +#endif // Buffer to read VM filters code. We moved it here from AddVMCode // function to reduce time spent in BitInput constructor. From 0d408831769bbdc463e908f837a3029086a0283f Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 11:19:30 -0500 Subject: [PATCH 062/124] unrar5: unpack wip --- .../FragmentedWindow.unpack50frag_cpp.cs | 4 +- .../Compressors/Rar/UnpackV2017/Unpack.cs | 4 ++ .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 40 +++++++++++-------- .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 4 +- 4 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs index 32ebd57b..ca1b78df 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs @@ -126,10 +126,10 @@ public void CopyString(uint Length,uint Distance,ref size_t UnpPtr,size_t MaxWin } -public void CopyData(byte *Dest,size_t WinPos,size_t Size) +public void CopyData(byte[] Dest, size_t destOffset, size_t WinPos,size_t Size) { for (size_t I=0;I this.PPMEscChar; set => this.PPMEscChar = value; } + + public static byte[] EnsureCapacity(byte[] array, int length) { + return array.Length < length ? new byte[length] : array; + } } } #endif \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index 57dfa466..73fb3722 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -325,31 +325,35 @@ void UnpWriteBuf() { uint BlockEnd=(BlockStart+BlockLength)&MaxWinMask; - FilterSrcMemory.Alloc(BlockLength); - byte *Mem=&FilterSrcMemory[0]; + //x FilterSrcMemory.Alloc(BlockLength); + FilterSrcMemory = EnsureCapacity(FilterSrcMemory, checked((int)BlockLength)); + byte[] Mem= FilterSrcMemory; if (BlockStart int + int EmptyCount=0; + // sharpcompress: size_t -> int + for (int I=0;I0) Filters[I-EmptyCount]=Filters[I]; @@ -419,9 +425,9 @@ void UnpWriteBuf() } -byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter Flt) +byte[] ApplyFilter(byte[] Data,uint DataSize,UnpackFilter Flt) { - byte *SrcData=Data; + byte[] SrcData=Data; switch(Flt.Type) { case FILTER_E8: @@ -484,8 +490,10 @@ byte* ApplyFilter(byte *Data,uint DataSize,UnpackFilter Flt) // values here, since RAR5 uses only 5 bits to store channel. uint Channels=Flt.Channels,SrcPos=0; - FilterDstMemory.Alloc(DataSize); - byte *DstData=&FilterDstMemory[0]; + //x FilterDstMemory.Alloc(DataSize); + FilterDstMemory = EnsureCapacity(FilterDstMemory, checked((int)DataSize)); + + byte[] DstData=FilterDstMemory; // Bytes from same channels are grouped to continual data blocks, // so we need to place them back to their interleaving positions. diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index 893c6d2f..0c4cc62f 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -271,8 +271,8 @@ internal partial class Unpack byte *ReadBufMT; #endif - readonly List FilterSrcMemory = new List(); - readonly List FilterDstMemory = new List(); + byte[] FilterSrcMemory = new byte[0]; + byte[] FilterDstMemory = new byte[0]; // Filters code, one entry per filter. readonly List Filters = new List(); From fd9790cc36e7dfe5e1911c636717e50da467b484 Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 14:15:20 -0500 Subject: [PATCH 063/124] unrar5: unpack wip --- .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index 73fb3722..b5eda3de 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -249,7 +249,8 @@ bool UnpReadBuf() // is not less than CRYPT_BLOCK_SIZE, so we can align it without risk // to make it zero. if (DataSize>0) - memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + //x memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + Buffer.BlockCopy(Inp.InBuf, Inp.InAddr, Inp.InBuf, 0, DataSize); Inp.InAddr=0; ReadTop=DataSize; } @@ -532,15 +533,15 @@ void UnpWriteArea(size_t StartPtr,size_t EndPtr) else if (EndPtr=DestUnpSize) return; @@ -548,7 +549,7 @@ void UnpWriteData(byte *Data,size_t Size) int64 LeftToWrite=DestUnpSize-WrittenFileSize; if ((int64)WriteSize>LeftToWrite) WriteSize=(size_t)LeftToWrite; - UnpIO->UnpWrite(Data,WriteSize); + UnpIO->UnpWrite(Data, offset, WriteSize); WrittenFileSize+=Size; } From 91c85f0aa62aab597dca306c9d08c78280f75ab0 Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 14:22:47 -0500 Subject: [PATCH 064/124] unrar5: unpack wip --- .../Compressors/Rar/UnpackV2017/Unpack.cs | 10 ++++++++++ .../Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index fdcd0df6..54c877e7 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -30,6 +30,15 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 } } + private int UnpIO_UnpRead(byte[] buf, int offset, int count) { + // NOTE: caller has logic to check for -1 for error we throw instead. + return this.readStream.Read(buf, offset, count); + } + + private void UnpIO_UnpWrite(byte[] buf, size_t offset, uint count) { + this.writeStream.Write(buf, checked((int)offset), checked((int)count)); + } + public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) { // as of 12/2017 .NET limits array indexing to using a signed integer @@ -95,6 +104,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 public static byte[] EnsureCapacity(byte[] array, int length) { return array.Length < length ? new byte[length] : array; } + } } #endif \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index b5eda3de..b362dae0 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -258,7 +258,7 @@ bool UnpReadBuf() DataSize=ReadTop; int ReadCode=0; if (BitInput.MAX_SIZE!=DataSize) - ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput.MAX_SIZE-DataSize); + ReadCode=UnpIO_UnpRead(Inp.InBuf,DataSize,BitInput.MAX_SIZE-DataSize); if (ReadCode>0) // Can be also -1. ReadTop+=ReadCode; ReadBorder=ReadTop-30; @@ -359,7 +359,7 @@ void UnpWriteBuf() Filters[I].Type=FILTER_NONE; if (OutMem!=null) - UnpIO->UnpWrite(OutMem,BlockLength); + UnpIO_UnpWrite(OutMem,0,BlockLength); UnpSomeRead=true; WrittenFileSize+=BlockLength; @@ -549,7 +549,7 @@ void UnpWriteData(byte[] Data, size_t offset, size_t Size) int64 LeftToWrite=DestUnpSize-WrittenFileSize; if ((int64)WriteSize>LeftToWrite) WriteSize=(size_t)LeftToWrite; - UnpIO->UnpWrite(Data, offset, WriteSize); + UnpIO_UnpWrite(Data, offset, WriteSize); WrittenFileSize+=Size; } From 66cb2ab662f67db93d04c520fe82318dfc9a8dac Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 14:28:22 -0500 Subject: [PATCH 065/124] unrar5: unpack wip --- .../Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 10 +++++----- .../Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs | 6 +++--- .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 4 ++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index b362dae0..4cf75009 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -637,7 +637,7 @@ bool ReadTables(BitInput Inp,ref UnpackBlockHeader Header, ref UnpackBlockTables BitLength[I]=(byte)Length; } - MakeDecodeTables(BitLength,Tables.BD,BC); + MakeDecodeTables(BitLength,0,Tables.BD,BC); byte[] Table = new byte[HUFF_TABLE_SIZE]; const uint TableSize=HUFF_TABLE_SIZE; @@ -702,10 +702,10 @@ bool ReadTables(BitInput Inp,ref UnpackBlockHeader Header, ref UnpackBlockTables TablesRead5=true; if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop) return false; - MakeDecodeTables(&Table[0],Tables.LD,NC); - MakeDecodeTables(&Table[NC],Tables.DD,DC); - MakeDecodeTables(&Table[NC+DC],Tables.LDD,LDC); - MakeDecodeTables(&Table[NC+DC+LDC],Tables.RD,RC); + MakeDecodeTables(Table, 0, Tables.LD,NC); + MakeDecodeTables(Table, (int)NC,Tables.DD,DC); + MakeDecodeTables(Table, (int)(NC+DC),Tables.LDD,LDC); + MakeDecodeTables(Table, (int)(NC+DC+LDC),Tables.RD,RC); return true; } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index 352da170..619974c2 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -233,7 +233,7 @@ void UnpInitData(bool Solid) // LengthTable contains the length in bits for every element of alphabet. // Dec is the structure to decode Huffman code/ // Size is size of length table and DecodeNum field in Dec structure, -void MakeDecodeTables(byte[] LengthTable,DecodeTable Dec,uint Size) +void MakeDecodeTables(byte[] LengthTable, int offset, DecodeTable Dec,uint Size) { // Size of alphabet and DecodePos array. Dec.MaxNum=Size; @@ -242,7 +242,7 @@ void MakeDecodeTables(byte[] LengthTable,DecodeTable Dec,uint Size) uint[] LengthCount = new uint[16]; //memset(LengthCount,0,sizeof(LengthCount)); for (size_t I=0;I Date: Wed, 20 Dec 2017 14:58:49 -0500 Subject: [PATCH 066/124] unrar5: unpack wip --- src/SharpCompress/Archives/Rar/RarArchive.cs | 8 ++++++++ .../FragmentedWindow.unpack50frag_cpp.cs | 15 +++++++++++++++ .../Compressors/Rar/UnpackV2017/Unpack.cs | 1 - .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 10 ++++++++-- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 2 ++ src/SharpCompress/Readers/Rar/RarReader.cs | 4 ++++ 6 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index ab341e0c..699073cb 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -24,7 +24,11 @@ namespace SharpCompress.Archives.Rar internal RarArchive(FileInfo fileInfo, ReaderOptions options) : base(ArchiveType.Rar, fileInfo, options) { +#if !RarV2017_USELEGACY + Unpack = new SharpCompress.Compressors.Rar.UnpackV2017.Unpack(); +#else Unpack = new SharpCompress.Compressors.Rar.UnpackV1.Unpack(); +#endif } protected override IEnumerable LoadVolumes(FileInfo file) @@ -41,7 +45,11 @@ namespace SharpCompress.Archives.Rar internal RarArchive(IEnumerable streams, ReaderOptions options) : base(ArchiveType.Rar, streams, options) { +#if !RarV2017_USELEGACY + Unpack = new SharpCompress.Compressors.Rar.UnpackV2017.Unpack(); +#else Unpack = new SharpCompress.Compressors.Rar.UnpackV1.Unpack(); +#endif } protected override IEnumerable LoadEntries(IEnumerable volumes) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs index ca1b78df..8d4dab7a 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs @@ -112,6 +112,21 @@ set { } } +// sharpcompress: added the following code +public void GetBuffer(size_t Item, out byte[] buf, out uint offset) { + if (Item0) - Filters.Alloc(Filters.Count-EmptyCount); + //Filters.Alloc(Filters.Count-EmptyCount); + throw new NotImplementedException(); if (!NotAllFiltersProcessed) // Only if all filters are processed. { @@ -428,6 +429,8 @@ void UnpWriteBuf() byte[] ApplyFilter(byte[] Data,uint DataSize,UnpackFilter Flt) { + throw new NotImplementedException(); +#if false byte[] SrcData=Data; switch(Flt.Type) { @@ -509,6 +512,7 @@ byte[] ApplyFilter(byte[] Data,uint DataSize,UnpackFilter Flt) } return null; +#endif } @@ -525,7 +529,9 @@ void UnpWriteArea(size_t StartPtr,size_t EndPtr) while (SizeToWrite>0) { size_t BlockSize=FragWindow.GetBlockSize(StartPtr,SizeToWrite); - UnpWriteData(&FragWindow[StartPtr],BlockSize); + //UnpWriteData(&FragWindow[StartPtr],BlockSize); + FragWindow.GetBuffer(StartPtr, out var __buffer, out var __offset); + UnpWriteData(__buffer, __offset, BlockSize); SizeToWrite-=BlockSize; StartPtr=(StartPtr+BlockSize) & MaxWinMask; } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index 619974c2..502763ad 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -205,6 +205,8 @@ void UnpInitData(bool Solid) // memset(Window,0,MaxWinSize); //memset(&BlockTables,0,sizeof(BlockTables)); BlockTables = new UnpackBlockTables(); + // sharpcompress: no default ctor for struct + BlockTables.Init(); UnpPtr=WrPtr=0; WriteBorder=Math.Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; } diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index 20afa5e1..71fe260c 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -18,7 +18,11 @@ namespace SharpCompress.Readers.Rar internal RarReader(ReaderOptions options) : base(options, ArchiveType.Rar) { +#if !RarV2017_USELEGACY + this.pack = new SharpCompress.Compressors.Rar.UnpackV2017.Unpack(); +#else this.pack = new SharpCompress.Compressors.Rar.UnpackV1.Unpack(); +#endif } internal abstract void ValidateArchive(RarVolume archive); From 3e8f52689e77678d8a72d6fdffd97e1edaee9710 Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 16:01:55 -0500 Subject: [PATCH 067/124] unrar5: unpack wip --- .../FragmentedWindow.unpack50frag_cpp.cs | 4 +-- .../Compressors/Rar/UnpackV2017/Unpack.cs | 9 +++-- .../Rar/UnpackV2017/Unpack.unpack15_cpp.cs | 34 +++++++++---------- .../Rar/UnpackV2017/Unpack.unpack20_cpp.cs | 18 +++++----- .../Rar/UnpackV2017/Unpack.unpack30_cpp.cs | 12 +++---- .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 4 +-- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 11 +++--- .../UnpackV2017/Unpack.unpackinline_cpp.cs | 4 +-- .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 6 ++-- 9 files changed, 45 insertions(+), 57 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs index 8d4dab7a..b0bc6da7 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs @@ -1,5 +1,4 @@ -#if RARWIP -#if !Rar2017_64bit +#if !Rar2017_64bit using nint = System.Int32; using nuint = System.UInt32; using size_t = System.UInt32; @@ -158,4 +157,3 @@ public size_t GetBlockSize(size_t StartPos,size_t RequiredSize) } } -#endif \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index 6fbf86c0..e40ec2ec 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -1,5 +1,4 @@ -#if RARWIP -#if !Rar2017_64bit +#if !Rar2017_64bit using nint = System.Int32; using nuint = System.UInt32; using size_t = System.UInt32; @@ -43,7 +42,8 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 // as of 12/2017 .NET limits array indexing to using a signed integer // MaxWinSize causes unpack to use a fragmented window when the file // window size exceeds MaxWinSize - MaxWinSize = ((uint)int.MaxValue) + 1; + // uggh, that's not how this variable is used, it's the size of the currently allocated window buffer + //x MaxWinSize = ((uint)int.MaxValue) + 1; // may be long.MaxValue which could indicate unknown size (not present in header) DestUnpSize = fileHeader.UncompressedSize; @@ -63,7 +63,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 { UnstoreFile(); } else { - DoUnpack(this.fileHeader.CompressionMethod, this.fileHeader.IsSolid); + DoUnpack(this.fileHeader.CompressionAlgorithm, this.fileHeader.IsSolid); } } @@ -106,4 +106,3 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 } } -#endif \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs index 917116ef..9889e8e8 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs @@ -1,6 +1,4 @@ -#if RARWIP -#if !RarV2017_RAR5ONLY -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -11,6 +9,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { +#if !RarV2017_RAR5ONLY const int STARTL1 =2; static uint[] DecL1={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00, @@ -439,7 +438,7 @@ void GetFlagsBuf() ChSetC[NewFlagsPlace]=Flags; } - +#endif void UnpInitData15(bool Solid) { if (!Solid) @@ -457,33 +456,33 @@ void UnpInitData15(bool Solid) ReadTop=0; } - void InitHuff() { for (uint I=0;I<256;I++) { - ChSet[I]=ChSetB[I]=I<<8; - ChSetA[I]=I; - ChSetC[I]=((~I+1) & 0xff)<<8; + ChSet[I]=ChSetB[I]=(ushort)(I<<8); + ChSetA[I]=(ushort)I; + ChSetC[I]=(ushort)(((~I+1) & 0xff)<<8); } - memset(NToPl,0,sizeof(NToPl)); - memset(NToPlB,0,sizeof(NToPlB)); - memset(NToPlC,0,sizeof(NToPlC)); + Utility.Memset(NToPl,0,NToPl.Length); + Utility.Memset(NToPlB,0,NToPlB.Length); + Utility.Memset(NToPlC,0,NToPlC.Length); CorrHuff(ChSetB,NToPlB); } -void CorrHuff(ushort *CharSet,byte *NumToPlace) +void CorrHuff(ushort[] CharSet,byte[] NumToPlace) { int I,J; for (I=7;I>=0;I--) - for (J=0;J<32;J++,CharSet++) - *CharSet=(*CharSet & ~0xff) | I; - memset(NumToPlace,0,sizeof(NToPl)); + for (J=0;J<32;J++) + CharSet[J]=(ushort)((CharSet[J] & ~0xff) | I); + Utility.Memset(NumToPlace,0,NToPl.Length); for (I=6;I>=0;I--) - NumToPlace[I]=(7-I)*32; + NumToPlace[I]=(byte)((7-I)*32); } +#if !RarV2017_RAR5ONLY void CopyString15(uint Distance,uint Length) { @@ -504,8 +503,7 @@ uint DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab) Inp.faddbits(StartPos); return(((Num-(I ? DecTab[I-1]:0))>>(16-StartPos))+PosTab[StartPos]); } +#endif } } -#endif -#endif \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs index 4744849b..40c5f055 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs @@ -1,6 +1,4 @@ -#if RARWIP -#if !RarV2017_RAR5ONLY -#if !Rar2017_64bit +#if !Rar2017_64bit using nint = System.Int32; using nuint = System.UInt32; using size_t = System.UInt32; @@ -17,13 +15,14 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; -using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack20Local; +//using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack20Local; namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { +#if !RarV2017_RAR5ONLY void CopyString20(uint Length,uint Distance) { LastDist=OldDist[OldDistPtr++ & 3]=Distance; @@ -292,6 +291,7 @@ void ReadLastTables() ReadTables20(); } +#endif void UnpInitData20(bool Solid) { @@ -303,12 +303,15 @@ void UnpInitData20(bool Solid) UnpCurChannel=0; UnpChannels=1; - memset(AudV,0,sizeof(AudV)); + //memset(AudV,0,sizeof(AudV)); + AudV = new AudioVariables[4]; Utility.Memset(UnpOldTable20, 0, UnpOldTable20.Length); - memset(MD,0,sizeof(MD)); + //memset(MD,0,sizeof(MD)); + MD = new DecodeTable[4]; } } +#if !RarV2017_RAR5ONLY byte DecodeAudio(int Delta) { @@ -402,8 +405,7 @@ byte DecodeAudio(int Delta) } return (byte)Ch; } +#endif } } -#endif -#endif \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs index f440d0d5..4b63e61b 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs @@ -1,6 +1,4 @@ -#if RARWIP -#if !RarV2017_RAR5ONLY -#if !Rar2017_64bit +#if !Rar2017_64bit using nint = System.Int32; using nuint = System.UInt32; using size_t = System.UInt32; @@ -18,12 +16,14 @@ using System.Text; using System.Threading.Tasks; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; -using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack30Local; +//using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack30Local; namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { + +#if !RarV2017_RAR5ONLY // We use it instead of direct PPM.DecodeChar call to be sure that // we reset PPM structures in case of corrupt data. It is important, // because these structures can be invalid after PPM.DecodeChar returned -1. @@ -761,6 +761,7 @@ bool ReadTables30() return true; } +#endif void UnpInitData30(bool Solid) { @@ -794,7 +795,6 @@ void InitFilters30(bool Solid) PrgStack.Clear(); } + } } -#endif -#endif \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index d39dbb98..8e5b7b85 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -1,5 +1,4 @@ -#if RARWIP -#if !Rar2017_64bit +#if !Rar2017_64bit using nint = System.Int32; using nuint = System.UInt32; using size_t = System.UInt32; @@ -724,4 +723,3 @@ void InitFilters() } } -#endif \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index 502763ad..4126bf34 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -1,5 +1,4 @@ -#if RARWIP -#if !Rar2017_64bit +#if !Rar2017_64bit using nint = System.Int32; using nuint = System.UInt32; using size_t = System.UInt32; @@ -44,13 +43,11 @@ public Unpack(/* ComprDataIO *DataIO */) // It prevents crash if first DoUnpack call is later made with wrong // (true) 'Solid' value. UnpInitData(false); -#if !RarV2017_RAR5ONLY #if !RarV2017_SFX_MODULE // RAR 1.5 decompression initialization UnpInitData15(false); InitHuff(); #endif -#endif } // later: may need Dispose() if we support thread pool @@ -191,6 +188,9 @@ void DoUnpack(uint Method,bool Solid) #endif Unpack5(Solid); break; +#if !Rar2017_NOSTRICT + default: throw new InvalidFormatException("unknown compression method " + Method); +#endif } } @@ -222,12 +222,10 @@ void UnpInitData(bool Solid) //memset(&BlockHeader,0,sizeof(BlockHeader)); BlockHeader = new UnpackBlockHeader(); BlockHeader.BlockSize=-1; // '-1' means not defined yet. -#if !RarV2017_RAR5ONLY #if !RarV2017_SFX_MODULE UnpInitData20(Solid); #endif UnpInitData30(Solid); -#endif UnpInitData50(Solid); } @@ -379,4 +377,3 @@ void MakeDecodeTables(byte[] LengthTable, int offset, DecodeTable Dec,uint Size) } } -#endif \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs index 0f839584..cd2fa8f0 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs @@ -1,5 +1,4 @@ -#if RARWIP -#if !Rar2017_64bit +#if !Rar2017_64bit using nint = System.Int32; using nuint = System.UInt32; using size_t = System.UInt32; @@ -175,4 +174,3 @@ uint SlotToLength(BitInput Inp,uint Slot) } } -#endif \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index 3e238e24..afea642c 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -1,5 +1,4 @@ -#if RARWIP -#if !Rar2017_64bit +#if !Rar2017_64bit using nint = System.Int32; using nuint = System.UInt32; using size_t = System.UInt32; @@ -341,7 +340,7 @@ internal partial class Unpack /***************************** Unpack v 2.0 *********************************/ //void Unpack20(bool Solid); - readonly DecodeTable[] MD = new DecodeTable[4]; // Decode multimedia data, up to 4 channels. + DecodeTable[] MD = new DecodeTable[4]; // Decode multimedia data, up to 4 channels. readonly byte[] UnpOldTable20 = new byte[MC20*4]; bool UnpAudioBlock; @@ -442,4 +441,3 @@ internal partial class Unpack } } -#endif \ No newline at end of file From 218823e9b21b3f68cf188e3aa31f4ee56b60e2f1 Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 18:13:04 -0500 Subject: [PATCH 068/124] rar5: unpack bugfix --- .../Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index 4126bf34..5645edec 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -283,6 +283,7 @@ void MakeDecodeTables(byte[] LengthTable, int offset, DecodeTable Dec,uint Size) // so we cannot use the original DecodePos. uint[] CopyDecodePos = new uint[Dec.DecodePos.Length]; //memcpy(CopyDecodePos,Dec->DecodePos,sizeof(CopyDecodePos)); + Array.Copy(Dec.DecodePos, 0, CopyDecodePos, 0, CopyDecodePos.Length); // For every bit length in the bit length table and so for every item // of alphabet. From a1e7f55b953c7a3259b3ccf0e5541a34532785eb Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 18:32:12 -0500 Subject: [PATCH 069/124] rar5: unpack working!!! --- .../Rar/UnpackV2017/Unpack.rawint_hpp.cs | 126 ++++++++++++++++++ .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 33 +++-- 2 files changed, 142 insertions(+), 17 deletions(-) create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs new file mode 100644 index 00000000..0d30cd8e --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.rawint_hpp.cs @@ -0,0 +1,126 @@ +#if !Rar2017_64bit +using nint = System.Int32; +using nuint = System.UInt32; +using size_t = System.UInt32; +#else +using nint = System.Int64; +using nuint = System.UInt64; +using size_t = System.UInt64; +#endif +using int64 = System.Int64; +using uint32 = System.UInt32; + +using System; +using System.Collections.Generic; +using System.Text; + +namespace SharpCompress.Compressors.Rar.UnpackV2017 +{ + partial class Unpack + { +//#define rotls(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n)))) +//#define rotrs(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n)))) +//#define rotl32(x,n) rotls(x,n,32) +//#define rotr32(x,n) rotrs(x,n,32) +// +//inline uint RawGet2(const void *Data) +//{ +// byte *D=(byte *)Data; +// return D[0]+(D[1]<<8); +//} + +uint32 RawGet4(byte[] D, int offset) +{ + return (uint)(D[offset]+(D[offset+1]<<8)+(D[offset+2]<<16)+(D[offset+3]<<24)); +} + +//inline uint64 RawGet8(const void *Data) +//{ +//#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) +// byte *D=(byte *)Data; +// return INT32TO64(RawGet4(D+4),RawGet4(D)); +//#else +// return *(uint64 *)Data; +//#endif +//} +// +// +//inline void RawPut2(uint Field,void *Data) +//{ +// byte *D=(byte *)Data; +// D[0]=(byte)(Field); +// D[1]=(byte)(Field>>8); +//} + +void RawPut4(uint32 Field,byte[] D, int offset) +{ + D[offset]=(byte)(Field); + D[offset+1]=(byte)(Field>>8); + D[offset+2]=(byte)(Field>>16); + D[offset+3]=(byte)(Field>>24); +} + +//inline void RawPut8(uint64 Field,void *Data) +//{ +//#if defined(BIG_ENDIAN) || !defined(ALLOW_MISALIGNED) +// byte *D=(byte *)Data; +// D[0]=(byte)(Field); +// D[1]=(byte)(Field>>8); +// D[2]=(byte)(Field>>16); +// D[3]=(byte)(Field>>24); +// D[4]=(byte)(Field>>32); +// D[5]=(byte)(Field>>40); +// D[6]=(byte)(Field>>48); +// D[7]=(byte)(Field>>56); +//#else +// *(uint64 *)Data=Field; +//#endif +//} + + +//#if defined(LITTLE_ENDIAN) && defined(ALLOW_MISALIGNED) +//#define USE_MEM_BYTESWAP +//#endif + +// Load 4 big endian bytes from memory and return uint32. +//inline uint32 RawGetBE4(const byte *m) +//{ +//#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) +// return _byteswap_ulong(*(uint32 *)m); +//#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) +// return __builtin_bswap32(*(uint32 *)m); +//#else +// return uint32(m[0]<<24) | uint32(m[1]<<16) | uint32(m[2]<<8) | m[3]; +//#endif +//} + + +// Save integer to memory as big endian. +//inline void RawPutBE4(uint32 i,byte *mem) +//{ +//#if defined(USE_MEM_BYTESWAP) && defined(_MSC_VER) +// *(uint32*)mem = _byteswap_ulong(i); +//#elif defined(USE_MEM_BYTESWAP) && (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) +// *(uint32*)mem = __builtin_bswap32(i); +//#else +// mem[0]=byte(i>>24); +// mem[1]=byte(i>>16); +// mem[2]=byte(i>>8); +// mem[3]=byte(i); +//#endif +//} + + +//inline uint32 ByteSwap32(uint32 i) +//{ +//#ifdef _MSC_VER +// return _byteswap_ulong(i); +//#elif (__GNUC__ > 3) && (__GNUC_MINOR__ > 2) +// return __builtin_bswap32(i); +//#else +// return (rotl32(i,24)&0xFF00FF00)|(rotl32(i,8)&0x00FF00FF); +//#endif +//} + + } +} diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index 8e5b7b85..3a0d63c5 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -404,7 +404,7 @@ void UnpWriteBuf() } if (EmptyCount>0) //Filters.Alloc(Filters.Count-EmptyCount); - throw new NotImplementedException(); + Filters.RemoveRange(Filters.Count-EmptyCount, EmptyCount); if (!NotAllFiltersProcessed) // Only if all filters are processed. { @@ -426,11 +426,10 @@ void UnpWriteBuf() } -byte[] ApplyFilter(byte[] Data,uint DataSize,UnpackFilter Flt) +byte[] ApplyFilter(byte[] __d,uint DataSize,UnpackFilter Flt) { - throw new NotImplementedException(); -#if false - byte[] SrcData=Data; + int Data = 0; + byte[] SrcData=__d; switch(Flt.Type) { case FILTER_E8: @@ -444,23 +443,24 @@ byte[] ApplyFilter(byte[] Data,uint DataSize,UnpackFilter Flt) // to avoid overflow for DataSize<4. for (uint CurPos=0;CurPos+4=0 - RawPut4(Addr+FileSize,Data); + RawPut4(Addr+FileSize,__d,Data); } else if (((Addr-FileSize) & 0x80000000)!=0) // Addr>8); - D[2]=(byte)(Offset>>16); + __d[D]=(byte)Offset; + __d[D+1]=(byte)(Offset>>8); + __d[D+2]=(byte)(Offset>>16); } } } @@ -504,14 +504,13 @@ byte[] ApplyFilter(byte[] Data,uint DataSize,UnpackFilter Flt) { byte PrevByte=0; for (uint DestPos=CurChannel;DestPos Date: Wed, 20 Dec 2017 18:32:36 -0500 Subject: [PATCH 070/124] rar5: fix checkin --- .../Rar/UnpackV1/Decode/AudioVariables.cs | 26 +++ .../Rar/UnpackV1/Decode/BitDecode.cs | 10 + .../Rar/UnpackV1/Decode/CodeType.cs | 15 ++ .../Compressors/Rar/UnpackV1/Decode/Decode.cs | 34 +++ .../Rar/UnpackV1/Decode/DistDecode.cs | 10 + .../Rar/UnpackV1/Decode/FilterType.cs | 10 + .../Rar/UnpackV1/Decode/LitDecode.cs | 10 + .../Rar/UnpackV1/Decode/LowDistDecode.cs | 10 + .../Rar/UnpackV1/Decode/MultDecode.cs | 10 + .../Rar/UnpackV1/Decode/PackDef.cs | 48 ++++ .../Rar/UnpackV1/Decode/RepDecode.cs | 10 + .../Rar/UnpackV1/PPM/BlockTypes.cs | 8 + .../Compressors/Rar/UnpackV1/UnpackUtility.cs | 220 ++++++++++++++++++ 13 files changed, 421 insertions(+) create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/Decode/AudioVariables.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/Decode/BitDecode.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/Decode/Decode.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/Decode/DistDecode.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LitDecode.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LowDistDecode.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/Decode/MultDecode.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/Decode/PackDef.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/Decode/RepDecode.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs create mode 100644 src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/AudioVariables.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/AudioVariables.cs new file mode 100644 index 00000000..c2510617 --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/AudioVariables.cs @@ -0,0 +1,26 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +{ + internal class AudioVariables + { + internal AudioVariables() + { + Dif = new int[11]; + } + + internal int[] Dif { get; } + internal int ByteCount { get; set; } + internal int D1 { get; set; } + + internal int D2 { get; set; } + internal int D3 { get; set; } + internal int D4 { get; set; } + + internal int K1 { get; set; } + internal int K2 { get; set; } + internal int K3 { get; set; } + internal int K4 { get; set; } + internal int K5 { get; set; } + internal int LastChar { get; set; } + internal int LastDelta { get; set; } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/BitDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/BitDecode.cs new file mode 100644 index 00000000..a8d499b5 --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/BitDecode.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +{ + internal class BitDecode : Decode + { + internal BitDecode() + : base(new int[PackDef.BC]) + { + } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs new file mode 100644 index 00000000..e9703ff1 --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/CodeType.cs @@ -0,0 +1,15 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +{ + internal enum CodeType + { + CODE_HUFFMAN, + CODE_LZ, + CODE_LZ2, + CODE_REPEATLZ, + CODE_CACHELZ, + CODE_STARTFILE, + CODE_ENDFILE, + CODE_VM, + CODE_VMDATA + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/Decode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/Decode.cs new file mode 100644 index 00000000..ceed74de --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/Decode.cs @@ -0,0 +1,34 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +{ + internal class Decode + { + internal Decode() + : this(new int[2]) + { + } + + protected Decode(int[] customDecodeNum) + { + DecodeLen = new int[16]; + DecodePos = new int[16]; + DecodeNum = customDecodeNum; + } + + /// returns the decode Length array + /// decodeLength + /// + internal int[] DecodeLen { get; } + + /// returns the decode num array + /// decodeNum + /// + internal int[] DecodeNum { get; } + + /// returns the decodePos array + /// decodePos + /// + internal int[] DecodePos { get; } + + internal int MaxNum { get; set; } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/DistDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/DistDecode.cs new file mode 100644 index 00000000..1a0b666b --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/DistDecode.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +{ + internal class DistDecode : Decode + { + internal DistDecode() + : base(new int[PackDef.DC]) + { + } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs new file mode 100644 index 00000000..6d915e44 --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/FilterType.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +{ + internal enum FilterType : byte + { + // These values must not be changed, because we use them directly + // in RAR5 compression and decompression code. + FILTER_DELTA=0, FILTER_E8, FILTER_E8E9, FILTER_ARM, + FILTER_AUDIO, FILTER_RGB, FILTER_ITANIUM, FILTER_PPM, FILTER_NONE + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LitDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LitDecode.cs new file mode 100644 index 00000000..8316c261 --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LitDecode.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +{ + internal class LitDecode : Decode + { + internal LitDecode() + : base(new int[PackDef.NC]) + { + } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LowDistDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LowDistDecode.cs new file mode 100644 index 00000000..a2095bd4 --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/LowDistDecode.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +{ + internal class LowDistDecode : Decode + { + internal LowDistDecode() + : base(new int[PackDef.LDC]) + { + } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/MultDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/MultDecode.cs new file mode 100644 index 00000000..e6df223c --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/MultDecode.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +{ + internal class MultDecode : Decode + { + internal MultDecode() + : base(new int[PackDef.MC20]) + { + } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/PackDef.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/PackDef.cs new file mode 100644 index 00000000..87f1977e --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/PackDef.cs @@ -0,0 +1,48 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +{ + internal static class PackDef + { + // 20171217 NOTE: these contants are gone from unrar src code + // seems to be more dynamic + public const int MAXWINSIZE = 0x400000; + public const int MAXWINMASK = MAXWINSIZE - 1; + + public const uint MAX_LZ_MATCH = 0x1001; + public const uint MAX3_LZ_MATCH = 0x101; // Maximum match length for RAR v3. + public const int LOW_DIST_REP_COUNT = 16; + + public const int NC = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ + public const int DC = 60; + public const int LDC = 17; + public const int RC = 28; +// 20171217: NOTE: these constants seem to have been updated in the unrar src code +// at some unknown point. updating causes decompression failure, not sure why. +// public const int NC = 306; /* alphabet = {0, 1, 2, ..., NC - 1} */ +// public const int DC = 64; +// public const int LDC = 16; +// public const int RC = 44; + public const int HUFF_TABLE_SIZE = NC + DC + RC + LDC; + public const int BC = 20; + + public const uint NC30 = 299; /* alphabet = {0, 1, 2, ..., NC - 1} */ + public const uint DC30 = 60; + public const uint LDC30 = 17; + public const uint RC30 = 28; + public const uint BC30 = 20; + public const uint HUFF_TABLE_SIZE30 = NC30 + DC30 + RC30 + LDC30; + + public const int NC20 = 298; /* alphabet = {0, 1, 2, ..., NC - 1} */ + public const int DC20 = 48; + public const int RC20 = 28; + public const int BC20 = 19; + public const int MC20 = 257; + + // Largest alphabet size among all values listed above. + public const uint LARGEST_TABLE_SIZE = 306; + + //public enum { + // CODE_HUFFMAN, CODE_LZ, CODE_REPEATLZ, CODE_CACHELZ, CODE_STARTFILE, + // CODE_ENDFILE, CODE_FILTER, CODE_FILTERDATA + //} + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/RepDecode.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/RepDecode.cs new file mode 100644 index 00000000..24d72d16 --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Decode/RepDecode.cs @@ -0,0 +1,10 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.Decode +{ + internal class RepDecode : Decode + { + internal RepDecode() + : base(new int[PackDef.RC]) + { + } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs new file mode 100644 index 00000000..b3f79fdc --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/PPM/BlockTypes.cs @@ -0,0 +1,8 @@ +namespace SharpCompress.Compressors.Rar.UnpackV1.PPM +{ + internal enum BlockTypes + { + BLOCK_LZ = 0, + BLOCK_PPM = 1 + } +} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs new file mode 100644 index 00000000..46261496 --- /dev/null +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackUtility.cs @@ -0,0 +1,220 @@ +using SharpCompress.Compressors.Rar.VM; + +namespace SharpCompress.Compressors.Rar.UnpackV1 +{ + internal static class UnpackUtility + { +//!!! TODO rename methods + internal static uint DecodeNumber(this BitInput input, Decode.Decode dec) { + return (uint)input.decodeNumber(dec); + } + + internal static int decodeNumber(this BitInput input, Decode.Decode dec) + { + int bits; + long bitField = input.GetBits() & 0xfffe; + + // if (bitField < dec.getDecodeLen()[8]) { + // if (bitField < dec.getDecodeLen()[4]) { + // if (bitField < dec.getDecodeLen()[2]) { + // if (bitField < dec.getDecodeLen()[1]) { + // bits = 1; + // } else { + // bits = 2; + // } + // } else { + // if (bitField < dec.getDecodeLen()[3]) { + // bits = 3; + // } else { + // bits = 4; + // } + // } + // } else { + // if (bitField < dec.getDecodeLen()[6]) { + // if (bitField < dec.getDecodeLen()[5]) + // bits = 5; + // else + // bits = 6; + // } else { + // if (bitField < dec.getDecodeLen()[7]) { + // bits = 7; + // } else { + // bits = 8; + // } + // } + // } + // } else { + // if (bitField < dec.getDecodeLen()[12]) { + // if (bitField < dec.getDecodeLen()[10]) + // if (bitField < dec.getDecodeLen()[9]) + // bits = 9; + // else + // bits = 10; + // else if (bitField < dec.getDecodeLen()[11]) + // bits = 11; + // else + // bits = 12; + // } else { + // if (bitField < dec.getDecodeLen()[14]) { + // if (bitField < dec.getDecodeLen()[13]) { + // bits = 13; + // } else { + // bits = 14; + // } + // } else { + // bits = 15; + // } + // } + // } + // addbits(bits); + // int N = dec.getDecodePos()[bits] + // + (((int) bitField - dec.getDecodeLen()[bits - 1]) >>> (16 - bits)); + // if (N >= dec.getMaxNum()) { + // N = 0; + // } + // return (dec.getDecodeNum()[N]); + int[] decodeLen = dec.DecodeLen; + if (bitField < decodeLen[8]) + { + if (bitField < decodeLen[4]) + { + if (bitField < decodeLen[2]) + { + if (bitField < decodeLen[1]) + { + bits = 1; + } + else + { + bits = 2; + } + } + else + { + if (bitField < decodeLen[3]) + { + bits = 3; + } + else + { + bits = 4; + } + } + } + else + { + if (bitField < decodeLen[6]) + { + if (bitField < decodeLen[5]) + { + bits = 5; + } + else + { + bits = 6; + } + } + else + { + if (bitField < decodeLen[7]) + { + bits = 7; + } + else + { + bits = 8; + } + } + } + } + else + { + if (bitField < decodeLen[12]) + { + if (bitField < decodeLen[10]) + { + if (bitField < decodeLen[9]) + { + bits = 9; + } + else + { + bits = 10; + } + } + else if (bitField < decodeLen[11]) + { + bits = 11; + } + else + { + bits = 12; + } + } + else + { + if (bitField < decodeLen[14]) + { + if (bitField < decodeLen[13]) + { + bits = 13; + } + else + { + bits = 14; + } + } + else + { + bits = 15; + } + } + } + input.AddBits(bits); + int N = dec.DecodePos[bits] + (Utility.URShift(((int)bitField - decodeLen[bits - 1]), (16 - bits))); + if (N >= dec.MaxNum) + { + N = 0; + } + return (dec.DecodeNum[N]); + } + + internal static void makeDecodeTables(byte[] lenTab, int offset, Decode.Decode dec, int size) + { + int[] lenCount = new int[16]; + int[] tmpPos = new int[16]; + int i; + long M, N; + + Utility.Fill(lenCount, 0); // memset(LenCount,0,sizeof(LenCount)); + + Utility.Fill(dec.DecodeNum, 0); // memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum)); + + for (i = 0; i < size; i++) + { + lenCount[lenTab[offset + i] & 0xF]++; + } + lenCount[0] = 0; + for (tmpPos[0] = 0, dec.DecodePos[0] = 0, dec.DecodeLen[0] = 0, N = 0, i = 1; i < 16; i++) + { + N = 2 * (N + lenCount[i]); + M = N << (15 - i); + if (M > 0xFFFF) + { + M = 0xFFFF; + } + dec.DecodeLen[i] = (int)M; + tmpPos[i] = dec.DecodePos[i] = dec.DecodePos[i - 1] + lenCount[i - 1]; + } + + for (i = 0; i < size; i++) + { + if (lenTab[offset + i] != 0) + { + dec.DecodeNum[tmpPos[lenTab[offset + i] & 0xF]++] = i; + } + } + dec.MaxNum = size; + } + } +} \ No newline at end of file From c42dc646ae99dcd0459ed968ba0d4eb06432d1de Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 18:51:48 -0500 Subject: [PATCH 071/124] rar5: reporting older algos --- .../Rar/UnpackV2017/Unpack.unpack30_cpp.cs | 162 +++++++++--------- 1 file changed, 80 insertions(+), 82 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs index 4b63e61b..ac1be05f 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs @@ -10,20 +10,16 @@ using size_t = System.UInt64; using int64 = System.Int64; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; -//using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack30Local; +using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack30Local; namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { -#if !RarV2017_RAR5ONLY +#if true || !RarV2017_RAR5ONLY // We use it instead of direct PPM.DecodeChar call to be sure that // we reset PPM structures in case of corrupt data. It is important, // because these structures can be invalid after PPM.DecodeChar returned -1. @@ -41,8 +37,8 @@ int SafePPMDecodeChar() internal static class Unpack30Local { public static byte[] LDecode={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; public static byte[] LBits= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; - public static int DDecode[DC]; - public static byte DBits[DC]; + public static int[] DDecode = new int[DC]; + public static byte[] DBits = new byte[DC]; public static int[] DBitLengthCounts= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12}; public static byte[] SDDecode={0,4,8,16,32,64,128,192}; public static byte[] SDBits= {2,2,3, 4, 5, 6, 6, 6}; @@ -54,11 +50,11 @@ void Unpack29(bool Solid) if (DDecode[1]==0) { int Dist=0,BitLength=0,Slot=0; - for (int I=0;I=271) { - uint Length=LDecode[Number-=271]+3; + uint Length=(uint)(LDecode[Number-=271]+3); if ((Bits=LBits[Number])>0) { - Length+=Inp.getbits()>>(16-Bits); + Length+=Inp.getbits()>>(int)(16-Bits); Inp.addbits(Bits); } uint DistNumber=DecodeNumber(Inp,BlockTables.DD); - uint Distance=DDecode[DistNumber]+1; + uint Distance=(uint)(DDecode[DistNumber]+1); if ((Bits=DBits[DistNumber])>0) { if (DistNumber>9) { if (Bits>4) { - Distance+=((Inp.getbits()>>(20-Bits))<<4); + Distance+=((Inp.getbits()>>(int)(20-Bits))<<4); Inp.addbits(Bits-4); } if (LowDistRepCount>0) { LowDistRepCount--; - Distance+=PrevLowDist; + Distance+=(uint)PrevLowDist; } else { uint LowDist=DecodeNumber(Inp,BlockTables.LDD); if (LowDist==16) { - LowDistRepCount=LOW_DIST_REP_COUNT-1; - Distance+=PrevLowDist; + LowDistRepCount=(int)(LOW_DIST_REP_COUNT-1); + Distance+=(uint)PrevLowDist; } else { Distance+=LowDist; - PrevLowDist=LowDist; + PrevLowDist=(int)LowDist; } } } else { - Distance+=Inp.getbits()>>(16-Bits); + Distance+=Inp.getbits()>>(int)(16-Bits); Inp.addbits(Bits); } } @@ -255,19 +251,19 @@ void Unpack29(bool Solid) int Length=LDecode[LengthNumber]+2; if ((Bits=LBits[LengthNumber])>0) { - Length+=Inp.getbits()>>(16-Bits); + Length+=Inp.getbits()>>(int)(16-Bits); Inp.addbits(Bits); } - LastLength=Length; - CopyString(Length,Distance); + LastLength=(uint)Length; + CopyString((uint)Length,Distance); continue; } if (Number<272) { - uint Distance=SDDecode[Number-=263]+1; + uint Distance=(uint)(SDDecode[Number-=263]+1); if ((Bits=SDBits[Number])>0) { - Distance+=Inp.getbits()>>(16-Bits); + Distance+=Inp.getbits()>>(int)(16-Bits); Inp.addbits(Bits); } InsertOldDist(Distance); @@ -385,10 +381,11 @@ bool ReadVMCodePPM() } -bool AddVMCode(uint FirstByte,byte *Code,int CodeSize) +bool AddVMCode(uint FirstByte,byte[] Code,int CodeSize) { VMCodeInp.InitBitInput(); - memcpy(VMCodeInp.InBuf,Code,Min(BitInput.MAX_SIZE,CodeSize)); + //x memcpy(VMCodeInp.InBuf,Code,Min(BitInput.MAX_SIZE,CodeSize)); + Array.Copy(Code, 0, VMCodeInp.InBuf, 0, Math.Min(BitInput.MAX_SIZE,CodeSize)); VM.Init(); uint FiltPos; @@ -401,28 +398,28 @@ bool AddVMCode(uint FirstByte,byte *Code,int CodeSize) FiltPos--; } else - FiltPos=LastFilter; // Use the same filter as last time. + FiltPos=(uint)this.LastFilter; // Use the same filter as last time. if (FiltPos>Filters30.Count || FiltPos>OldFilterLengths.Count) return false; - LastFilter=FiltPos; + LastFilter=(int)FiltPos; bool NewFilter=(FiltPos==Filters30.Count); - UnpackFilter30 *StackFilter=new UnpackFilter30(); // New filter for PrgStack. + UnpackFilter30 StackFilter=new UnpackFilter30(); // New filter for PrgStack. - UnpackFilter30 *Filter; + UnpackFilter30 Filter; if (NewFilter) // New filter code, never used before since VM reset. { if (FiltPos>MAX3_UNPACK_FILTERS) { // Too many different filters, corrupt archive. - delete StackFilter; + //delete StackFilter; return false; } Filters30.Add(1); Filters30[Filters30.Count-1]=Filter=new UnpackFilter30(); - StackFilter->ParentFilter=(uint)(Filters30.Count-1); + StackFilter.ParentFilter=(uint)(Filters30.Count-1); // Reserve one item to store the data block length of our new filter // entry. We'll set it to real block length below, after reading it. @@ -433,11 +430,11 @@ bool AddVMCode(uint FirstByte,byte *Code,int CodeSize) else // Filter was used in the past. { Filter=Filters30[FiltPos]; - StackFilter->ParentFilter=FiltPos; + StackFilter.ParentFilter=FiltPos; } - uint EmptyCount=0; - for (uint I=0;IMAX3_UNPACK_FILTERS) { - delete StackFilter; + //delete StackFilter; return false; } PrgStack.Add(1); @@ -461,13 +458,13 @@ bool AddVMCode(uint FirstByte,byte *Code,int CodeSize) uint BlockStart=RarVM.ReadData(VMCodeInp); if ((FirstByte & 0x40)!=0) BlockStart+=258; - StackFilter->BlockStart=(uint)((BlockStart+UnpPtr)&MaxWinMask); + StackFilter.BlockStart=(uint)((BlockStart+UnpPtr)&MaxWinMask); if ((FirstByte & 0x20)!=0) { - StackFilter->BlockLength=RarVM.ReadData(VMCodeInp); + StackFilter.BlockLength=RarVM.ReadData(VMCodeInp); // Store the last data block length for current filter. - OldFilterLengths[FiltPos]=StackFilter->BlockLength; + OldFilterLengths[FiltPos]=StackFilter.BlockLength; } else { @@ -475,23 +472,23 @@ bool AddVMCode(uint FirstByte,byte *Code,int CodeSize) // for same filter. It is possible for corrupt data to access a new // and not filled yet item of OldFilterLengths array here. This is why // we set new OldFilterLengths items to zero above. - StackFilter->BlockLength=FiltPosNextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=BlockStart; + StackFilter.NextWindow=WrPtr!=UnpPtr && ((WrPtr-UnpPtr)&MaxWinMask)<=BlockStart; // DebugLog("\nNextWindow: UnpPtr=%08x WrPtr=%08x BlockStart=%08x",UnpPtr,WrPtr,BlockStart); - memset(StackFilter->Prg.InitR,0,sizeof(StackFilter->Prg.InitR)); - StackFilter->Prg.InitR[4]=StackFilter->BlockLength; + memset(StackFilter.Prg.InitR,0,sizeof(StackFilter.Prg.InitR)); + StackFilter.Prg.InitR[4]=StackFilter.BlockLength; if ((FirstByte & 0x10)!=0) // Set registers to optional parameters if any. { uint InitMask=VMCodeInp.fgetbits()>>9; VMCodeInp.faddbits(7); - for (uint I=0;I<7;I++) - if (InitMask & (1<Prg.InitR[I]=RarVM.ReadData(VMCodeInp); + for (int I=0;I<7;I++) + if ((InitMask & (1<Prg); } - StackFilter->Prg.Type=Filter->Prg.Type; + StackFilter.Prg.Type=Filter.Prg.Type; return true; } @@ -535,7 +532,7 @@ bool UnpReadBuf30() } else DataSize=ReadTop; - int ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput.MAX_SIZE-DataSize); + int ReadCode=UnpIO_UnpRead(Inp.InBuf,DataSize,BitInput.MAX_SIZE-DataSize); if (ReadCode>0) ReadTop+=ReadCode; ReadBorder=ReadTop-30; @@ -555,16 +552,16 @@ void UnpWriteBuf30() // these data can be used for future string matches, so we must // preserve them in original form. - UnpackFilter30 *flt=PrgStack[I]; + UnpackFilter30 flt=PrgStack[I]; if (flt==null) continue; - if (flt->NextWindow) + if (flt.NextWindow) { - flt->NextWindow=false; + flt.NextWindow=false; continue; } - uint BlockStart=flt->BlockStart; - uint BlockLength=flt->BlockLength; + uint BlockStart=flt.BlockStart; + uint BlockLength=flt.BlockLength; if (((BlockStart-WrittenBorder)&MaxWinMask)FilteredData; - uint FilteredDataSize=Prg->FilteredDataSize; + byte[] FilteredData=Prg.FilteredData; + uint FilteredDataSize=Prg.FilteredDataSize; delete PrgStack[I]; PrgStack[I]=null; while (I+1BlockStart!=BlockStart || - NextFilter->BlockLength!=FilteredDataSize || NextFilter->NextWindow) + if (NextFilter==null || NextFilter.BlockStart!=BlockStart || + NextFilter.BlockLength!=FilteredDataSize || NextFilter.NextWindow) break; // Apply several filters to same data block. VM.SetMemory(0,FilteredData,FilteredDataSize); - VM_PreparedProgram *ParentPrg=&Filters30[NextFilter->ParentFilter]->Prg; + VM_PreparedProgram *ParentPrg=&Filters30[NextFilter.ParentFilter]->Prg; VM_PreparedProgram *NextPrg=&NextFilter->Prg; ExecuteCode(NextPrg); - FilteredData=NextPrg->FilteredData; - FilteredDataSize=NextPrg->FilteredDataSize; + FilteredData=NextPrg.FilteredData; + FilteredDataSize=NextPrg.FilteredDataSize; I++; delete PrgStack[I]; PrgStack[I]=null; } - UnpIO->UnpWrite(FilteredData,FilteredDataSize); + UnpIO_UnpWrite(FilteredData,0,FilteredDataSize); UnpSomeRead=true; WrittenFileSize+=FilteredDataSize; WrittenBorder=BlockEnd; @@ -630,9 +627,9 @@ void UnpWriteBuf30() // the window border to process this filter next time, not now. for (size_t J=I;JNextWindow) - flt->NextWindow=false; + UnpackFilter30 flt=PrgStack[J]; + if (flt!=null && flt.NextWindow) + flt.NextWindow=false; } WrPtr=WrittenBorder; return; @@ -654,12 +651,12 @@ void ExecuteCode(VM_PreparedProgram *Prg) bool ReadTables30() { - byte BitLength[BC]; - byte Table[HUFF_TABLE_SIZE30]; + byte[] BitLength = new byte[BC]; + byte[] Table = new byte[HUFF_TABLE_SIZE30]; if (Inp.InAddr>ReadTop-25) if (!UnpReadBuf30()) return(false); - Inp.faddbits((8-Inp.InBit)&7); + Inp.faddbits((uint)((8-Inp.InBit)&7)); uint BitField=Inp.fgetbits(); if ((BitField & 0x8000) != 0) { @@ -694,9 +691,9 @@ bool ReadTables30() } } else - BitLength[I]=Length; + BitLength[I]=(byte)Length; } - MakeDecodeTables(BitLength,BlockTables.BD,BC30); + MakeDecodeTables(BitLength,0,BlockTables.BD,BC30); const uint TableSize=HUFF_TABLE_SIZE30; for (uint I=0;IReadTop) return false; - MakeDecodeTables(&Table[0],&BlockTables.LD,NC30); - MakeDecodeTables(&Table[NC30],&BlockTables.DD,DC30); - MakeDecodeTables(&Table[NC30+DC30],&BlockTables.LDD,LDC30); - MakeDecodeTables(&Table[NC30+DC30+LDC30],&BlockTables.RD,RC30); - memcpy(UnpOldTable,Table,sizeof(UnpOldTable)); + MakeDecodeTables(Table,0,BlockTables.LD,NC30); + MakeDecodeTables(Table,(int)NC30,BlockTables.DD,DC30); + MakeDecodeTables(Table,(int)(NC30+DC30),BlockTables.LDD,LDC30); + MakeDecodeTables(Table,(int)(NC30+DC30+LDC30),BlockTables.RD,RC30); + //x memcpy(UnpOldTable,Table,sizeof(UnpOldTable)); + Array.Copy(Table,0,UnpOldTable,0,UnpOldTable.Length); return true; } From 3c29122dfeb51d6ccb7133db67c692c415f3d910 Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 19:04:41 -0500 Subject: [PATCH 072/124] rar5: porting old algos --- .../Rar/UnpackV2017/Unpack.unpack15_cpp.cs | 64 ++++++------- .../Rar/UnpackV2017/Unpack.unpack20_cpp.cs | 89 +++++++++---------- .../Rar/UnpackV2017/Unpack.unpack30_cpp.cs | 14 +-- 3 files changed, 78 insertions(+), 89 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs index 9889e8e8..87830e02 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs @@ -1,15 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; +using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack15Local; namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { -#if !RarV2017_RAR5ONLY +#if true || !RarV2017_RAR5ONLY const int STARTL1 =2; static uint[] DecL1={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00, @@ -129,13 +124,14 @@ uint GetShortLen1(uint pos) { return ((pos)==1 ? (uint)(this.Buf60+3):ShortLen1[ //#define GetShortLen2(pos) ((pos)==3 ? Buf60+3:ShortLen2[pos]) uint GetShortLen2(uint pos) { return ((pos)==3 ? (uint)(this.Buf60+3):ShortLen2[pos]); } - static uint[] ShortLen1={1,3,4,4,5,6,7,8,8,4,4,5,6,6,4,0}; - static uint[] ShortXor1={0,0xa0,0xd0,0xe0,0xf0,0xf8,0xfc,0xfe, +internal static class Unpack15Local { + public static readonly uint[] ShortLen1={1,3,4,4,5,6,7,8,8,4,4,5,6,6,4,0}; + public static readonly uint[] ShortXor1={0,0xa0,0xd0,0xe0,0xf0,0xf8,0xfc,0xfe, 0xff,0xc0,0x80,0x90,0x98,0x9c,0xb0}; - static uint[] ShortLen2={2,3,3,3,4,4,5,6,6,4,4,5,6,6,4,0}; - static uint[] ShortXor2={0,0x40,0x60,0xa0,0xd0,0xe0,0xf0,0xf8, + public static readonly uint[] ShortLen2={2,3,3,3,4,4,5,6,6,4,4,5,6,6,4,0}; + public static readonly uint[] ShortXor2={0,0x40,0x60,0xa0,0xd0,0xe0,0xf0,0xf8, 0xfc,0xc0,0x80,0x90,0x98,0x9c,0xb0}; - +} void ShortLZ() { @@ -166,14 +162,14 @@ void ShortLZ() if (AvrLn1<37) { for (Length=0;;Length++) - if (((BitField^ShortXor1[Length]) & (~(0xff>>GetShortLen1(Length))))==0) + if (((BitField^ShortXor1[Length]) & (~(0xff>>(int)GetShortLen1(Length))))==0) break; Inp.faddbits(GetShortLen1(Length)); } else { for (Length=0;;Length++) - if (((BitField^ShortXor2[Length]) & (~(0xff>>GetShortLen2(Length))))==0) + if (((BitField^ShortXor2[Length]) & (~(0xff>>(int)GetShortLen2(Length))))==0) break; Inp.faddbits(GetShortLen2(Length)); } @@ -224,13 +220,13 @@ void ShortLZ() AvrLn1 += Length; AvrLn1 -= AvrLn1 >> 4; - DistancePlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff; + DistancePlace=(int)(DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2) & 0xff); Distance=ChSetA[DistancePlace]; if (--DistancePlace != -1) { LastDistance=ChSetA[DistancePlace]; - ChSetA[DistancePlace+1]=LastDistance; - ChSetA[DistancePlace]=Distance; + ChSetA[DistancePlace+1]=(ushort)LastDistance; + ChSetA[DistancePlace]=(ushort)Distance; } Length+=2; OldDist[OldDistPtr++] = ++Distance; @@ -271,7 +267,7 @@ void LongLZ() } else { - for (Length=0;((BitField<> 8; - while (1) + while (true) { Distance = ChSetB[DistancePlace & 0xff]; NewDistancePlace = NToPlB[Distance++ & 0xff]++; - if (!(Distance & 0xff)) + if ((Distance & 0xff) != 0) CorrHuff(ChSetB,NToPlB); else break; } ChSetB[DistancePlace & 0xff]=ChSetB[NewDistancePlace]; - ChSetB[NewDistancePlace]=Distance; + ChSetB[NewDistancePlace]=(ushort)Distance; Distance=((Distance & 0xff00) | (Inp.fgetbits() >> 8)) >> 1; Inp.faddbits(7); @@ -343,18 +339,18 @@ void HuffDecode() uint BitField=Inp.fgetbits(); if (AvrPlc > 0x75ff) - BytePlace=DecodeNum(BitField,STARTHF4,DecHf4,PosHf4); + BytePlace=(int)DecodeNum(BitField,STARTHF4,DecHf4,PosHf4); else if (AvrPlc > 0x5dff) - BytePlace=DecodeNum(BitField,STARTHF3,DecHf3,PosHf3); + BytePlace=(int)DecodeNum(BitField,STARTHF3,DecHf3,PosHf3); else if (AvrPlc > 0x35ff) - BytePlace=DecodeNum(BitField,STARTHF2,DecHf2,PosHf2); + BytePlace=(int)DecodeNum(BitField,STARTHF2,DecHf2,PosHf2); else if (AvrPlc > 0x0dff) - BytePlace=DecodeNum(BitField,STARTHF1,DecHf1,PosHf1); + BytePlace=(int)DecodeNum(BitField,STARTHF1,DecHf1,PosHf1); else - BytePlace=DecodeNum(BitField,STARTHF0,DecHf0,PosHf0); + BytePlace=(int)DecodeNum(BitField,STARTHF0,DecHf0,PosHf0); BytePlace&=0xff; if (StMode != 0) { @@ -384,7 +380,7 @@ void HuffDecode() else if (NumHuf++ >= 16 && FlagsCnt==0) StMode=1; - AvrPlc += BytePlace; + AvrPlc += (uint)BytePlace; AvrPlc -= AvrPlc >> 8; Nhfb+=16; if (Nhfb > 0xff) @@ -396,7 +392,7 @@ void HuffDecode() Window[UnpPtr++]=(byte)(ChSet[BytePlace]>>8); --DestUnpSize; - while (1) + while (true) { CurByte=ChSet[BytePlace]; NewBytePlace=NToPl[CurByte++ & 0xff]++; @@ -407,7 +403,7 @@ void HuffDecode() } ChSet[BytePlace]=ChSet[NewBytePlace]; - ChSet[NewBytePlace]=CurByte; + ChSet[NewBytePlace]=(ushort)CurByte; } @@ -421,7 +417,7 @@ void GetFlagsBuf() // While normally we do not use the last item to code the flags byte here, // we need to check for value 256 when unpacking in case we unpack // a corrupt archive. - if (FlagsPlace>=sizeof(ChSetC)/sizeof(ChSetC[0])) + if (FlagsPlace>=ChSetC.Length) return; while (true) @@ -435,7 +431,7 @@ void GetFlagsBuf() } ChSetC[FlagsPlace]=ChSetC[NewFlagsPlace]; - ChSetC[NewFlagsPlace]=Flags; + ChSetC[NewFlagsPlace]=(ushort)Flags; } #endif @@ -482,7 +478,6 @@ void CorrHuff(ushort[] CharSet,byte[] NumToPlace) NumToPlace[I]=(byte)((7-I)*32); } -#if !RarV2017_RAR5ONLY void CopyString15(uint Distance,uint Length) { @@ -495,15 +490,14 @@ void CopyString15(uint Distance,uint Length) } -uint DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab) +uint DecodeNum(uint Num,uint StartPos,uint[] DecTab,uint[] PosTab) { int I; for (Num&=0xfff0,I=0;DecTab[I]<=Num;I++) StartPos++; Inp.faddbits(StartPos); - return(((Num-(I ? DecTab[I-1]:0))>>(16-StartPos))+PosTab[StartPos]); + return(((Num-(I != 0 ? DecTab[I-1]:0))>>(int)(16-StartPos))+PosTab[StartPos]); } -#endif } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs index 40c5f055..a42b55d5 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs @@ -10,19 +10,15 @@ using size_t = System.UInt64; using int64 = System.Int64; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; -//using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack20Local; +using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack20Local; namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { -#if !RarV2017_RAR5ONLY +#if true || !RarV2017_RAR5ONLY void CopyString20(uint Length,uint Distance) { LastDist=OldDist[OldDistPtr++ & 3]=Distance; @@ -33,12 +29,12 @@ void CopyString20(uint Length,uint Distance) internal static class Unpack20Local { - public static byte[] LDecode={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; - public static byte[] LBits= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; - public static uint[] DDecode={0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040}; - public static byte[] DBits= {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; - public static byte[] SDDecode={0,4,8,16,32,64,128,192}; - public static byte[] SDBits= {2,2,3, 4, 5, 6, 6, 6}; + public static readonly byte[] LDecode={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; + public static readonly byte[] LBits= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; + public static readonly uint[] DDecode={0,1,2,3,4,6,8,12,16,24,32,48,64,96,128,192,256,384,512,768,1024,1536,2048,3072,4096,6144,8192,12288,16384,24576,32768U,49152U,65536,98304,131072,196608,262144,327680,393216,458752,524288,589824,655360,720896,786432,851968,917504,983040}; + public static readonly byte[] DBits= {0,0,0,0,1,1,2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}; + public static readonly byte[] SDDecode={0,4,8,16,32,64,128,192}; + public static readonly byte[] SDBits= {2,2,3, 4, 5, 6, 6, 6}; } void Unpack20(bool Solid) { @@ -95,10 +91,10 @@ void Unpack20(bool Solid) } if (Number>269) { - uint Length=LDecode[Number-=270]+3; + uint Length=(uint)(LDecode[Number-=270]+3); if ((Bits=LBits[Number])>0) { - Length+=Inp.getbits()>>(16-Bits); + Length+=Inp.getbits()>>(int)(16-Bits); Inp.addbits(Bits); } @@ -106,7 +102,7 @@ void Unpack20(bool Solid) uint Distance=DDecode[DistNumber]+1; if ((Bits=DBits[DistNumber])>0) { - Distance+=Inp.getbits()>>(16-Bits); + Distance+=Inp.getbits()>>(int)(16-Bits); Inp.addbits(Bits); } @@ -135,10 +131,10 @@ void Unpack20(bool Solid) { uint Distance=OldDist[(OldDistPtr-(Number-256)) & 3]; uint LengthNumber=DecodeNumber(Inp,BlockTables.RD); - uint Length=LDecode[LengthNumber]+2; + uint Length=(uint)(LDecode[LengthNumber]+2); if ((Bits=LBits[LengthNumber])>0) { - Length+=Inp.getbits()>>(16-Bits); + Length+=Inp.getbits()>>(int)(16-Bits); Inp.addbits(Bits); } if (Distance>=0x101) @@ -156,10 +152,10 @@ void Unpack20(bool Solid) } if (Number<270) { - uint Distance=SDDecode[Number-=261]+1; + uint Distance=(uint)(SDDecode[Number-=261]+1); if ((Bits=SDBits[Number])>0) { - Distance+=Inp.getbits()>>(16-Bits); + Distance+=Inp.getbits()>>(int)(16-Bits); Inp.addbits(Bits); } CopyString20(2,Distance); @@ -177,20 +173,20 @@ void UnpWriteBuf20() UnpSomeRead=true; if (UnpPtrUnpWrite(&Window[WrPtr],-(int)WrPtr & MaxWinMask); - UnpIO->UnpWrite(Window,UnpPtr); + UnpIO_UnpWrite(Window, WrPtr,(uint)(-(int)this.WrPtr & this.MaxWinMask)); + UnpIO_UnpWrite(Window,0,UnpPtr); UnpAllBuf=true; } else - UnpIO->UnpWrite(&Window[WrPtr],UnpPtr-WrPtr); + UnpIO_UnpWrite(Window,WrPtr,UnpPtr-WrPtr); WrPtr=UnpPtr; } bool ReadTables20() { - byte BitLength[BC20]; - byte Table[MC20*4]; + byte[] BitLength = new byte[BC20]; + byte[] Table = new byte[MC20*4]; if (Inp.InAddr>ReadTop-25) if (!UnpReadBuf()) return false; @@ -218,7 +214,7 @@ bool ReadTables20() BitLength[I]=(byte)(Inp.getbits() >> 12); Inp.addbits(4); } - MakeDecodeTables(BitLength,BlockTables.BD,BC20); + MakeDecodeTables(BitLength,0,BlockTables.BD,BC20); for (uint I=0;IReadTop-5) @@ -227,7 +223,7 @@ bool ReadTables20() uint Number=DecodeNumber(Inp,BlockTables.BD); if (Number<16) { - Table[I]=(Number+UnpOldTable20[I]) & 0xf; + Table[I]=(byte)((Number+this.UnpOldTable20[I]) & 0xf); I++; } else @@ -266,14 +262,15 @@ bool ReadTables20() return true; if (UnpAudioBlock) for (uint I=0;I>3) & 0xFF; - uint Ch=PCh-Delta; + uint Ch=(uint)(PCh-Delta); int D=(sbyte)Delta; // Left shift of negative value is undefined behavior in C++, // so we cast it to unsigned to follow the standard. - D=(uint)D<<3; + D=(int)((uint)D<<3); - V.Dif[0]+=Math.Abs(D); - V.Dif[1]+=Math.Abs(D-V.D1); - V.Dif[2]+=Math.Abs(D+V.D1); - V.Dif[3]+=Math.Abs(D-V.D2); - V.Dif[4]+=Math.Abs(D+V.D2); - V.Dif[5]+=Math.Abs(D-V.D3); - V.Dif[6]+=Math.Abs(D+V.D3); - V.Dif[7]+=Math.Abs(D-V.D4); - V.Dif[8]+=Math.Abs(D+V.D4); - V.Dif[9]+=Math.Abs(D-UnpChannelDelta); - V.Dif[10]+=Math.Abs(D+UnpChannelDelta); + V.Dif[0]+=(uint)Math.Abs(D); + V.Dif[1]+=(uint)Math.Abs(D-V.D1); + V.Dif[2]+=(uint)Math.Abs(D+V.D1); + V.Dif[3]+=(uint)Math.Abs(D-V.D2); + V.Dif[4]+=(uint)Math.Abs(D+V.D2); + V.Dif[5]+=(uint)Math.Abs(D-V.D3); + V.Dif[6]+=(uint)Math.Abs(D+V.D3); + V.Dif[7]+=(uint)Math.Abs(D-V.D4); + V.Dif[8]+=(uint)Math.Abs(D+V.D4); + V.Dif[9]+=(uint)Math.Abs(D-UnpChannelDelta); + V.Dif[10]+=(uint)Math.Abs(D+UnpChannelDelta); UnpChannelDelta=V.LastDelta=(sbyte)(Ch-V.LastChar); - V.LastChar=Ch; + V.LastChar=(int)Ch; if ((V.ByteCount & 0x1F)==0) { @@ -405,7 +401,6 @@ byte DecodeAudio(int Delta) } return (byte)Ch; } -#endif } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs index ac1be05f..de7319ff 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs @@ -35,13 +35,13 @@ int SafePPMDecodeChar() } internal static class Unpack30Local { - public static byte[] LDecode={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; - public static byte[] LBits= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; - public static int[] DDecode = new int[DC]; - public static byte[] DBits = new byte[DC]; - public static int[] DBitLengthCounts= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12}; - public static byte[] SDDecode={0,4,8,16,32,64,128,192}; - public static byte[] SDBits= {2,2,3, 4, 5, 6, 6, 6}; + public static readonly byte[] LDecode={0,1,2,3,4,5,6,7,8,10,12,14,16,20,24,28,32,40,48,56,64,80,96,112,128,160,192,224}; + public static readonly byte[] LBits= {0,0,0,0,0,0,0,0,1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5}; + public static readonly int[] DDecode = new int[DC]; + public static readonly byte[] DBits = new byte[DC]; + public static readonly int[] DBitLengthCounts= {4,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,14,0,12}; + public static readonly byte[] SDDecode={0,4,8,16,32,64,128,192}; + public static readonly byte[] SDBits= {2,2,3, 4, 5, 6, 6, 6}; } void Unpack29(bool Solid) { From 554153e6a0de1ee7f7ec385ae44c50589ddaac66 Mon Sep 17 00:00:00 2001 From: coderb Date: Wed, 20 Dec 2017 19:10:06 -0500 Subject: [PATCH 073/124] rar5: port old algos --- .../Rar/UnpackV2017/Unpack.unpack15_cpp.cs | 2 -- .../Rar/UnpackV2017/Unpack.unpack20_cpp.cs | 2 -- .../Rar/UnpackV2017/Unpack.unpack30_cpp.cs | 23 ++++++++++--------- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 2 +- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs index 87830e02..d76ee10c 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs @@ -4,7 +4,6 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { -#if true || !RarV2017_RAR5ONLY const int STARTL1 =2; static uint[] DecL1={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00, @@ -434,7 +433,6 @@ void GetFlagsBuf() ChSetC[NewFlagsPlace]=(ushort)Flags; } -#endif void UnpInitData15(bool Solid) { if (!Solid) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs index a42b55d5..57ecdc02 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs @@ -18,7 +18,6 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 internal partial class Unpack { -#if true || !RarV2017_RAR5ONLY void CopyString20(uint Length,uint Distance) { LastDist=OldDist[OldDistPtr++ & 3]=Distance; @@ -288,7 +287,6 @@ void ReadLastTables() ReadTables20(); } -#endif void UnpInitData20(bool Solid) { diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs index de7319ff..8ec81763 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs @@ -12,14 +12,14 @@ using int64 = System.Int64; using System; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; -using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack30Local; +//using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack30Local; namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { -#if true || !RarV2017_RAR5ONLY +#if !RarV2017_RAR5ONLY // We use it instead of direct PPM.DecodeChar call to be sure that // we reset PPM structures in case of corrupt data. It is important, // because these structures can be invalid after PPM.DecodeChar returned -1. @@ -251,7 +251,7 @@ void Unpack29(bool Solid) int Length=LDecode[LengthNumber]+2; if ((Bits=LBits[LengthNumber])>0) { - Length+=Inp.getbits()>>(int)(16-Bits); + Length+=(int)(Inp.getbits()>>(int)(16-Bits)); Inp.addbits(Bits); } LastLength=(uint)Length; @@ -345,7 +345,7 @@ bool ReadVMCode() bool ReadVMCodePPM() { - uint FirstByte=SafePPMDecodeChar(); + uint FirstByte=(uint)SafePPMDecodeChar(); if ((int)FirstByte==-1) return false; uint Length=(FirstByte & 7)+1; @@ -429,7 +429,7 @@ bool AddVMCode(uint FirstByte,byte[] Code,int CodeSize) } else // Filter was used in the past. { - Filter=Filters30[FiltPos]; + Filter=Filters30[(int)FiltPos]; StackFilter.ParentFilter=FiltPos; } @@ -452,8 +452,8 @@ bool AddVMCode(uint FirstByte,byte[] Code,int CodeSize) PrgStack.Add(1); EmptyCount=1; } - size_t StackPos=PrgStack.Count-EmptyCount; - PrgStack[StackPos]=StackFilter; + size_t StackPos=(uint)(this.PrgStack.Count-EmptyCount); + PrgStack[(int)StackPos]=StackFilter; uint BlockStart=RarVM.ReadData(VMCodeInp); if ((FirstByte & 0x40)!=0) @@ -464,7 +464,7 @@ bool AddVMCode(uint FirstByte,byte[] Code,int CodeSize) StackFilter.BlockLength=RarVM.ReadData(VMCodeInp); // Store the last data block length for current filter. - OldFilterLengths[FiltPos]=StackFilter.BlockLength; + OldFilterLengths[(int)FiltPos]=(int)StackFilter.BlockLength; } else { @@ -472,7 +472,7 @@ bool AddVMCode(uint FirstByte,byte[] Code,int CodeSize) // for same filter. It is possible for corrupt data to access a new // and not filled yet item of OldFilterLengths array here. This is why // we set new OldFilterLengths items to zero above. - StackFilter.BlockLength=FiltPos0) - memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + //x memmove(Inp.InBuf,Inp.InBuf+Inp.InAddr,DataSize); + Array.Copy(Inp.InBuf,Inp.InAddr,Inp.InBuf,0,DataSize); Inp.InAddr=0; ReadTop=DataSize; } @@ -544,7 +545,7 @@ void UnpWriteBuf30() { uint WrittenBorder=(uint)WrPtr; uint WriteSize=(uint)((UnpPtr-WrittenBorder)&MaxWinMask); - for (size_t I=0;I Date: Wed, 3 Jan 2018 00:23:34 +0900 Subject: [PATCH 074/124] Utility.Skip uses seek --- src/SharpCompress/Utility.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 751dd29d..ae6587f6 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -141,6 +141,12 @@ namespace SharpCompress public static void Skip(this Stream source, long advanceAmount) { + if (source.CanSeek) + { + source.Position += advanceAmount; + return; + } + byte[] buffer = GetTransferByteArray(); try { From 9092ecf33128925f6e66f3a7b91641de435a45cb Mon Sep 17 00:00:00 2001 From: Dmitry Nesterov <4ybaka@gmail.com> Date: Thu, 4 Jan 2018 22:57:32 +0100 Subject: [PATCH 075/124] Added ability to leave tar archive open after stream is closed --- src/SharpCompress/Writers/Tar/TarWriter.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/Writers/Tar/TarWriter.cs b/src/SharpCompress/Writers/Tar/TarWriter.cs index db7bc97a..c4036b1b 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.cs @@ -11,9 +11,13 @@ namespace SharpCompress.Writers.Tar { public class TarWriter : AbstractWriter { - public TarWriter(Stream destination, WriterOptions options) + private readonly bool finalizeArchiveOnClose; + + public TarWriter(Stream destination, WriterOptions options, bool finalizeArchiveOnClose = true) : base(ArchiveType.Tar, options) { + this.finalizeArchiveOnClose = finalizeArchiveOnClose; + if (!destination.CanWrite) { throw new ArgumentException("Tars require writable streams."); @@ -97,8 +101,10 @@ namespace SharpCompress.Writers.Tar { if (isDisposing) { - PadTo512(0, true); - PadTo512(0, true); + if (finalizeArchiveOnClose){ + PadTo512(0, true); + PadTo512(0, true); + } switch (OutputStream) { case BZip2Stream b: From 8f7ea420b332c426cc6cedeea7f9a28fa5707746 Mon Sep 17 00:00:00 2001 From: Dmitry Nesterov <4ybaka@gmail.com> Date: Sat, 13 Jan 2018 00:41:35 +0100 Subject: [PATCH 076/124] Revert "Added ability to leave tar archive open after stream is closed" This reverts commit 9092ecf33128925f6e66f3a7b91641de435a45cb. --- src/SharpCompress/Writers/Tar/TarWriter.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/SharpCompress/Writers/Tar/TarWriter.cs b/src/SharpCompress/Writers/Tar/TarWriter.cs index c4036b1b..db7bc97a 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.cs @@ -11,13 +11,9 @@ namespace SharpCompress.Writers.Tar { public class TarWriter : AbstractWriter { - private readonly bool finalizeArchiveOnClose; - - public TarWriter(Stream destination, WriterOptions options, bool finalizeArchiveOnClose = true) + public TarWriter(Stream destination, WriterOptions options) : base(ArchiveType.Tar, options) { - this.finalizeArchiveOnClose = finalizeArchiveOnClose; - if (!destination.CanWrite) { throw new ArgumentException("Tars require writable streams."); @@ -101,10 +97,8 @@ namespace SharpCompress.Writers.Tar { if (isDisposing) { - if (finalizeArchiveOnClose){ - PadTo512(0, true); - PadTo512(0, true); - } + PadTo512(0, true); + PadTo512(0, true); switch (OutputStream) { case BZip2Stream b: From f85fd1f6a4754ffd7ecc3a823683a1ac71cb9016 Mon Sep 17 00:00:00 2001 From: Dmitry Nesterov <4ybaka@gmail.com> Date: Sat, 13 Jan 2018 00:44:42 +0100 Subject: [PATCH 077/124] Added ability to leave tar archive open after stream is closed --- src/SharpCompress/Archives/Tar/TarArchive.cs | 2 +- src/SharpCompress/Writers/Tar/TarWriter.cs | 12 +++++++--- .../Writers/Tar/TarWriterOptions.cs | 23 +++++++++++++++++++ src/SharpCompress/Writers/WriterFactory.cs | 2 +- .../SharpCompress.Test/Tar/TarWriterTests.cs | 21 ++++++++++++++++- 5 files changed, 54 insertions(+), 6 deletions(-) create mode 100755 src/SharpCompress/Writers/Tar/TarWriterOptions.cs diff --git a/src/SharpCompress/Archives/Tar/TarArchive.cs b/src/SharpCompress/Archives/Tar/TarArchive.cs index 35d7fd15..961c1f6e 100644 --- a/src/SharpCompress/Archives/Tar/TarArchive.cs +++ b/src/SharpCompress/Archives/Tar/TarArchive.cs @@ -182,7 +182,7 @@ namespace SharpCompress.Archives.Tar IEnumerable oldEntries, IEnumerable newEntries) { - using (var writer = new TarWriter(stream, options)) + using (var writer = new TarWriter(stream, new TarWriterOptions(options))) { foreach (var entry in oldEntries.Concat(newEntries) .Where(x => !x.IsDirectory)) diff --git a/src/SharpCompress/Writers/Tar/TarWriter.cs b/src/SharpCompress/Writers/Tar/TarWriter.cs index db7bc97a..659c60e1 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.cs @@ -11,9 +11,13 @@ namespace SharpCompress.Writers.Tar { public class TarWriter : AbstractWriter { - public TarWriter(Stream destination, WriterOptions options) + private bool finalizeArchiveOnClose; + + public TarWriter(Stream destination, TarWriterOptions options) : base(ArchiveType.Tar, options) { + finalizeArchiveOnClose = options.FinalizeArchiveOnClose; + if (!destination.CanWrite) { throw new ArgumentException("Tars require writable streams."); @@ -97,8 +101,10 @@ namespace SharpCompress.Writers.Tar { if (isDisposing) { - PadTo512(0, true); - PadTo512(0, true); + if (finalizeArchiveOnClose) { + PadTo512(0, true); + PadTo512(0, true); + } switch (OutputStream) { case BZip2Stream b: diff --git a/src/SharpCompress/Writers/Tar/TarWriterOptions.cs b/src/SharpCompress/Writers/Tar/TarWriterOptions.cs new file mode 100755 index 00000000..9174b23f --- /dev/null +++ b/src/SharpCompress/Writers/Tar/TarWriterOptions.cs @@ -0,0 +1,23 @@ +using SharpCompress.Archives; +using SharpCompress.Common; + +namespace SharpCompress.Writers.Tar +{ + public class TarWriterOptions : WriterOptions + { + /// + /// Indicates if archive should be finalized (by 2 empty blocks) on close. + /// + public bool FinalizeArchiveOnClose { get; } + + public TarWriterOptions(CompressionType compressionType, bool finalizeArchiveOnClose) + : base(compressionType) + { + FinalizeArchiveOnClose = finalizeArchiveOnClose; + } + + internal TarWriterOptions(WriterOptions options) : this(options.CompressionType, true) + { + } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Writers/WriterFactory.cs b/src/SharpCompress/Writers/WriterFactory.cs index b623ee63..1f1c01a5 100644 --- a/src/SharpCompress/Writers/WriterFactory.cs +++ b/src/SharpCompress/Writers/WriterFactory.cs @@ -27,7 +27,7 @@ namespace SharpCompress.Writers } case ArchiveType.Tar: { - return new TarWriter(stream, writerOptions); + return new TarWriter(stream, new TarWriterOptions(writerOptions)); } default: { diff --git a/tests/SharpCompress.Test/Tar/TarWriterTests.cs b/tests/SharpCompress.Test/Tar/TarWriterTests.cs index faeabd22..4b0b2168 100644 --- a/tests/SharpCompress.Test/Tar/TarWriterTests.cs +++ b/tests/SharpCompress.Test/Tar/TarWriterTests.cs @@ -1,4 +1,6 @@ -using SharpCompress.Common; +using System.IO; +using SharpCompress.Common; +using SharpCompress.Writers.Tar; using Xunit; namespace SharpCompress.Test.Tar @@ -34,5 +36,22 @@ namespace SharpCompress.Test.Tar { Assert.Throws(() => Write(CompressionType.Rar, "Zip.ppmd.noEmptyDirs.zip", "Zip.ppmd.noEmptyDirs.zip")); } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Tar_Finalize_Archive(bool finalizeArchive) + { + using (MemoryStream stream = new MemoryStream()) + using (Stream content = File.OpenRead(Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg"))) { + using (TarWriter writer = new TarWriter(stream, new TarWriterOptions(CompressionType.None, finalizeArchive))) { + writer.Write("doesn't matter", content, null); + } + + var paddedContentWithHeader = content.Length / 512 * 512 + 512 + 512; + var expectedStreamLength = finalizeArchive ? paddedContentWithHeader + 512 * 2 : paddedContentWithHeader; + Assert.Equal(expectedStreamLength, stream.Length); + } + } } } From a62f4df0b1044f4cd8ed69a5627541d91faa61e7 Mon Sep 17 00:00:00 2001 From: Frederik Carlier Date: Fri, 16 Feb 2018 14:43:23 +0100 Subject: [PATCH 078/124] Implement entry.ToString(), let it return entry.Key (#351) --- src/SharpCompress/Common/Entry.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/SharpCompress/Common/Entry.cs b/src/SharpCompress/Common/Entry.cs index 5f07af2f..409a8a2a 100644 --- a/src/SharpCompress/Common/Entry.cs +++ b/src/SharpCompress/Common/Entry.cs @@ -65,6 +65,12 @@ namespace SharpCompress.Common /// public abstract bool IsSplit { get; } + /// + public override string ToString() + { + return this.Key; + } + internal abstract IEnumerable Parts { get; } internal bool IsSolid { get; set; } From 5481609554662fc9f0a75286cc51520f62a266fc Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Tue, 27 Feb 2018 08:48:45 +0000 Subject: [PATCH 079/124] Build with new cake --- .circleci/config.yml | 10 +++++++--- build.sh | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8cd5464c..a1fa617c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,10 +2,14 @@ version: 2 jobs: build: docker: - - image: adamhathcock/cake-build:latest + - image: microsoft/dotnet:2.0.5-sdk-2.1.4 steps: - checkout + - run: + name: Install unzip + command: | + apt-get update + apt-get install -y unzip - run: name: Build - command: ./build.sh - \ No newline at end of file + command: ./build.sh \ No newline at end of file diff --git a/build.sh b/build.sh index 90111e63..71dd07dc 100755 --- a/build.sh +++ b/build.sh @@ -8,7 +8,7 @@ # Define directories. SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) TOOLS_DIR=$SCRIPT_DIR/tools -CAKE_VERSION=0.23.0 +CAKE_VERSION=0.26.0 CAKE_DLL=$TOOLS_DIR/Cake.CoreCLR.$CAKE_VERSION/Cake.dll # Make sure the tools folder exist. From 6e2c7d28573549d243b860625bf1c2665c080f0b Mon Sep 17 00:00:00 2001 From: frabar666 Date: Mon, 26 Feb 2018 23:49:09 +0100 Subject: [PATCH 080/124] support Deflate64 decompression --- FORMATS.md | 5 +- src/SharpCompress/Common/CompressionType.cs | 3 +- src/SharpCompress/Common/Zip/ZipEntry.cs | 4 + src/SharpCompress/Common/Zip/ZipFilePart.cs | 5 + .../Compressors/Deflate64/BlockType.cs | 13 + .../Compressors/Deflate64/Deflate64Stream.cs | 257 ++++++ .../Compressors/Deflate64/DeflateInput.cs | 43 + .../Deflate64/FastEncoderStatus.cs | 245 ++++++ .../Compressors/Deflate64/HuffmanTree.cs | 311 ++++++++ .../Compressors/Deflate64/InflaterManaged.cs | 738 ++++++++++++++++++ .../Compressors/Deflate64/InflaterState.cs | 42 + .../Compressors/Deflate64/InputBuffer.cs | 202 +++++ .../Compressors/Deflate64/Match.cs | 17 + .../Compressors/Deflate64/MatchState.cs | 13 + .../Compressors/Deflate64/OutputWindow.cs | 151 ++++ tests/SharpCompress.Test/TestBase.cs | 1 + .../SharpCompress.Test/Zip/ZipArchiveTests.cs | 10 + .../SharpCompress.Test/Zip/ZipReaderTests.cs | 5 + tests/TestArchives/Archives/Zip.deflate64.zip | Bin 0 -> 60747 bytes 19 files changed, 2062 insertions(+), 3 deletions(-) create mode 100644 src/SharpCompress/Compressors/Deflate64/BlockType.cs create mode 100644 src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs create mode 100644 src/SharpCompress/Compressors/Deflate64/DeflateInput.cs create mode 100644 src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs create mode 100644 src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs create mode 100644 src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs create mode 100644 src/SharpCompress/Compressors/Deflate64/InflaterState.cs create mode 100644 src/SharpCompress/Compressors/Deflate64/InputBuffer.cs create mode 100644 src/SharpCompress/Compressors/Deflate64/Match.cs create mode 100644 src/SharpCompress/Compressors/Deflate64/MatchState.cs create mode 100644 src/SharpCompress/Compressors/Deflate64/OutputWindow.cs create mode 100644 tests/TestArchives/Archives/Zip.deflate64.zip diff --git a/FORMATS.md b/FORMATS.md index 09d5ae63..2b1d3b34 100644 --- a/FORMATS.md +++ b/FORMATS.md @@ -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, BZip2, LZMA/LZMA2, PPMd | Both | ZipArchive | ZipReader | ZipWriter | +| Zip (2) | None, 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) | @@ -22,7 +22,7 @@ | LZip (single file) (5) | LZip (LZMA) | Both | LZipArchive | LZipReader | LZipWriter | 1. SOLID Rars are only supported in the RarReader API. - 2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading/writing is supported but only with seekable streams as the Zip spec doesn't support Zip64 data in post data descriptors. + 2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading/writing is supported but only with seekable streams as the Zip spec doesn't support Zip64 data in post data descriptors. Deflate64 is only supported for reading. 3. The Tar format requires a file size in the header. If no size is specified to the TarWriter and the stream is not seekable, then an exception will be thrown. 4. The 7Zip format doesn't allow for reading as a forward-only stream so 7Zip is only supported through the Archive API 5. LZip has no support for extra data like the file name or timestamp. There is a default filename used when looking at the entry Key on the archive. @@ -36,6 +36,7 @@ For those who want to directly compress/decompress bits. The single file format | BZip2Stream | Both | | GZipStream | Both | | DeflateStream | Both | +| Deflate64Stream | Decompress | | LZMAStream | Both | | PPMdStream | Both | | ADCStream | Decompress | diff --git a/src/SharpCompress/Common/CompressionType.cs b/src/SharpCompress/Common/CompressionType.cs index e2774385..23ed354f 100644 --- a/src/SharpCompress/Common/CompressionType.cs +++ b/src/SharpCompress/Common/CompressionType.cs @@ -13,6 +13,7 @@ BCJ2, LZip, Xz, - Unknown + Unknown, + Deflate64 } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index 8b8c7337..111c8a20 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -32,6 +32,10 @@ namespace SharpCompress.Common.Zip { return CompressionType.Deflate; } + case ZipCompressionMethod.Deflate64: + { + return CompressionType.Deflate64; + } case ZipCompressionMethod.LZMA: { return CompressionType.LZMA; diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index 1f626b0a..9b2e74ca 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -5,6 +5,7 @@ using SharpCompress.Common.Zip.Headers; using SharpCompress.Compressors; using SharpCompress.Compressors.BZip2; using SharpCompress.Compressors.Deflate; +using SharpCompress.Compressors.Deflate64; using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.PPMd; using SharpCompress.Converters; @@ -66,6 +67,10 @@ namespace SharpCompress.Common.Zip { return new DeflateStream(stream, CompressionMode.Decompress); } + case ZipCompressionMethod.Deflate64: + { + return new Deflate64Stream(stream, CompressionMode.Decompress); + } case ZipCompressionMethod.BZip2: { return new BZip2Stream(stream, CompressionMode.Decompress); diff --git a/src/SharpCompress/Compressors/Deflate64/BlockType.cs b/src/SharpCompress/Compressors/Deflate64/BlockType.cs new file mode 100644 index 00000000..c8938079 --- /dev/null +++ b/src/SharpCompress/Compressors/Deflate64/BlockType.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace SharpCompress.Compressors.Deflate64 +{ + internal enum BlockType + { + Uncompressed = 0, + Static = 1, + Dynamic = 2 + } +} diff --git a/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs b/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs new file mode 100644 index 00000000..1c8a5d9f --- /dev/null +++ b/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs @@ -0,0 +1,257 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using SharpCompress.Common.Zip; +using SharpCompress.Compressors.Deflate; +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; + +namespace SharpCompress.Compressors.Deflate64 +{ + public sealed partial class Deflate64Stream : Stream + { + internal const int DefaultBufferSize = 8192; + + private Stream _stream; + private CompressionMode _mode; + private bool _leaveOpen; + private InflaterManaged _inflater; + private byte[] _buffer; + + public Deflate64Stream(Stream stream, CompressionMode mode, + CompressionLevel level = CompressionLevel.Default, + bool leaveOpen = false) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + if (mode != CompressionMode.Decompress) + throw new NotImplementedException("Deflate64: this implementation only supports decompression"); + if (!stream.CanRead) + throw new ArgumentException("Deflate64: input stream is not readable", nameof(stream)); + + InitializeInflater(stream, leaveOpen, ZipCompressionMethod.Deflate64); + } + + /// + /// Sets up this DeflateManagedStream to be used for Inflation/Decompression + /// + internal void InitializeInflater(Stream stream, bool leaveOpen, ZipCompressionMethod method = ZipCompressionMethod.Deflate) + { + Debug.Assert(stream != null); + Debug.Assert(method == ZipCompressionMethod.Deflate || method == ZipCompressionMethod.Deflate64); + if (!stream.CanRead) + throw new ArgumentException("Deflate64: input stream is not readable", nameof(stream)); + + _inflater = new InflaterManaged(method == ZipCompressionMethod.Deflate64); + + _stream = stream; + _mode = CompressionMode.Decompress; + _leaveOpen = leaveOpen; + _buffer = new byte[DefaultBufferSize]; + } + + public override bool CanRead + { + get + { + if (_stream == null) + { + return false; + } + + return (_mode == CompressionMode.Decompress && _stream.CanRead); + } + } + + public override bool CanWrite + { + get + { + if (_stream == null) + { + return false; + } + + return (_mode == CompressionMode.Compress && _stream.CanWrite); + } + } + + public override bool CanSeek => false; + + public override long Length + { + get { throw new NotSupportedException("Deflate64: not supported"); } + } + + public override long Position + { + get { throw new NotSupportedException("Deflate64: not supported"); } + set { throw new NotSupportedException("Deflate64: not supported"); } + } + + public override void Flush() + { + EnsureNotDisposed(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("Deflate64: not supported"); + } + + public override void SetLength(long value) + { + throw new NotSupportedException("Deflate64: not supported"); + } + + public override int Read(byte[] array, int offset, int count) + { + EnsureDecompressionMode(); + ValidateParameters(array, offset, count); + EnsureNotDisposed(); + + int bytesRead; + int currentOffset = offset; + int remainingCount = count; + + while (true) + { + bytesRead = _inflater.Inflate(array, currentOffset, remainingCount); + currentOffset += bytesRead; + remainingCount -= bytesRead; + + if (remainingCount == 0) + { + break; + } + + if (_inflater.Finished()) + { + // if we finished decompressing, we can't have anything left in the outputwindow. + Debug.Assert(_inflater.AvailableOutput == 0, "We should have copied all stuff out!"); + break; + } + + int bytes = _stream.Read(_buffer, 0, _buffer.Length); + if (bytes <= 0) + { + break; + } + else if (bytes > _buffer.Length) + { + // 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"); + } + + _inflater.SetInput(_buffer, 0, bytes); + } + + return count - remainingCount; + } + + private void ValidateParameters(byte[] array, int offset, int count) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset)); + + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + + if (array.Length - offset < count) + throw new ArgumentException("Deflate64: invalid offset/count combination"); + } + + private void EnsureNotDisposed() + { + if (_stream == null) + ThrowStreamClosedException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowStreamClosedException() + { + throw new ObjectDisposedException(null, "Deflate64: stream has been disposed"); + } + + private void EnsureDecompressionMode() + { + if (_mode != CompressionMode.Decompress) + ThrowCannotReadFromDeflateManagedStreamException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowCannotReadFromDeflateManagedStreamException() + { + throw new InvalidOperationException("Deflate64: cannot read from this stream"); + } + + private void EnsureCompressionMode() + { + if (_mode != CompressionMode.Compress) + ThrowCannotWriteToDeflateManagedStreamException(); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowCannotWriteToDeflateManagedStreamException() + { + throw new InvalidOperationException("Deflate64: cannot write to this stream"); + } + + public override void Write(byte[] array, int offset, int count) + { + ThrowCannotWriteToDeflateManagedStreamException(); + } + + // This is called by Dispose: + private void PurgeBuffers(bool disposing) + { + if (!disposing) + return; + + if (_stream == null) + return; + + Flush(); + } + + protected override void Dispose(bool disposing) + { + try + { + PurgeBuffers(disposing); + } + finally + { + // Close the underlying stream even if PurgeBuffers threw. + // Stream.Close() may throw here (may or may not be due to the same error). + // In this case, we still need to clean up internal resources, hence the inner finally blocks. + try + { + if (disposing && !_leaveOpen && _stream != null) + _stream.Dispose(); + } + finally + { + _stream = null; + + try + { + _inflater?.Dispose(); + } + finally + { + _inflater = null; + base.Dispose(disposing); + } + } + } + } + } +} diff --git a/src/SharpCompress/Compressors/Deflate64/DeflateInput.cs b/src/SharpCompress/Compressors/Deflate64/DeflateInput.cs new file mode 100644 index 00000000..faf26edb --- /dev/null +++ b/src/SharpCompress/Compressors/Deflate64/DeflateInput.cs @@ -0,0 +1,43 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace SharpCompress.Compressors.Deflate64 +{ + internal sealed class DeflateInput + { + internal byte[] Buffer { get; set; } + internal int Count { get; set; } + internal int StartIndex { get; set; } + + internal void ConsumeBytes(int n) + { + Debug.Assert(n <= Count, "Should use more bytes than what we have in the buffer"); + StartIndex += n; + Count -= n; + Debug.Assert(StartIndex + Count <= Buffer.Length, "Input buffer is in invalid state!"); + } + + internal InputState DumpState() => new InputState(Count, StartIndex); + + internal void RestoreState(InputState state) + { + Count = state._count; + StartIndex = state._startIndex; + } + + internal /*readonly */struct InputState + { + internal readonly int _count; + internal readonly int _startIndex; + + internal InputState(int count, int startIndex) + { + _count = count; + _startIndex = startIndex; + } + } + } +} diff --git a/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs b/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs new file mode 100644 index 00000000..1e455a46 --- /dev/null +++ b/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs @@ -0,0 +1,245 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; + +namespace SharpCompress.Compressors.Deflate64 +{ + internal static class FastEncoderStatics + { + // static information for encoding, DO NOT MODIFY + + internal static readonly byte[] FastEncoderTreeStructureData = + { + 0xec,0xbd,0x07,0x60,0x1c,0x49,0x96,0x25,0x26,0x2f,0x6d,0xca, + 0x7b,0x7f,0x4a,0xf5,0x4a,0xd7,0xe0,0x74,0xa1,0x08,0x80,0x60, + 0x13,0x24,0xd8,0x90,0x40,0x10,0xec,0xc1,0x88,0xcd,0xe6,0x92, + 0xec,0x1d,0x69,0x47,0x23,0x29,0xab,0x2a,0x81,0xca,0x65,0x56, + 0x65,0x5d,0x66,0x16,0x40,0xcc,0xed,0x9d,0xbc,0xf7,0xde,0x7b, + 0xef,0xbd,0xf7,0xde,0x7b,0xef,0xbd,0xf7,0xba,0x3b,0x9d,0x4e, + 0x27,0xf7,0xdf,0xff,0x3f,0x5c,0x66,0x64,0x01,0x6c,0xf6,0xce, + 0x4a,0xda,0xc9,0x9e,0x21,0x80,0xaa,0xc8,0x1f,0x3f,0x7e,0x7c, + 0x1f,0x3f + }; + + internal static readonly byte[] BFinalFastEncoderTreeStructureData = + { + 0xed,0xbd,0x07,0x60,0x1c,0x49,0x96,0x25,0x26,0x2f,0x6d,0xca, + 0x7b,0x7f,0x4a,0xf5,0x4a,0xd7,0xe0,0x74,0xa1,0x08,0x80,0x60, + 0x13,0x24,0xd8,0x90,0x40,0x10,0xec,0xc1,0x88,0xcd,0xe6,0x92, + 0xec,0x1d,0x69,0x47,0x23,0x29,0xab,0x2a,0x81,0xca,0x65,0x56, + 0x65,0x5d,0x66,0x16,0x40,0xcc,0xed,0x9d,0xbc,0xf7,0xde,0x7b, + 0xef,0xbd,0xf7,0xde,0x7b,0xef,0xbd,0xf7,0xba,0x3b,0x9d,0x4e, + 0x27,0xf7,0xdf,0xff,0x3f,0x5c,0x66,0x64,0x01,0x6c,0xf6,0xce, + 0x4a,0xda,0xc9,0x9e,0x21,0x80,0xaa,0xc8,0x1f,0x3f,0x7e,0x7c, + 0x1f,0x3f + }; + + // Output a currentMatch with length matchLen (>= MIN_MATCH) and displacement matchPos + // + // Optimisation: unlike the other encoders, here we have an array of codes for each currentMatch + // length (not just each currentMatch length slot), complete with all the extra bits filled in, in + // a single array element. + // + // There are many advantages to doing this: + // + // 1. A single array lookup on g_FastEncoderLiteralCodeInfo, instead of separate array lookups + // on g_LengthLookup (to get the length slot), g_FastEncoderLiteralTreeLength, + // g_FastEncoderLiteralTreeCode, g_ExtraLengthBits, and g_BitMask + // + // 2. The array is an array of ULONGs, so no access penalty, unlike for accessing those USHORT + // code arrays in the other encoders (although they could be made into ULONGs with some + // modifications to the source). + // + // Note, if we could guarantee that codeLen <= 16 always, then we could skip an if statement here. + // + // A completely different optimisation is used for the distance codes since, obviously, a table for + // all 8192 distances combining their extra bits is not feasible. The distance codeinfo table is + // made up of code[], len[] and # extraBits for this code. + // + // The advantages are similar to the above; a ULONG array instead of a USHORT and BYTE array, better + // 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. + + internal static readonly uint[] FastEncoderLiteralCodeInfo = + { + 0x0000d7ee,0x0004d7ee,0x0002d7ee,0x0006d7ee,0x0001d7ee,0x0005d7ee,0x0003d7ee, + 0x0007d7ee,0x000037ee,0x0000c7ec,0x00000126,0x000437ee,0x000237ee,0x000637ee, + 0x000137ee,0x000537ee,0x000337ee,0x000737ee,0x0000b7ee,0x0004b7ee,0x0002b7ee, + 0x0006b7ee,0x0001b7ee,0x0005b7ee,0x0003b7ee,0x0007b7ee,0x000077ee,0x000477ee, + 0x000277ee,0x000677ee,0x000017ed,0x000177ee,0x00000526,0x000577ee,0x000023ea, + 0x0001c7ec,0x000377ee,0x000777ee,0x000217ed,0x000063ea,0x00000b68,0x00000ee9, + 0x00005beb,0x000013ea,0x00000467,0x00001b68,0x00000c67,0x00002ee9,0x00000768, + 0x00001768,0x00000f68,0x00001ee9,0x00001f68,0x00003ee9,0x000053ea,0x000001e9, + 0x000000e8,0x000021e9,0x000011e9,0x000010e8,0x000031e9,0x000033ea,0x000008e8, + 0x0000f7ee,0x0004f7ee,0x000018e8,0x000009e9,0x000004e8,0x000029e9,0x000014e8, + 0x000019e9,0x000073ea,0x0000dbeb,0x00000ce8,0x00003beb,0x0002f7ee,0x000039e9, + 0x00000bea,0x000005e9,0x00004bea,0x000025e9,0x000027ec,0x000015e9,0x000035e9, + 0x00000de9,0x00002bea,0x000127ec,0x0000bbeb,0x0006f7ee,0x0001f7ee,0x0000a7ec, + 0x00007beb,0x0005f7ee,0x0000fbeb,0x0003f7ee,0x0007f7ee,0x00000fee,0x00000326, + 0x00000267,0x00000a67,0x00000667,0x00000726,0x00001ce8,0x000002e8,0x00000e67, + 0x000000a6,0x0001a7ec,0x00002de9,0x000004a6,0x00000167,0x00000967,0x000002a6, + 0x00000567,0x000117ed,0x000006a6,0x000001a6,0x000005a6,0x00000d67,0x000012e8, + 0x00000ae8,0x00001de9,0x00001ae8,0x000007eb,0x000317ed,0x000067ec,0x000097ed, + 0x000297ed,0x00040fee,0x00020fee,0x00060fee,0x00010fee,0x00050fee,0x00030fee, + 0x00070fee,0x00008fee,0x00048fee,0x00028fee,0x00068fee,0x00018fee,0x00058fee, + 0x00038fee,0x00078fee,0x00004fee,0x00044fee,0x00024fee,0x00064fee,0x00014fee, + 0x00054fee,0x00034fee,0x00074fee,0x0000cfee,0x0004cfee,0x0002cfee,0x0006cfee, + 0x0001cfee,0x0005cfee,0x0003cfee,0x0007cfee,0x00002fee,0x00042fee,0x00022fee, + 0x00062fee,0x00012fee,0x00052fee,0x00032fee,0x00072fee,0x0000afee,0x0004afee, + 0x0002afee,0x0006afee,0x0001afee,0x0005afee,0x0003afee,0x0007afee,0x00006fee, + 0x00046fee,0x00026fee,0x00066fee,0x00016fee,0x00056fee,0x00036fee,0x00076fee, + 0x0000efee,0x0004efee,0x0002efee,0x0006efee,0x0001efee,0x0005efee,0x0003efee, + 0x0007efee,0x00001fee,0x00041fee,0x00021fee,0x00061fee,0x00011fee,0x00051fee, + 0x00031fee,0x00071fee,0x00009fee,0x00049fee,0x00029fee,0x00069fee,0x00019fee, + 0x00059fee,0x00039fee,0x00079fee,0x00005fee,0x00045fee,0x00025fee,0x00065fee, + 0x00015fee,0x00055fee,0x00035fee,0x00075fee,0x0000dfee,0x0004dfee,0x0002dfee, + 0x0006dfee,0x0001dfee,0x0005dfee,0x0003dfee,0x0007dfee,0x00003fee,0x00043fee, + 0x00023fee,0x00063fee,0x00013fee,0x00053fee,0x00033fee,0x00073fee,0x0000bfee, + 0x0004bfee,0x0002bfee,0x0006bfee,0x0001bfee,0x0005bfee,0x0003bfee,0x0007bfee, + 0x00007fee,0x00047fee,0x00027fee,0x00067fee,0x00017fee,0x000197ed,0x000397ed, + 0x000057ed,0x00057fee,0x000257ed,0x00037fee,0x000157ed,0x00077fee,0x000357ed, + 0x0000ffee,0x0004ffee,0x0002ffee,0x0006ffee,0x0001ffee,0x00000084,0x00000003, + 0x00000184,0x00000044,0x00000144,0x000000c5,0x000002c5,0x000001c5,0x000003c6, + 0x000007c6,0x00000026,0x00000426,0x000003a7,0x00000ba7,0x000007a7,0x00000fa7, + 0x00000227,0x00000627,0x00000a27,0x00000e27,0x00000068,0x00000868,0x00001068, + 0x00001868,0x00000369,0x00001369,0x00002369,0x00003369,0x000006ea,0x000026ea, + 0x000046ea,0x000066ea,0x000016eb,0x000036eb,0x000056eb,0x000076eb,0x000096eb, + 0x0000b6eb,0x0000d6eb,0x0000f6eb,0x00003dec,0x00007dec,0x0000bdec,0x0000fdec, + 0x00013dec,0x00017dec,0x0001bdec,0x0001fdec,0x00006bed,0x0000ebed,0x00016bed, + 0x0001ebed,0x00026bed,0x0002ebed,0x00036bed,0x0003ebed,0x000003ec,0x000043ec, + 0x000083ec,0x0000c3ec,0x000103ec,0x000143ec,0x000183ec,0x0001c3ec,0x00001bee, + 0x00009bee,0x00011bee,0x00019bee,0x00021bee,0x00029bee,0x00031bee,0x00039bee, + 0x00041bee,0x00049bee,0x00051bee,0x00059bee,0x00061bee,0x00069bee,0x00071bee, + 0x00079bee,0x000167f0,0x000367f0,0x000567f0,0x000767f0,0x000967f0,0x000b67f0, + 0x000d67f0,0x000f67f0,0x001167f0,0x001367f0,0x001567f0,0x001767f0,0x001967f0, + 0x001b67f0,0x001d67f0,0x001f67f0,0x000087ef,0x000187ef,0x000287ef,0x000387ef, + 0x000487ef,0x000587ef,0x000687ef,0x000787ef,0x000887ef,0x000987ef,0x000a87ef, + 0x000b87ef,0x000c87ef,0x000d87ef,0x000e87ef,0x000f87ef,0x0000e7f0,0x0002e7f0, + 0x0004e7f0,0x0006e7f0,0x0008e7f0,0x000ae7f0,0x000ce7f0,0x000ee7f0,0x0010e7f0, + 0x0012e7f0,0x0014e7f0,0x0016e7f0,0x0018e7f0,0x001ae7f0,0x001ce7f0,0x001ee7f0, + 0x0005fff3,0x000dfff3,0x0015fff3,0x001dfff3,0x0025fff3,0x002dfff3,0x0035fff3, + 0x003dfff3,0x0045fff3,0x004dfff3,0x0055fff3,0x005dfff3,0x0065fff3,0x006dfff3, + 0x0075fff3,0x007dfff3,0x0085fff3,0x008dfff3,0x0095fff3,0x009dfff3,0x00a5fff3, + 0x00adfff3,0x00b5fff3,0x00bdfff3,0x00c5fff3,0x00cdfff3,0x00d5fff3,0x00ddfff3, + 0x00e5fff3,0x00edfff3,0x00f5fff3,0x00fdfff3,0x0003fff3,0x000bfff3,0x0013fff3, + 0x001bfff3,0x0023fff3,0x002bfff3,0x0033fff3,0x003bfff3,0x0043fff3,0x004bfff3, + 0x0053fff3,0x005bfff3,0x0063fff3,0x006bfff3,0x0073fff3,0x007bfff3,0x0083fff3, + 0x008bfff3,0x0093fff3,0x009bfff3,0x00a3fff3,0x00abfff3,0x00b3fff3,0x00bbfff3, + 0x00c3fff3,0x00cbfff3,0x00d3fff3,0x00dbfff3,0x00e3fff3,0x00ebfff3,0x00f3fff3, + 0x00fbfff3,0x0007fff3,0x000ffff3,0x0017fff3,0x001ffff3,0x0027fff3,0x002ffff3, + 0x0037fff3,0x003ffff3,0x0047fff3,0x004ffff3,0x0057fff3,0x005ffff3,0x0067fff3, + 0x006ffff3,0x0077fff3,0x007ffff3,0x0087fff3,0x008ffff3,0x0097fff3,0x009ffff3, + 0x00a7fff3,0x00affff3,0x00b7fff3,0x00bffff3,0x00c7fff3,0x00cffff3,0x00d7fff3, + 0x00dffff3,0x00e7fff3,0x00effff3,0x00f7fff3,0x00fffff3,0x0001e7f1,0x0003e7f1, + 0x0005e7f1,0x0007e7f1,0x0009e7f1,0x000be7f1,0x000de7f1,0x000fe7f1,0x0011e7f1, + 0x0013e7f1,0x0015e7f1,0x0017e7f1,0x0019e7f1,0x001be7f1,0x001de7f1,0x001fe7f1, + 0x0021e7f1,0x0023e7f1,0x0025e7f1,0x0027e7f1,0x0029e7f1,0x002be7f1,0x002de7f1, + 0x002fe7f1,0x0031e7f1,0x0033e7f1,0x0035e7f1,0x0037e7f1,0x0039e7f1,0x003be7f1, + 0x003de7f1,0x000047eb + }; + + internal static readonly uint[] FastEncoderDistanceCodeInfo = + { + 0x00000f06,0x0001ff0a,0x0003ff0b,0x0007ff0b,0x0000ff19,0x00003f18,0x0000bf28, + 0x00007f28,0x00001f37,0x00005f37,0x00000d45,0x00002f46,0x00000054,0x00001d55, + 0x00000864,0x00000365,0x00000474,0x00001375,0x00000c84,0x00000284,0x00000a94, + 0x00000694,0x00000ea4,0x000001a4,0x000009b4,0x00000bb5,0x000005c4,0x00001bc5, + 0x000007d5,0x000017d5,0x00000000,0x00000100 + }; + + internal static readonly uint[] BitMask = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767 }; + internal static readonly byte[] ExtraLengthBits = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + internal static readonly byte[] ExtraDistanceBits = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0 }; + internal const int NumChars = 256; + internal const int NumLengthBaseCodes = 29; + internal const int NumDistBaseCodes = 30; + + internal const uint FastEncoderPostTreeBitBuf = 0x0022; + internal const int FastEncoderPostTreeBitCount = 9; + + internal const uint NoCompressionHeader = 0x0; + internal const int NoCompressionHeaderBitCount = 3; + internal const uint BFinalNoCompressionHeader = 0x1; + internal const int BFinalNoCompressionHeaderBitCount = 3; + internal const int MaxCodeLen = 16; + + private static readonly byte[] s_distLookup = CreateDistanceLookup(); + + private static byte[] CreateDistanceLookup() + { + byte[] result = new byte[512]; + + // Generate the global slot tables which allow us to convert a distance + // (0..32K) to a distance slot (0..29) + // + // Distance table + // Extra Extra Extra + // Code Bits Dist Code Bits Dist Code Bits Distance + // ---- ---- ---- ---- ---- ------ ---- ---- -------- + // 0 0 1 10 4 33-48 20 9 1025-1536 + // 1 0 2 11 4 49-64 21 9 1537-2048 + // 2 0 3 12 5 65-96 22 10 2049-3072 + // 3 0 4 13 5 97-128 23 10 3073-4096 + // 4 1 5,6 14 6 129-192 24 11 4097-6144 + // 5 1 7,8 15 6 193-256 25 11 6145-8192 + // 6 2 9-12 16 7 257-384 26 12 8193-12288 + // 7 2 13-16 17 7 385-512 27 12 12289-16384 + // 8 3 17-24 18 8 513-768 28 13 16385-24576 + // 9 3 25-32 19 8 769-1024 29 13 24577-32768 + + // Initialize the mapping length (0..255) -> length code (0..28) + //int length = 0; + //for (code = 0; code < FastEncoderStatics.NumLengthBaseCodes-1; code++) { + // for (int n = 0; n < (1 << FastEncoderStatics.ExtraLengthBits[code]); n++) + // lengthLookup[length++] = (byte) code; + //} + //lengthLookup[length-1] = (byte) code; + + // Initialize the mapping dist (0..32K) -> dist code (0..29) + int dist = 0; + int code; + for (code = 0; code < 16; code++) + { + for (int n = 0; n < (1 << ExtraDistanceBits[code]); n++) + result[dist++] = (byte)code; + } + + dist >>= 7; // from now on, all distances are divided by 128 + + for (; code < NumDistBaseCodes; code++) + { + for (int n = 0; n < (1 << (ExtraDistanceBits[code] - 7)); n++) + result[256 + dist++] = (byte)code; + } + + return result; + } + + // Return the position slot (0...29) of a match offset (0...32767) + internal static int GetSlot(int pos) => + s_distLookup[((pos) < 256) ? (pos) : (256 + ((pos) >> 7))]; + + // Reverse 'length' of the bits in code + public static uint BitReverse(uint code, int length) + { + uint new_code = 0; + + Debug.Assert(length > 0 && length <= 16, "Invalid len"); + do + { + new_code |= (code & 1); + new_code <<= 1; + code >>= 1; + } while (--length > 0); + + return new_code >> 1; + } + } +} diff --git a/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs b/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs new file mode 100644 index 00000000..b57219a2 --- /dev/null +++ b/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs @@ -0,0 +1,311 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.IO; + +namespace SharpCompress.Compressors.Deflate64 +{ + // Strictly speaking this class is not a HuffmanTree, this class is + // a lookup table combined with a HuffmanTree. The idea is to speed up + // the lookup for short symbols (they should appear more frequently ideally.) + // However we don't want to create a huge table since it might take longer to + // build the table than decoding (Deflate usually generates new tables frequently.) + // + // Jean-loup Gailly and Mark Adler gave a very good explanation about this. + // The full text (algorithm.txt) can be found inside + // ftp://ftp.uu.net/pub/archiving/zip/zlib/zlib.zip. + // + // Following paper explains decoding in details: + // Hirschberg and Lelewer, "Efficient decoding of prefix codes," + // Comm. ACM, 33,4, April 1990, pp. 449-459. + // + + internal sealed class HuffmanTree + { + internal const int MaxLiteralTreeElements = 288; + internal const int MaxDistTreeElements = 32; + internal const int EndOfBlockCode = 256; + internal const int NumberOfCodeLengthTreeElements = 19; + + private readonly int _tableBits; + private readonly short[] _table; + private readonly short[] _left; + private readonly short[] _right; + private readonly byte[] _codeLengthArray; +#if DEBUG + private uint[] _codeArrayDebug; +#endif + + private readonly int _tableMask; + + // huffman tree for static block + public static HuffmanTree StaticLiteralLengthTree { get; } = new HuffmanTree(GetStaticLiteralTreeLength()); + + public static HuffmanTree StaticDistanceTree { get; } = new HuffmanTree(GetStaticDistanceTreeLength()); + + public HuffmanTree(byte[] codeLengths) + { + Debug.Assert( + codeLengths.Length == MaxLiteralTreeElements || + codeLengths.Length == MaxDistTreeElements || + codeLengths.Length == NumberOfCodeLengthTreeElements, + "we only expect three kinds of Length here"); + _codeLengthArray = codeLengths; + + if (_codeLengthArray.Length == MaxLiteralTreeElements) + { + // bits for Literal/Length tree table + _tableBits = 9; + } + else + { + // bits for distance tree table and code length tree table + _tableBits = 7; + } + _tableMask = (1 << _tableBits) - 1; + + _table = new short[1 << _tableBits]; + + // I need to find proof that left and right array will always be + // enough. I think they are. + _left = new short[2 * _codeLengthArray.Length]; + _right = new short[2 * _codeLengthArray.Length]; + + CreateTable(); + } + + // Generate the array contains huffman codes lengths for static huffman tree. + // The data is in RFC 1951. + private static byte[] GetStaticLiteralTreeLength() + { + byte[] literalTreeLength = new byte[MaxLiteralTreeElements]; + for (int i = 0; i <= 143; i++) + literalTreeLength[i] = 8; + + for (int i = 144; i <= 255; i++) + literalTreeLength[i] = 9; + + for (int i = 256; i <= 279; i++) + literalTreeLength[i] = 7; + + for (int i = 280; i <= 287; i++) + literalTreeLength[i] = 8; + + return literalTreeLength; + } + + private static byte[] GetStaticDistanceTreeLength() + { + byte[] staticDistanceTreeLength = new byte[MaxDistTreeElements]; + for (int i = 0; i < MaxDistTreeElements; i++) + { + staticDistanceTreeLength[i] = 5; + } + return staticDistanceTreeLength; + } + + // Calculate the huffman code for each character based on the code length for each character. + // This algorithm is described in standard RFC 1951 + private uint[] CalculateHuffmanCode() + { + uint[] bitLengthCount = new uint[17]; + foreach (int codeLength in _codeLengthArray) + { + bitLengthCount[codeLength]++; + } + bitLengthCount[0] = 0; // clear count for length 0 + + uint[] nextCode = new uint[17]; + uint tempCode = 0; + for (int bits = 1; bits <= 16; bits++) + { + tempCode = (tempCode + bitLengthCount[bits - 1]) << 1; + nextCode[bits] = tempCode; + } + + uint[] code = new uint[MaxLiteralTreeElements]; + for (int i = 0; i < _codeLengthArray.Length; i++) + { + int len = _codeLengthArray[i]; + + if (len > 0) + { + code[i] = FastEncoderStatics.BitReverse(nextCode[len], len); + nextCode[len]++; + } + } + return code; + } + + private void CreateTable() + { + uint[] codeArray = CalculateHuffmanCode(); +#if DEBUG + _codeArrayDebug = codeArray; +#endif + + short avail = (short)_codeLengthArray.Length; + + for (int ch = 0; ch < _codeLengthArray.Length; ch++) + { + // length of this code + int len = _codeLengthArray[ch]; + if (len > 0) + { + // start value (bit reversed) + int start = (int)codeArray[ch]; + + if (len <= _tableBits) + { + // If a particular symbol is shorter than nine bits, + // then that symbol's translation is duplicated + // in all those entries that start with that symbol's bits. + // For example, if the symbol is four bits, then it's duplicated + // 32 times in a nine-bit table. If a symbol is nine bits long, + // it appears in the table once. + // + // Make sure that in the loop below, code is always + // less than table_size. + // + // On last iteration we store at array index: + // initial_start_at + (locs-1)*increment + // = initial_start_at + locs*increment - increment + // = initial_start_at + (1 << tableBits) - increment + // = initial_start_at + table_size - increment + // + // Therefore we must ensure: + // initial_start_at + table_size - increment < table_size + // or: initial_start_at < increment + // + int increment = 1 << len; + if (start >= increment) + { + throw new InvalidDataException("Deflate64: invalid Huffman data"); + } + + // Note the bits in the table are reverted. + int locs = 1 << (_tableBits - len); + for (int j = 0; j < locs; j++) + { + _table[start] = (short)ch; + start += increment; + } + } + else + { + // For any code which has length longer than num_elements, + // build a binary tree. + + int overflowBits = len - _tableBits; // the nodes we need to respent the data. + int codeBitMask = 1 << _tableBits; // mask to get current bit (the bits can't fit in the table) + + // the left, right table is used to repesent the + // the rest bits. When we got the first part (number bits.) and look at + // tbe table, we will need to follow the tree to find the real character. + // This is in place to avoid bloating the table if there are + // a few ones with long code. + int index = start & ((1 << _tableBits) - 1); + short[] array = _table; + + do + { + short value = array[index]; + + if (value == 0) + { + // set up next pointer if this node is not used before. + array[index] = (short)-avail; // use next available slot. + value = (short)-avail; + avail++; + } + + if (value > 0) + { + // prevent an IndexOutOfRangeException from array[index] + throw new InvalidDataException("Deflate64: invalid Huffman data"); + } + + Debug.Assert(value < 0, "CreateTable: Only negative numbers are used for tree pointers!"); + + if ((start & codeBitMask) == 0) + { + // if current bit is 0, go change the left array + array = _left; + } + else + { + // if current bit is 1, set value in the right array + array = _right; + } + index = -value; // go to next node + + codeBitMask <<= 1; + overflowBits--; + } while (overflowBits != 0); + + array[index] = (short)ch; + } + } + } + } + + // + // This function will try to get enough bits from input and + // try to decode the bits. + // If there are no enought bits in the input, this function will return -1. + // + public int GetNextSymbol(InputBuffer input) + { + // Try to load 16 bits into input buffer if possible and get the bitBuffer value. + // If there aren't 16 bits available we will return all we have in the + // input buffer. + uint bitBuffer = input.TryLoad16Bits(); + if (input.AvailableBits == 0) + { // running out of input. + return -1; + } + + // decode an element + int symbol = _table[bitBuffer & _tableMask]; + if (symbol < 0) + { // this will be the start of the binary tree + // navigate the tree + uint mask = (uint)1 << _tableBits; + do + { + symbol = -symbol; + if ((bitBuffer & mask) == 0) + symbol = _left[symbol]; + else + symbol = _right[symbol]; + mask <<= 1; + } while (symbol < 0); + } + + int codeLength = _codeLengthArray[symbol]; + + // huffman code lengths must be at least 1 bit long + if (codeLength <= 0) + { + throw new InvalidDataException("Deflate64: invalid Huffman data"); + } + + // + // If this code is longer than the # bits we had in the bit buffer (i.e. + // we read only part of the code), we can hit the entry in the table or the tree + // for another symbol. However the length of another symbol will not match the + // available bits count. + if (codeLength > input.AvailableBits) + { + // We already tried to load 16 bits and maximum length is 15, + // so this means we are running out of input. + return -1; + } + + input.SkipBits(codeLength); + return symbol; + } + } +} diff --git a/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs b/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs new file mode 100644 index 00000000..32194d78 --- /dev/null +++ b/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs @@ -0,0 +1,738 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// +// zlib.h -- interface of the 'zlib' general purpose compression library +// version 1.2.1, November 17th, 2003 +// +// Copyright (C) 1995-2003 Jean-loup Gailly and Mark Adler +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// + +using System; +using System.Diagnostics; +using System.IO; + +namespace SharpCompress.Compressors.Deflate64 +{ + internal sealed class InflaterManaged + { + // const tables used in decoding: + + // Extra bits for length code 257 - 285. + private static readonly byte[] s_extraLengthBits = + { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,16 }; + + // The base length for length code 257 - 285. + // The formula to get the real length for a length code is lengthBase[code - 257] + (value stored in extraBits) + private static readonly int[] s_lengthBase = + { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,3}; + + // The base distance for distance code 0 - 31 + // The real distance for a distance code is distanceBasePosition[code] + (value stored in extraBits) + private static readonly int[] s_distanceBasePosition = + { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,32769,49153 }; + + // code lengths for code length alphabet is stored in following order + private static readonly byte[] s_codeOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + private static readonly byte[] s_staticDistanceTreeTable = + { + 0x00,0x10,0x08,0x18,0x04,0x14,0x0c,0x1c,0x02,0x12,0x0a,0x1a, + 0x06,0x16,0x0e,0x1e,0x01,0x11,0x09,0x19,0x05,0x15,0x0d,0x1d, + 0x03,0x13,0x0b,0x1b,0x07,0x17,0x0f,0x1f + }; + + private readonly OutputWindow _output; + private readonly InputBuffer _input; + private HuffmanTree _literalLengthTree; + private HuffmanTree _distanceTree; + + private InflaterState _state; + //private bool _hasFormatReader; + private int _bfinal; + private BlockType _blockType; + + // uncompressed block + private readonly byte[] _blockLengthBuffer = new byte[4]; + private int _blockLength; + + // compressed block + private int _length; + private int _distanceCode; + private int _extraBits; + + private int _loopCounter; + private int _literalLengthCodeCount; + private int _distanceCodeCount; + private int _codeLengthCodeCount; + private int _codeArraySize; + private int _lengthCode; + + private readonly byte[] _codeList; // temporary array to store the code length for literal/Length and distance + private readonly byte[] _codeLengthTreeCodeLength; + private readonly bool _deflate64; + private HuffmanTree _codeLengthTree; + + //private IFileFormatReader _formatReader; // class to decode header and footer (e.g. gzip) + + internal InflaterManaged(/*IFileFormatReader reader, */bool deflate64) + { + _output = new OutputWindow(); + _input = new InputBuffer(); + + _codeList = new byte[HuffmanTree.MaxLiteralTreeElements + HuffmanTree.MaxDistTreeElements]; + _codeLengthTreeCodeLength = new byte[HuffmanTree.NumberOfCodeLengthTreeElements]; + _deflate64 = deflate64; + //if (reader != null) + //{ + // _formatReader = reader; + // _hasFormatReader = true; + //} + Reset(); + } + + private void Reset() + { + _state = //_hasFormatReader ? + //InflaterState.ReadingHeader : // start by reading Header info + InflaterState.ReadingBFinal; // start by reading BFinal bit + } + + public void SetInput(byte[] inputBytes, int offset, int length) => + _input.SetInput(inputBytes, offset, length); // append the bytes + + public bool Finished() => _state == InflaterState.Done || _state == InflaterState.VerifyingFooter; + + public int AvailableOutput => _output.AvailableBytes; + + public int Inflate(byte[] bytes, int offset, int length) + { + // copy bytes from output to outputbytes if we have available bytes + // if buffer is not filled up. keep decoding until no input are available + // if decodeBlock returns false. Throw an exception. + int count = 0; + do + { + int copied = _output.CopyTo(bytes, offset, length); + if (copied > 0) + { + //if (_hasFormatReader) + //{ + // _formatReader.UpdateWithBytesRead(bytes, offset, copied); + //} + + offset += copied; + count += copied; + length -= copied; + } + + if (length == 0) + { // filled in the bytes array + break; + } + // Decode will return false when more input is needed + } while (!Finished() && Decode()); + + if (_state == InflaterState.VerifyingFooter) + { // finished reading CRC + // In this case finished is true and output window has all the data. + // But some data in output window might not be copied out. + if (_output.AvailableBytes == 0) + { + //_formatReader.Validate(); + } + } + + return count; + } + + //Each block of compressed data begins with 3 header bits + // containing the following data: + // first bit BFINAL + // next 2 bits BTYPE + // Note that the header bits do not necessarily begin on a byte + // boundary, since a block does not necessarily occupy an integral + // number of bytes. + // BFINAL is set if and only if this is the last block of the data + // set. + // BTYPE specifies how the data are compressed, as follows: + // 00 - no compression + // 01 - compressed with fixed Huffman codes + // 10 - compressed with dynamic Huffman codes + // 11 - reserved (error) + // The only difference between the two compressed cases is how the + // Huffman codes for the literal/length and distance alphabets are + // defined. + // + // This function returns true for success (end of block or output window is full,) + // false if we are short of input + // + private bool Decode() + { + bool eob = false; + bool result = false; + + if (Finished()) + { + return true; + } + + //if (_hasFormatReader) + //{ + // if (_state == InflaterState.ReadingHeader) + // { + // if (!_formatReader.ReadHeader(_input)) + // { + // return false; + // } + // _state = InflaterState.ReadingBFinal; + // } + // else if (_state == InflaterState.StartReadingFooter || _state == InflaterState.ReadingFooter) + // { + // if (!_formatReader.ReadFooter(_input)) + // return false; + + // _state = InflaterState.VerifyingFooter; + // return true; + // } + //} + + if (_state == InflaterState.ReadingBFinal) + { + // reading bfinal bit + // Need 1 bit + if (!_input.EnsureBitsAvailable(1)) + return false; + + _bfinal = _input.GetBits(1); + _state = InflaterState.ReadingBType; + } + + if (_state == InflaterState.ReadingBType) + { + // Need 2 bits + if (!_input.EnsureBitsAvailable(2)) + { + _state = InflaterState.ReadingBType; + return false; + } + + _blockType = (BlockType)_input.GetBits(2); + if (_blockType == BlockType.Dynamic) + { + _state = InflaterState.ReadingNumLitCodes; + } + else if (_blockType == BlockType.Static) + { + _literalLengthTree = HuffmanTree.StaticLiteralLengthTree; + _distanceTree = HuffmanTree.StaticDistanceTree; + _state = InflaterState.DecodeTop; + } + else if (_blockType == BlockType.Uncompressed) + { + _state = InflaterState.UncompressedAligning; + } + else + { + throw new InvalidDataException("Deflate64: unknown block type"); + } + } + + if (_blockType == BlockType.Dynamic) + { + if (_state < InflaterState.DecodeTop) + { + // we are reading the header + result = DecodeDynamicBlockHeader(); + } + else + { + result = DecodeBlock(out eob); // this can returns true when output is full + } + } + else if (_blockType == BlockType.Static) + { + result = DecodeBlock(out eob); + } + else if (_blockType == BlockType.Uncompressed) + { + result = DecodeUncompressedBlock(out eob); + } + else + { + throw new InvalidDataException("Deflate64: unknown block type"); + } + + // + // If we reached the end of the block and the block we were decoding had + // bfinal=1 (final block) + // + if (eob && (_bfinal != 0)) + { + //if (_hasFormatReader) + // _state = InflaterState.StartReadingFooter; + //else + _state = InflaterState.Done; + } + return result; + } + + + // Format of Non-compressed blocks (BTYPE=00): + // + // Any bits of input up to the next byte boundary are ignored. + // The rest of the block consists of the following information: + // + // 0 1 2 3 4... + // +---+---+---+---+================================+ + // | LEN | NLEN |... LEN bytes of literal data...| + // +---+---+---+---+================================+ + // + // LEN is the number of data bytes in the block. NLEN is the + // one's complement of LEN. + private bool DecodeUncompressedBlock(out bool end_of_block) + { + end_of_block = false; + while (true) + { + switch (_state) + { + case InflaterState.UncompressedAligning: // initial state when calling this function + // we must skip to a byte boundary + _input.SkipToByteBoundary(); + _state = InflaterState.UncompressedByte1; + goto case InflaterState.UncompressedByte1; + + case InflaterState.UncompressedByte1: // decoding block length + case InflaterState.UncompressedByte2: + case InflaterState.UncompressedByte3: + case InflaterState.UncompressedByte4: + int bits = _input.GetBits(8); + if (bits < 0) + { + return false; + } + + _blockLengthBuffer[_state - InflaterState.UncompressedByte1] = (byte)bits; + if (_state == InflaterState.UncompressedByte4) + { + _blockLength = _blockLengthBuffer[0] + ((int)_blockLengthBuffer[1]) * 256; + int blockLengthComplement = _blockLengthBuffer[2] + ((int)_blockLengthBuffer[3]) * 256; + + // make sure complement matches + if ((ushort)_blockLength != (ushort)(~blockLengthComplement)) + { + throw new InvalidDataException("Deflate64: invalid block length"); + } + } + + _state += 1; + break; + + case InflaterState.DecodingUncompressed: // copying block data + + // Directly copy bytes from input to output. + int bytesCopied = _output.CopyFrom(_input, _blockLength); + _blockLength -= bytesCopied; + + if (_blockLength == 0) + { + // Done with this block, need to re-init bit buffer for next block + _state = InflaterState.ReadingBFinal; + end_of_block = true; + return true; + } + + // We can fail to copy all bytes for two reasons: + // Running out of Input + // running out of free space in output window + if (_output.FreeBytes == 0) + { + return true; + } + + return false; + + default: + Debug./*Fail*/Assert(false, "check why we are here!"); + throw new InvalidDataException("Deflate64: unknown state"); + } + } + } + + private bool DecodeBlock(out bool end_of_block_code_seen) + { + end_of_block_code_seen = false; + + int freeBytes = _output.FreeBytes; // it is a little bit faster than frequently accessing the property + while (freeBytes > 65536) + { + // With Deflate64 we can have up to a 64kb length, so we ensure at least that much space is available + // in the OutputWindow to avoid overwriting previous unflushed output data. + + int symbol; + switch (_state) + { + case InflaterState.DecodeTop: + // decode an element from the literal tree + + // TODO: optimize this!!! + symbol = _literalLengthTree.GetNextSymbol(_input); + if (symbol < 0) + { + // running out of input + return false; + } + + if (symbol < 256) + { + // literal + _output.Write((byte)symbol); + --freeBytes; + } + else if (symbol == 256) + { + // end of block + end_of_block_code_seen = true; + // Reset state + _state = InflaterState.ReadingBFinal; + return true; + } + else + { + // length/distance pair + symbol -= 257; // length code started at 257 + if (symbol < 8) + { + symbol += 3; // match length = 3,4,5,6,7,8,9,10 + _extraBits = 0; + } + else if (!_deflate64 && symbol == 28) + { + // extra bits for code 285 is 0 + symbol = 258; // code 285 means length 258 + _extraBits = 0; + } + else + { + if (symbol < 0 || symbol >= s_extraLengthBits.Length) + { + throw new InvalidDataException("Deflate64: invalid data"); + } + _extraBits = s_extraLengthBits[symbol]; + Debug.Assert(_extraBits != 0, "We handle other cases separately!"); + } + _length = symbol; + goto case InflaterState.HaveInitialLength; + } + break; + + case InflaterState.HaveInitialLength: + if (_extraBits > 0) + { + _state = InflaterState.HaveInitialLength; + int bits = _input.GetBits(_extraBits); + if (bits < 0) + { + return false; + } + + if (_length < 0 || _length >= s_lengthBase.Length) + { + throw new InvalidDataException("Deflate64: invalid data"); + } + _length = s_lengthBase[_length] + bits; + } + _state = InflaterState.HaveFullLength; + goto case InflaterState.HaveFullLength; + + case InflaterState.HaveFullLength: + if (_blockType == BlockType.Dynamic) + { + _distanceCode = _distanceTree.GetNextSymbol(_input); + } + else + { + // get distance code directly for static block + _distanceCode = _input.GetBits(5); + if (_distanceCode >= 0) + { + _distanceCode = s_staticDistanceTreeTable[_distanceCode]; + } + } + + if (_distanceCode < 0) + { + // running out input + return false; + } + + _state = InflaterState.HaveDistCode; + goto case InflaterState.HaveDistCode; + + case InflaterState.HaveDistCode: + // To avoid a table lookup we note that for distanceCode > 3, + // extra_bits = (distanceCode-2) >> 1 + int offset; + if (_distanceCode > 3) + { + _extraBits = (_distanceCode - 2) >> 1; + int bits = _input.GetBits(_extraBits); + if (bits < 0) + { + return false; + } + offset = s_distanceBasePosition[_distanceCode] + bits; + } + else + { + offset = _distanceCode + 1; + } + + _output.WriteLengthDistance(_length, offset); + freeBytes -= _length; + _state = InflaterState.DecodeTop; + break; + + default: + Debug./*Fail*/Assert(false, "check why we are here!"); + throw new InvalidDataException("Deflate64: unknown state"); + } + } + + return true; + } + + + // Format of the dynamic block header: + // 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286) + // 5 Bits: HDIST, # of Distance codes - 1 (1 - 32) + // 4 Bits: HCLEN, # of Code Length codes - 4 (4 - 19) + // + // (HCLEN + 4) x 3 bits: code lengths for the code length + // alphabet given just above, in the order: 16, 17, 18, + // 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + // + // These code lengths are interpreted as 3-bit integers + // (0-7); as above, a code length of 0 means the + // corresponding symbol (literal/length or distance code + // length) is not used. + // + // HLIT + 257 code lengths for the literal/length alphabet, + // encoded using the code length Huffman code + // + // HDIST + 1 code lengths for the distance alphabet, + // encoded using the code length Huffman code + // + // The code length repeat codes can cross from HLIT + 257 to the + // HDIST + 1 code lengths. In other words, all code lengths form + // a single sequence of HLIT + HDIST + 258 values. + private bool DecodeDynamicBlockHeader() + { + switch (_state) + { + case InflaterState.ReadingNumLitCodes: + _literalLengthCodeCount = _input.GetBits(5); + if (_literalLengthCodeCount < 0) + { + return false; + } + _literalLengthCodeCount += 257; + _state = InflaterState.ReadingNumDistCodes; + goto case InflaterState.ReadingNumDistCodes; + + case InflaterState.ReadingNumDistCodes: + _distanceCodeCount = _input.GetBits(5); + if (_distanceCodeCount < 0) + { + return false; + } + _distanceCodeCount += 1; + _state = InflaterState.ReadingNumCodeLengthCodes; + goto case InflaterState.ReadingNumCodeLengthCodes; + + case InflaterState.ReadingNumCodeLengthCodes: + _codeLengthCodeCount = _input.GetBits(4); + if (_codeLengthCodeCount < 0) + { + return false; + } + _codeLengthCodeCount += 4; + _loopCounter = 0; + _state = InflaterState.ReadingCodeLengthCodes; + goto case InflaterState.ReadingCodeLengthCodes; + + case InflaterState.ReadingCodeLengthCodes: + while (_loopCounter < _codeLengthCodeCount) + { + int bits = _input.GetBits(3); + if (bits < 0) + { + return false; + } + _codeLengthTreeCodeLength[s_codeOrder[_loopCounter]] = (byte)bits; + ++_loopCounter; + } + + for (int i = _codeLengthCodeCount; i < s_codeOrder.Length; i++) + { + _codeLengthTreeCodeLength[s_codeOrder[i]] = 0; + } + + // create huffman tree for code length + _codeLengthTree = new HuffmanTree(_codeLengthTreeCodeLength); + _codeArraySize = _literalLengthCodeCount + _distanceCodeCount; + _loopCounter = 0; // reset loop count + + _state = InflaterState.ReadingTreeCodesBefore; + goto case InflaterState.ReadingTreeCodesBefore; + + case InflaterState.ReadingTreeCodesBefore: + case InflaterState.ReadingTreeCodesAfter: + while (_loopCounter < _codeArraySize) + { + if (_state == InflaterState.ReadingTreeCodesBefore) + { + if ((_lengthCode = _codeLengthTree.GetNextSymbol(_input)) < 0) + { + return false; + } + } + + // The alphabet for code lengths is as follows: + // 0 - 15: Represent code lengths of 0 - 15 + // 16: Copy the previous code length 3 - 6 times. + // The next 2 bits indicate repeat length + // (0 = 3, ... , 3 = 6) + // Example: Codes 8, 16 (+2 bits 11), + // 16 (+2 bits 10) will expand to + // 12 code lengths of 8 (1 + 6 + 5) + // 17: Repeat a code length of 0 for 3 - 10 times. + // (3 bits of length) + // 18: Repeat a code length of 0 for 11 - 138 times + // (7 bits of length) + if (_lengthCode <= 15) + { + _codeList[_loopCounter++] = (byte)_lengthCode; + } + else + { + int repeatCount; + if (_lengthCode == 16) + { + if (!_input.EnsureBitsAvailable(2)) + { + _state = InflaterState.ReadingTreeCodesAfter; + return false; + } + + if (_loopCounter == 0) + { + // can't have "prev code" on first code + throw new InvalidDataException(); + } + + byte previousCode = _codeList[_loopCounter - 1]; + repeatCount = _input.GetBits(2) + 3; + + if (_loopCounter + repeatCount > _codeArraySize) + { + throw new InvalidDataException(); + } + + for (int j = 0; j < repeatCount; j++) + { + _codeList[_loopCounter++] = previousCode; + } + } + else if (_lengthCode == 17) + { + if (!_input.EnsureBitsAvailable(3)) + { + _state = InflaterState.ReadingTreeCodesAfter; + return false; + } + + repeatCount = _input.GetBits(3) + 3; + + if (_loopCounter + repeatCount > _codeArraySize) + { + throw new InvalidDataException(); + } + + for (int j = 0; j < repeatCount; j++) + { + _codeList[_loopCounter++] = 0; + } + } + else + { + // code == 18 + if (!_input.EnsureBitsAvailable(7)) + { + _state = InflaterState.ReadingTreeCodesAfter; + return false; + } + + repeatCount = _input.GetBits(7) + 11; + + if (_loopCounter + repeatCount > _codeArraySize) + { + throw new InvalidDataException(); + } + + for (int j = 0; j < repeatCount; j++) + { + _codeList[_loopCounter++] = 0; + } + } + } + _state = InflaterState.ReadingTreeCodesBefore; // we want to read the next code. + } + break; + + default: + Debug./*Fail*/Assert(false, "check why we are here!"); + throw new InvalidDataException("Deflate64: unknown state"); + } + + byte[] literalTreeCodeLength = new byte[HuffmanTree.MaxLiteralTreeElements]; + byte[] distanceTreeCodeLength = new byte[HuffmanTree.MaxDistTreeElements]; + + // Create literal and distance tables + Array.Copy(_codeList, 0, literalTreeCodeLength, 0, _literalLengthCodeCount); + Array.Copy(_codeList, _literalLengthCodeCount, distanceTreeCodeLength, 0, _distanceCodeCount); + + // Make sure there is an end-of-block code, otherwise how could we ever end? + if (literalTreeCodeLength[HuffmanTree.EndOfBlockCode] == 0) + { + throw new InvalidDataException(); + } + + _literalLengthTree = new HuffmanTree(literalTreeCodeLength); + _distanceTree = new HuffmanTree(distanceTreeCodeLength); + _state = InflaterState.DecodeTop; + return true; + } + + public void Dispose() { } + } +} diff --git a/src/SharpCompress/Compressors/Deflate64/InflaterState.cs b/src/SharpCompress/Compressors/Deflate64/InflaterState.cs new file mode 100644 index 00000000..356ea88f --- /dev/null +++ b/src/SharpCompress/Compressors/Deflate64/InflaterState.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace SharpCompress.Compressors.Deflate64 +{ + // Do not rearrange the enum values. + internal enum InflaterState + { + ReadingHeader = 0, // Only applies to GZIP + + ReadingBFinal = 2, // About to read bfinal bit + ReadingBType = 3, // About to read blockType bits + + ReadingNumLitCodes = 4, // About to read # literal codes + ReadingNumDistCodes = 5, // About to read # dist codes + ReadingNumCodeLengthCodes = 6, // About to read # code length codes + ReadingCodeLengthCodes = 7, // In the middle of reading the code length codes + ReadingTreeCodesBefore = 8, // In the middle of reading tree codes (loop top) + ReadingTreeCodesAfter = 9, // In the middle of reading tree codes (extension; code > 15) + + DecodeTop = 10, // About to decode a literal (char/match) in a compressed block + HaveInitialLength = 11, // Decoding a match, have the literal code (base length) + HaveFullLength = 12, // Ditto, now have the full match length (incl. extra length bits) + HaveDistCode = 13, // Ditto, now have the distance code also, need extra dist bits + + /* uncompressed blocks */ + UncompressedAligning = 15, + UncompressedByte1 = 16, + UncompressedByte2 = 17, + UncompressedByte3 = 18, + UncompressedByte4 = 19, + DecodingUncompressed = 20, + + // These three apply only to GZIP + StartReadingFooter = 21, // (Initialisation for reading footer) + ReadingFooter = 22, + VerifyingFooter = 23, + + Done = 24 // Finished + } +} diff --git a/src/SharpCompress/Compressors/Deflate64/InputBuffer.cs b/src/SharpCompress/Compressors/Deflate64/InputBuffer.cs new file mode 100644 index 00000000..0f585388 --- /dev/null +++ b/src/SharpCompress/Compressors/Deflate64/InputBuffer.cs @@ -0,0 +1,202 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +namespace SharpCompress.Compressors.Deflate64 +{ + // This class can be used to read bits from an byte array quickly. + // Normally we get bits from 'bitBuffer' field and bitsInBuffer stores + // the number of bits available in 'BitBuffer'. + // When we used up the bits in bitBuffer, we will try to get byte from + // the byte array and copy the byte to appropiate position in bitBuffer. + // + // The byte array is not reused. We will go from 'start' to 'end'. + // When we reach the end, most read operations will return -1, + // which means we are running out of input. + + internal sealed class InputBuffer + { + private byte[] _buffer; // byte array to store input + private int _start; // start poisition of the buffer + private int _end; // end position of the buffer + private uint _bitBuffer = 0; // store the bits here, we can quickly shift in this buffer + private int _bitsInBuffer = 0; // number of bits available in bitBuffer + + /// Total bits available in the input buffer. + public int AvailableBits => _bitsInBuffer; + + /// Total bytes available in the input buffer. + public int AvailableBytes => (_end - _start) + (_bitsInBuffer / 8); + + /// Ensure that count bits are in the bit buffer. + /// Can be up to 16. + /// Returns false if input is not sufficient to make this true. + public bool EnsureBitsAvailable(int count) + { + Debug.Assert(0 < count && count <= 16, "count is invalid."); + + // manual inlining to improve perf + if (_bitsInBuffer < count) + { + if (NeedsInput()) + { + return false; + } + // insert a byte to bitbuffer + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; + + if (_bitsInBuffer < count) + { + if (NeedsInput()) + { + return false; + } + // insert a byte to bitbuffer + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; + } + } + + return true; + } + + /// + /// This function will try to load 16 or more bits into bitBuffer. + /// It returns whatever is contained in bitBuffer after loading. + /// The main difference between this and GetBits is that this will + /// never return -1. So the caller needs to check AvailableBits to + /// see how many bits are available. + /// + public uint TryLoad16Bits() + { + if (_bitsInBuffer < 8) + { + if (_start < _end) + { + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; + } + + if (_start < _end) + { + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; + } + } + else if (_bitsInBuffer < 16) + { + if (_start < _end) + { + _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer; + _bitsInBuffer += 8; + } + } + + return _bitBuffer; + } + + private uint GetBitMask(int count) => ((uint)1 << count) - 1; + + /// Gets count bits from the input buffer. Returns -1 if not enough bits available. + public int GetBits(int count) + { + Debug.Assert(0 < count && count <= 16, "count is invalid."); + + if (!EnsureBitsAvailable(count)) + { + return -1; + } + + int result = (int)(_bitBuffer & GetBitMask(count)); + _bitBuffer >>= count; + _bitsInBuffer -= count; + return result; + } + + /// + /// Copies length bytes from input buffer to output buffer starting at output[offset]. + /// You have to make sure, that the buffer is byte aligned. If not enough bytes are + /// available, copies fewer bytes. + /// + /// Returns the number of bytes copied, 0 if no byte is available. + public int CopyTo(byte[] output, int offset, int length) + { + Debug.Assert(output != null); + Debug.Assert(offset >= 0); + Debug.Assert(length >= 0); + Debug.Assert(offset <= output.Length - length); + Debug.Assert((_bitsInBuffer % 8) == 0); + + // Copy the bytes in bitBuffer first. + int bytesFromBitBuffer = 0; + while (_bitsInBuffer > 0 && length > 0) + { + output[offset++] = (byte)_bitBuffer; + _bitBuffer >>= 8; + _bitsInBuffer -= 8; + length--; + bytesFromBitBuffer++; + } + + if (length == 0) + { + return bytesFromBitBuffer; + } + + int avail = _end - _start; + if (length > avail) + { + length = avail; + } + + Array.Copy(_buffer, _start, output, offset, length); + _start += length; + return bytesFromBitBuffer + length; + } + + /// + /// Return true is all input bytes are used. + /// This means the caller can call SetInput to add more input. + /// + public bool NeedsInput() => _start == _end; + + /// + /// Set the byte array to be processed. + /// All the bits remained in bitBuffer will be processed before the new bytes. + /// We don't clone the byte array here since it is expensive. + /// The caller should make sure after a buffer is passed in. + /// It will not be changed before calling this function again. + /// + public void SetInput(byte[] buffer, int offset, int length) + { + Debug.Assert(buffer != null); + Debug.Assert(offset >= 0); + Debug.Assert(length >= 0); + Debug.Assert(offset <= buffer.Length - length); + Debug.Assert(_start == _end); + + _buffer = buffer; + _start = offset; + _end = offset + length; + } + + /// Skip n bits in the buffer. + public void SkipBits(int n) + { + Debug.Assert(_bitsInBuffer >= n, "No enough bits in the buffer, Did you call EnsureBitsAvailable?"); + _bitBuffer >>= n; + _bitsInBuffer -= n; + } + + /// Skips to the next byte boundary. + public void SkipToByteBoundary() + { + _bitBuffer >>= (_bitsInBuffer % 8); + _bitsInBuffer = _bitsInBuffer - (_bitsInBuffer % 8); + } + } +} diff --git a/src/SharpCompress/Compressors/Deflate64/Match.cs b/src/SharpCompress/Compressors/Deflate64/Match.cs new file mode 100644 index 00000000..4d5ce54b --- /dev/null +++ b/src/SharpCompress/Compressors/Deflate64/Match.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace SharpCompress.Compressors.Deflate64 +{ + /// + /// This class represents a match in the history window. + /// + internal sealed class Match + { + internal MatchState State { get; set; } + internal int Position { get; set; } + internal int Length { get; set; } + internal byte Symbol { get; set; } + } +} diff --git a/src/SharpCompress/Compressors/Deflate64/MatchState.cs b/src/SharpCompress/Compressors/Deflate64/MatchState.cs new file mode 100644 index 00000000..f88913bc --- /dev/null +++ b/src/SharpCompress/Compressors/Deflate64/MatchState.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace SharpCompress.Compressors.Deflate64 +{ + internal enum MatchState + { + HasSymbol = 1, + HasMatch = 2, + HasSymbolAndMatch = 3 + } +} diff --git a/src/SharpCompress/Compressors/Deflate64/OutputWindow.cs b/src/SharpCompress/Compressors/Deflate64/OutputWindow.cs new file mode 100644 index 00000000..b5889319 --- /dev/null +++ b/src/SharpCompress/Compressors/Deflate64/OutputWindow.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; + +namespace SharpCompress.Compressors.Deflate64 +{ + /// + /// This class maintains a window for decompressed output. + /// We need to keep this because the decompressed information can be + /// a literal or a length/distance pair. For length/distance pair, + /// we need to look back in the output window and copy bytes from there. + /// We use a byte array of WindowSize circularly. + /// + internal sealed class OutputWindow + { + // With Deflate64 we can have up to a 65536 length as well as up to a 65538 distance. This means we need a Window that is at + // least 131074 bytes long so we have space to retrieve up to a full 64kb in lookback and place it in our buffer without + // overwriting existing data. OutputWindow requires that the WindowSize be an exponent of 2, so we round up to 2^18. + private const int WindowSize = 262144; + private const int WindowMask = 262143; + + private readonly byte[] _window = new byte[WindowSize]; // The window is 2^18 bytes + private int _end; // this is the position to where we should write next byte + private int _bytesUsed; // The number of bytes in the output window which is not consumed. + + /// Add a byte to output window. + public void Write(byte b) + { + Debug.Assert(_bytesUsed < WindowSize, "Can't add byte when window is full!"); + _window[_end++] = b; + _end &= WindowMask; + ++_bytesUsed; + } + + public void WriteLengthDistance(int length, int distance) + { + Debug.Assert((_bytesUsed + length) <= WindowSize, "No Enough space"); + + // move backwards distance bytes in the output stream, + // and copy length bytes from this position to the output stream. + _bytesUsed += length; + int copyStart = (_end - distance) & WindowMask; // start position for coping. + + int border = WindowSize - length; + if (copyStart <= border && _end < border) + { + if (length <= distance) + { + Array.Copy(_window, copyStart, _window, _end, length); + _end += length; + } + else + { + // The referenced string may overlap the current + // position; for example, if the last 2 bytes decoded have values + // X and Y, a string reference with + // adds X,Y,X,Y,X to the output stream. + while (length-- > 0) + { + _window[_end++] = _window[copyStart++]; + } + } + } + else + { + // copy byte by byte + while (length-- > 0) + { + _window[_end++] = _window[copyStart++]; + _end &= WindowMask; + copyStart &= WindowMask; + } + } + } + + /// + /// Copy up to length of bytes from input directly. + /// This is used for uncompressed block. + /// + public int CopyFrom(InputBuffer input, int length) + { + length = Math.Min(Math.Min(length, WindowSize - _bytesUsed), input.AvailableBytes); + int copied; + + // We might need wrap around to copy all bytes. + int tailLen = WindowSize - _end; + if (length > tailLen) + { + // copy the first part + copied = input.CopyTo(_window, _end, tailLen); + if (copied == tailLen) + { + // only try to copy the second part if we have enough bytes in input + copied += input.CopyTo(_window, 0, length - tailLen); + } + } + else + { + // only one copy is needed if there is no wrap around. + copied = input.CopyTo(_window, _end, length); + } + + _end = (_end + copied) & WindowMask; + _bytesUsed += copied; + return copied; + } + + /// Free space in output window. + public int FreeBytes => WindowSize - _bytesUsed; + + /// Bytes not consumed in output window. + public int AvailableBytes => _bytesUsed; + + /// Copy the decompressed bytes to output array. + public int CopyTo(byte[] output, int offset, int length) + { + int copy_end; + + if (length > _bytesUsed) + { + // we can copy all the decompressed bytes out + copy_end = _end; + length = _bytesUsed; + } + else + { + copy_end = (_end - _bytesUsed + length) & WindowMask; // copy length of bytes + } + + int copied = length; + + int tailLen = length - copy_end; + if (tailLen > 0) + { + // this means we need to copy two parts separately + // copy tailLen bytes from the end of output window + Array.Copy(_window, WindowSize - tailLen, + output, offset, tailLen); + offset += tailLen; + length = copy_end; + } + Array.Copy(_window, copy_end - length, output, offset, length); + _bytesUsed -= copied; + Debug.Assert(_bytesUsed >= 0, "check this function and find why we copied more bytes than we have"); + return copied; + } + } +} diff --git a/tests/SharpCompress.Test/TestBase.cs b/tests/SharpCompress.Test/TestBase.cs index 4547ad76..4cf938ec 100644 --- a/tests/SharpCompress.Test/TestBase.cs +++ b/tests/SharpCompress.Test/TestBase.cs @@ -30,6 +30,7 @@ namespace SharpCompress.Test yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd-.zip"); yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd.zip"); yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip"); + yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate64.zip"); yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.dd.zip"); yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.zip"); yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.none.zip"); diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index 031698d8..a613abad 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -49,6 +49,11 @@ namespace SharpCompress.Test.Zip { ArchiveStreamRead("Zip.deflate.zip"); } + [Fact] + public void Zip_Deflate64_ArchiveStreamRead() + { + ArchiveStreamRead("Zip.deflate64.zip"); + } [Fact] public void Zip_LZMA_Streamed_ArchiveStreamRead() @@ -101,6 +106,11 @@ namespace SharpCompress.Test.Zip { ArchiveFileRead("Zip.deflate.zip"); } + [Fact] + public void Zip_Deflate64_ArchiveFileRead() + { + ArchiveFileRead("Zip.deflate64.zip"); + } [Fact] public void Zip_LZMA_Streamed_ArchiveFileRead() diff --git a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs index 4e264dcc..c1f7f701 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -101,6 +101,11 @@ namespace SharpCompress.Test.Zip { Read("Zip.deflate.zip", CompressionType.Deflate); } + [Fact] + public void Zip_Deflate64_Read() + { + Read("Zip.deflate64.zip", CompressionType.Deflate64); + } [Fact] public void Zip_LZMA_Streamed_Read() diff --git a/tests/TestArchives/Archives/Zip.deflate64.zip b/tests/TestArchives/Archives/Zip.deflate64.zip new file mode 100644 index 0000000000000000000000000000000000000000..2c2e89050361492e81b3404258348bf0a742c853 GIT binary patch literal 60747 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 Date: Thu, 1 Mar 2018 15:35:55 +0000 Subject: [PATCH 081/124] Remove netcoreapp1.x testing --- SharpCompress.sln.DotSettings | 8 ++++++++ build.cake | 13 ++----------- tests/SharpCompress.Test/SharpCompress.Test.csproj | 8 +------- tests/SharpCompress.Test/TestBase.cs | 6 +----- 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/SharpCompress.sln.DotSettings b/SharpCompress.sln.DotSettings index a3a163da..5b016930 100644 --- a/SharpCompress.sln.DotSettings +++ b/SharpCompress.sln.DotSettings @@ -45,10 +45,14 @@ True 1 1 + NEVER + NEVER False False + NEVER True False + NEVER True True @@ -115,6 +119,10 @@ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> True + True + True + True True + True True True diff --git a/build.cake b/build.cake index c92639cc..3fc65799 100644 --- a/build.cake +++ b/build.cake @@ -25,7 +25,8 @@ Task("Build") var settings = new DotNetCoreBuildSettings { Framework = "netstandard1.0", - Configuration = "Release" + Configuration = "Release", + NoRestore = true }; DotNetCoreBuild("./src/SharpCompress/SharpCompress.csproj", settings); @@ -50,16 +51,6 @@ Task("Test") Configuration = "Release", Framework = "netcoreapp2.0" }; - - DotNetCoreTest(file.ToString(), settings); - - - settings = new DotNetCoreTestSettings - { - Configuration = "Release", - Framework = "netcoreapp1.1" - }; - DotNetCoreTest(file.ToString(), settings); } }); diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index 8dbcecb1..a79fe35d 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -1,6 +1,6 @@  - netcoreapp1.1;netcoreapp2.0 + netcoreapp2.0 SharpCompress.Test ../../SharpCompress.snk true @@ -16,10 +16,4 @@ - - - - - - \ No newline at end of file diff --git a/tests/SharpCompress.Test/TestBase.cs b/tests/SharpCompress.Test/TestBase.cs index 4547ad76..423535d2 100644 --- a/tests/SharpCompress.Test/TestBase.cs +++ b/tests/SharpCompress.Test/TestBase.cs @@ -241,13 +241,9 @@ namespace SharpCompress.Test { Monitor.Enter(lockObject); -#if NETSTANDARD20 var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf("SharpCompress.Test", StringComparison.OrdinalIgnoreCase); SOLUTION_BASE_PATH = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory.Substring(0, index)); -#else - var index = Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationBasePath.IndexOf("SharpCompress.Test", StringComparison.OrdinalIgnoreCase); - SOLUTION_BASE_PATH = Path.GetDirectoryName(Microsoft.Extensions.PlatformAbstractions.PlatformServices.Default.Application.ApplicationBasePath.Substring(0, index)); -#endif + TEST_ARCHIVES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Archives"); ORIGINAL_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Original"); MISC_TEST_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "MiscTest"); From 750c1fb0690a3141a8b148d8263422476b3f42c2 Mon Sep 17 00:00:00 2001 From: PrettierCI Date: Thu, 15 Mar 2018 08:55:06 +0000 Subject: [PATCH 082/124] Sync with Prettier --- FORMATS.md | 71 +++++++++++++++++++++++++++--------------------------- README.md | 27 +++++++++++---------- USAGE.md | 9 +++---- 3 files changed, 53 insertions(+), 54 deletions(-) diff --git a/FORMATS.md b/FORMATS.md index 2b1d3b34..972c9c88 100644 --- a/FORMATS.md +++ b/FORMATS.md @@ -2,48 +2,48 @@ ## Accessing Archives -- Archive classes allow random access to a seekable stream. -- Reader classes allow forward-only reading on a stream. -- Writer classes allow forward-only Writing on a stream. +* Archive classes allow random access to a seekable stream. +* Reader classes allow forward-only reading on a stream. +* Writer classes allow forward-only Writing on a stream. ## Supported Format Table -| 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 | -| Tar | None | Both | TarArchive | TarReader | TarWriter (3) | -| Tar.GZip | DEFLATE | Both | TarArchive | TarReader | TarWriter (3) | -| Tar.BZip2 | BZip2 | Both | TarArchive | TarReader | TarWriter (3) | -| Tar.LZip | LZMA | Both | TarArchive | TarReader | TarWriter (3) | -| Tar.XZ | LZMA2 | Decompress | TarArchive | TarReader | TarWriter (3) | -| GZip (single file) | DEFLATE | Both | GZipArchive | GZipReader | GZipWriter | -| 7Zip (4) | LZMA, LZMA2, BZip2, PPMd, BCJ, BCJ2, Deflate | Decompress | SevenZipArchive | N/A | N/A | -| LZip (single file) (5) | LZip (LZMA) | Both | LZipArchive | LZipReader | LZipWriter | +| 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 | +| Tar | None | Both | TarArchive | TarReader | TarWriter (3) | +| Tar.GZip | DEFLATE | Both | TarArchive | TarReader | TarWriter (3) | +| Tar.BZip2 | BZip2 | Both | TarArchive | TarReader | TarWriter (3) | +| Tar.LZip | LZMA | Both | TarArchive | TarReader | TarWriter (3) | +| Tar.XZ | LZMA2 | Decompress | TarArchive | TarReader | TarWriter (3) | +| GZip (single file) | DEFLATE | Both | GZipArchive | GZipReader | GZipWriter | +| 7Zip (4) | LZMA, LZMA2, BZip2, PPMd, BCJ, BCJ2, Deflate | Decompress | SevenZipArchive | N/A | N/A | +| LZip (single file) (5) | LZip (LZMA) | Both | LZipArchive | LZipReader | LZipWriter | - 1. SOLID Rars are only supported in the RarReader API. - 2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading/writing is supported but only with seekable streams as the Zip spec doesn't support Zip64 data in post data descriptors. Deflate64 is only supported for reading. - 3. The Tar format requires a file size in the header. If no size is specified to the TarWriter and the stream is not seekable, then an exception will be thrown. - 4. The 7Zip format doesn't allow for reading as a forward-only stream so 7Zip is only supported through the Archive API - 5. LZip has no support for extra data like the file name or timestamp. There is a default filename used when looking at the entry Key on the archive. +1. SOLID Rars are only supported in the RarReader API. +2. Zip format supports pkware and WinzipAES encryption. However, encrypted LZMA is not supported. Zip64 reading/writing is supported but only with seekable streams as the Zip spec doesn't support Zip64 data in post data descriptors. Deflate64 is only supported for reading. +3. The Tar format requires a file size in the header. If no size is specified to the TarWriter and the stream is not seekable, then an exception will be thrown. +4. The 7Zip format doesn't allow for reading as a forward-only stream so 7Zip is only supported through the Archive API +5. LZip has no support for extra data like the file name or timestamp. There is a default filename used when looking at the entry Key on the archive. ## Compression Streams -For those who want to directly compress/decompress bits. The single file formats are represented here as well. However, BZip2, LZip and XZ have no metadata (GZip has a little) so using them without something like a Tar file makes little sense. +For those who want to directly compress/decompress bits. The single file formats are represented here as well. However, BZip2, LZip and XZ have no metadata (GZip has a little) so using them without something like a Tar file makes little sense. -| Compressor | Compress/Decompress | -| --- | --- | -| BZip2Stream | Both | -| GZipStream | Both | -| DeflateStream | Both | -| Deflate64Stream | Decompress | -| LZMAStream | Both | -| PPMdStream | Both | -| ADCStream | Decompress | -| LZipStream | Both | -| XZStream | Decompress | +| Compressor | Compress/Decompress | +| --------------- | ------------------- | +| BZip2Stream | Both | +| GZipStream | Both | +| DeflateStream | Both | +| Deflate64Stream | Decompress | +| LZMAStream | Both | +| PPMdStream | Both | +| ADCStream | Decompress | +| LZipStream | Both | +| XZStream | Decompress | -## Archive Formats vs Compression +## Archive Formats vs Compression Sometimes the terminology gets mixed. @@ -53,9 +53,8 @@ DEFLATE, LZMA are pure compression algorithms ### Formats -Formats like Zip, 7Zip, Rar are archive formats only. They use other compression methods (e.g. DEFLATE, LZMA, etc.) or propriatory (e.g RAR) +Formats like Zip, 7Zip, Rar are archive formats only. They use other compression methods (e.g. DEFLATE, LZMA, etc.) or propriatory (e.g RAR) ### Overlap -GZip, BZip2 and LZip are single file archival formats. The overlap in the API happens because Tar uses the single file formats as "compression" methods and the API tries to hide this a bit. - +GZip, BZip2 and LZip are single file archival formats. The overlap in the API happens because Tar uses the single file formats as "compression" methods and the API tries to hide this a bit. diff --git a/README.md b/README.md index ae23f57a..b3f04c30 100644 --- a/README.md +++ b/README.md @@ -2,34 +2,35 @@ SharpCompress is a compression library in pure C# for .NET 3.5, 4.5, .NET Standard 1.0, 1.3 that can unrar, un7zip, unzip, untar unbzip2 and ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip are implemented. -The major feature is support for non-seekable streams so large files can be processed on the fly (i.e. download stream). +The major feature is support for non-seekable streams so large files can be processed on the fly (i.e. download stream). -AppVeyor Build - +AppVeyor Build - [![Build status](https://ci.appveyor.com/api/projects/status/voxg971oemmvxh1e/branch/master?svg=true)](https://ci.appveyor.com/project/adamhathcock/sharpcompress/branch/master) -Circle CI Build - +Circle CI Build - [![CircleCI](https://circleci.com/gh/adamhathcock/sharpcompress.svg?style=svg)](https://circleci.com/gh/adamhathcock/sharpcompress) ## Need Help? + Post Issues on Github! Check the [Supported Formats](FORMATS.md) and [Basic Usage.](USAGE.md) ## Recommended Formats -In general, I recommend GZip (Deflate)/BZip2 (BZip)/LZip (LZMA) as the simplicity of the formats lend to better long term archival as well as the streamability. Tar is often used in conjunction for multiple files in a single archive (e.g. `.tar.gz`) +In general, I recommend GZip (Deflate)/BZip2 (BZip)/LZip (LZMA) as the simplicity of the formats lend to better long term archival as well as the streamability. Tar is often used in conjunction for multiple files in a single archive (e.g. `.tar.gz`) -Zip is okay, but it's a very hap-hazard format and the variation in headers and implementations makes it hard to get correct. Uses Deflate by default but supports a lot of compression methods. +Zip is okay, but it's a very hap-hazard format and the variation in headers and implementations makes it hard to get correct. Uses Deflate by default but supports a lot of compression methods. -RAR is not recommended as it's a propriatory format and the compression is closed source. Use Tar/LZip for LZMA +RAR is not recommended as it's a propriatory format and the compression is closed source. Use Tar/LZip for LZMA -7Zip and XZ both are overly complicated. 7Zip does not support streamable formats. XZ has known holes explained here: (http://www.nongnu.org/lzip/xz_inadequate.html) Use Tar/LZip for LZMA compression instead. +7Zip and XZ both are overly complicated. 7Zip does not support streamable formats. XZ has known holes explained here: (http://www.nongnu.org/lzip/xz_inadequate.html) Use Tar/LZip for LZMA compression instead. ## A Simple Request Hi everyone. I hope you're using SharpCompress and finding it useful. Please give me feedback on what you'd like to see changed especially as far as usability goes. New feature suggestions are always welcome as well. I would also like to know what projects SharpCompress is being used in. I like seeing how it is used to give me ideas for future versions. Thanks! -Please do not email me directly to ask for help. If you think there is a real issue, please report it here. +Please do not email me directly to ask for help. If you think there is a real issue, please report it here. ## Want to contribute? @@ -54,9 +55,9 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un ### Version 0.17.0 -* New - Full LZip support! Can read and write LZip files and Tars inside LZip files. [Make LZip a first class citizen. #241](https://github.com/adamhathcock/sharpcompress/issues/241) -* New - XZ read support! Can read XZ files and Tars inside XZ files. [XZ in SharpCompress #91](https://github.com/adamhathcock/sharpcompress/issues/94) -* Fix - [Regression - zip file writing on seekable streams always assumed stream start was 0. Introduced with Zip64 writing.](https://github.com/adamhathcock/sharpcompress/issues/244) +* New - Full LZip support! Can read and write LZip files and Tars inside LZip files. [Make LZip a first class citizen. #241](https://github.com/adamhathcock/sharpcompress/issues/241) +* New - XZ read support! Can read XZ files and Tars inside XZ files. [XZ in SharpCompress #91](https://github.com/adamhathcock/sharpcompress/issues/94) +* Fix - [Regression - zip file writing on seekable streams always assumed stream start was 0. Introduced with Zip64 writing.](https://github.com/adamhathcock/sharpcompress/issues/244) * Fix - [Zip files with post-data descriptors can be properly skipped via decompression](https://github.com/adamhathcock/sharpcompress/issues/162) ### Version 0.16.2 @@ -136,7 +137,7 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un ### Version 0.11.6 * Bug fix for global header in Tar -* Writers now have a leaveOpen `bool` overload. They won't close streams if not-requested to. +* Writers now have a leaveOpen `bool` overload. They won't close streams if not-requested to. ### Version 0.11.5 @@ -155,7 +156,7 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un ### Version 0.11 -* Been over a year, contains mainly fixes from contributors! +* Been over a year, contains mainly fixes from contributors! * Possible breaking change: ArchiveEncoding is UTF8 by default now. * TAR supports writing long names using longlink * RAR Protect Header added diff --git a/USAGE.md b/USAGE.md index 4d612aa4..8470ca1f 100644 --- a/USAGE.md +++ b/USAGE.md @@ -1,7 +1,8 @@ # SharpCompress Usage ## Stream Rules -When dealing with Streams, the rule should be that you don't close a stream you didn't create. This, in effect, should mean you should always put a Stream in a using block to dispose it. + +When dealing with Streams, the rule should be that you don't close a stream you didn't create. This, in effect, should mean you should always put a Stream in a using block to dispose it. However, the .NET Framework often has classes that will dispose streams by default to make things "easy" like the following: @@ -12,7 +13,7 @@ using (var reader = new StreamReader(File.Open("foo"))) } ``` -In this example, reader should get disposed. However, stream rules should say the the `FileStream` created by `File.Open` should remain open. However, the .NET Framework closes it for you by default unless you override the constructor. In general, you should be writing Stream code like this: +In this example, reader should get disposed. However, stream rules should say the the `FileStream` created by `File.Open` should remain open. However, the .NET Framework closes it for you by default unless you override the constructor. In general, you should be writing Stream code like this: ```C# using (var fileStream = File.Open("foo")) @@ -25,7 +26,7 @@ using (var reader = new StreamReader(fileStream)) To deal with the "correct" rules as well as the expectations of users, I've decided on this: * When writing, leave streams open. -* When reading, close streams +* When reading, close streams To be explicit though, consider always using the overloads that use `ReaderOptions` or `WriterOptions` and explicitly set `LeaveStreamOpen` the way you want. @@ -43,11 +44,9 @@ using (var archive = ZipArchive.Create()) } ``` - ### Create Zip Archive from all files in a directory and save in memory ```C# - var memoryStream = new MemoryStream(); using (var archive = ZipArchive.Create()) { From 0cab9bd4b4be89aee76838a6af1cd41082f80e0b Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 24 Mar 2018 07:42:20 +0000 Subject: [PATCH 083/124] Mark for 0.20.0 --- src/SharpCompress/SharpCompress.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index cb2c3144..5c1d0815 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -2,9 +2,9 @@ SharpCompress - Pure C# Decompression/Compression en-US - 0.19.2 - 0.19.2.0 - 0.19.2.0 + 0.20.0 + 0.20.0 + 0.20.0 Adam Hathcock net45;net35;netstandard1.0;netstandard1.3;netstandard2.0 true From 04ba6c2d735d7c69c18812d014a49645ca3f143e Mon Sep 17 00:00:00 2001 From: "srperias@microsoft.com" Date: Tue, 27 Mar 2018 13:54:16 -0700 Subject: [PATCH 084/124] ZipArchive Reader: Uses IBM PC character encoding to decode filename and comment if the general purpose bit 11 is not set in the header --- .../Common/Zip/Headers/DirectoryEntryHeader.cs | 6 ++++++ src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs index e7502d26..b1c4860a 100644 --- a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Text; namespace SharpCompress.Common.Zip.Headers { @@ -16,6 +17,11 @@ namespace SharpCompress.Common.Zip.Headers Version = reader.ReadUInt16(); VersionNeededToExtract = reader.ReadUInt16(); Flags = (HeaderFlags)reader.ReadUInt16(); + if ((Flags & HeaderFlags.UTF8) == 0) + { + // IBM Code Page 437 + ArchiveEncoding.Default = Encoding.GetEncoding("iso-8859-1"); + } CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16(); LastModifiedTime = reader.ReadUInt16(); LastModifiedDate = reader.ReadUInt16(); diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs index 2b89385b..eaf0f62c 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs @@ -15,6 +15,11 @@ namespace SharpCompress.Common.Zip.Headers { Version = reader.ReadUInt16(); Flags = (HeaderFlags)reader.ReadUInt16(); + if ((Flags & HeaderFlags.UTF8) == 0) + { + // IBM Code Page 437 + ArchiveEncoding.Default = Encoding.GetEncoding("iso-8859-1"); + } CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16(); LastModifiedTime = reader.ReadUInt16(); LastModifiedDate = reader.ReadUInt16(); From 5d8728d5923276bfc98575cc0810f500178a521e Mon Sep 17 00:00:00 2001 From: "srperias@microsoft.com" Date: Wed, 28 Mar 2018 13:12:54 -0700 Subject: [PATCH 085/124] Decode without setting the default Encoding type --- .../Zip/Headers/DirectoryEntryHeader.cs | 20 ++++++++++++------- .../Common/Zip/Headers/LocalEntryHeader.cs | 17 ++++++++++------ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs index b1c4860a..6c855a28 100644 --- a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs @@ -17,11 +17,6 @@ namespace SharpCompress.Common.Zip.Headers Version = reader.ReadUInt16(); VersionNeededToExtract = reader.ReadUInt16(); Flags = (HeaderFlags)reader.ReadUInt16(); - if ((Flags & HeaderFlags.UTF8) == 0) - { - // IBM Code Page 437 - ArchiveEncoding.Default = Encoding.GetEncoding("iso-8859-1"); - } CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16(); LastModifiedTime = reader.ReadUInt16(); LastModifiedDate = reader.ReadUInt16(); @@ -37,10 +32,21 @@ namespace SharpCompress.Common.Zip.Headers RelativeOffsetOfEntryHeader = reader.ReadUInt32(); byte[] name = reader.ReadBytes(nameLength); - Name = ArchiveEncoding.Decode(name); byte[] extra = reader.ReadBytes(extraLength); byte[] comment = reader.ReadBytes(commentLength); - Comment = ArchiveEncoding.Decode(comment); + if ((Flags & HeaderFlags.UTF8) == 0) + { + // Use IBM Code Page 437 (IBM PC character encoding set) + var extendedASCIIEncoding = Encoding.GetEncoding("IBM437"); + Name = extendedASCIIEncoding.GetString(name, 0, name.Length); + Comment = extendedASCIIEncoding.GetString(comment, 0, comment.Length); + } + else + { + Name = ArchiveEncoding.Decode(name); + Comment = ArchiveEncoding.Decode(comment); + } + LoadExtra(extra); var unicodePathExtra = Extra.FirstOrDefault(u => u.Type == ExtraDataType.UnicodePathExtraField); diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs index eaf0f62c..01f3a63e 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs @@ -15,11 +15,6 @@ namespace SharpCompress.Common.Zip.Headers { Version = reader.ReadUInt16(); Flags = (HeaderFlags)reader.ReadUInt16(); - if ((Flags & HeaderFlags.UTF8) == 0) - { - // IBM Code Page 437 - ArchiveEncoding.Default = Encoding.GetEncoding("iso-8859-1"); - } CompressionMethod = (ZipCompressionMethod)reader.ReadUInt16(); LastModifiedTime = reader.ReadUInt16(); LastModifiedDate = reader.ReadUInt16(); @@ -30,7 +25,17 @@ namespace SharpCompress.Common.Zip.Headers ushort extraLength = reader.ReadUInt16(); byte[] name = reader.ReadBytes(nameLength); byte[] extra = reader.ReadBytes(extraLength); - Name = ArchiveEncoding.Decode(name); + if ((Flags & HeaderFlags.UTF8) == 0) + { + // Use IBM Code Page 437 (IBM PC character encoding set) + var extendedASCIIEncoding = Encoding.GetEncoding("IBM437"); + Name = extendedASCIIEncoding.GetString(name, 0, name.Length); + } + else + { + Name = ArchiveEncoding.Decode(name); + } + LoadExtra(extra); var unicodePathExtra = Extra.FirstOrDefault(u => u.Type == ExtraDataType.UnicodePathExtraField); From b960f2e5ba83569c4a20400d5766c500718491f1 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 22 Apr 2018 09:17:03 +0100 Subject: [PATCH 086/124] Minor build updates --- .circleci/config.yml | 2 +- tests/SharpCompress.Test/SharpCompress.Test.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a1fa617c..f9bd67fe 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: microsoft/dotnet:2.0.5-sdk-2.1.4 + - image: microsoft/dotnet:2.0.7-sdk-2.1.105 steps: - checkout - run: diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index a79fe35d..5635735e 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -12,7 +12,7 @@ - + From e919c99b14b0e0e333afa91d7d55965f72edb679 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 22 Apr 2018 10:02:18 +0100 Subject: [PATCH 087/124] First pass of removing explicit leaveOpen on streams. --- src/SharpCompress/Common/GZip/GZipFilePart.cs | 2 +- .../Common/Rar/Headers/RarHeaderFactory.cs | 7 ++---- .../Common/SevenZip/SevenZipFilePart.cs | 1 - src/SharpCompress/Common/Volume.cs | 2 +- src/SharpCompress/Common/Zip/ZipFilePart.cs | 2 +- .../Compressors/BZip2/BZip2Stream.cs | 11 ++++---- .../Compressors/BZip2/CBZip2InputStream.cs | 25 ++++--------------- .../Compressors/BZip2/CBZip2OutputStream.cs | 20 ++++----------- .../Compressors/Deflate/DeflateStream.cs | 8 +++--- .../Compressors/Deflate/GZipStream.cs | 14 +++-------- .../Compressors/Deflate/ZlibBaseStream.cs | 8 +----- .../Compressors/Deflate/ZlibStream.cs | 18 +++++-------- .../Compressors/Deflate64/Deflate64Stream.cs | 20 +++++++-------- .../Compressors/LZMA/LZipStream.cs | 9 ++----- src/SharpCompress/IO/NonDisposingStream.cs | 13 +++++++--- src/SharpCompress/Readers/ReaderFactory.cs | 4 +-- src/SharpCompress/Readers/Tar/TarReader.cs | 2 +- src/SharpCompress/Writers/GZip/GZipWriter.cs | 8 ++++-- src/SharpCompress/Writers/Tar/TarWriter.cs | 11 +++++--- src/SharpCompress/Writers/Zip/ZipWriter.cs | 10 +++++--- tests/SharpCompress.Test/ArchiveTests.cs | 7 ++++-- 21 files changed, 84 insertions(+), 118 deletions(-) diff --git a/src/SharpCompress/Common/GZip/GZipFilePart.cs b/src/SharpCompress/Common/GZip/GZipFilePart.cs index 30cc20e9..2aecd3ff 100644 --- a/src/SharpCompress/Common/GZip/GZipFilePart.cs +++ b/src/SharpCompress/Common/GZip/GZipFilePart.cs @@ -30,7 +30,7 @@ namespace SharpCompress.Common.GZip internal override Stream GetCompressedStream() { - return new DeflateStream(_stream, CompressionMode.Decompress, CompressionLevel.Default, false); + return new DeflateStream(_stream, CompressionMode.Decompress, CompressionLevel.Default); } internal override Stream GetRawStream() diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index d05840ee..f3b67960 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -90,14 +90,11 @@ namespace SharpCompress.Common.Rar.Headers } catch (Exception e) { - if (!Options.LeaveStreamOpen) - { #if NET35 - reader.Close(); + reader.Close(); #else - reader.Dispose(); + reader.Dispose(); #endif - } throw new InvalidFormatException("Error trying to read rar signature.", e); } return stream; diff --git a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs index ea42d174..97c5e8ad 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs @@ -24,7 +24,6 @@ namespace SharpCompress.Common.SevenZip } } - internal Stream BaseStream { get; private set; } internal CFileItem Header { get; } internal CFolder Folder { get; } internal int Index { get; } diff --git a/src/SharpCompress/Common/Volume.cs b/src/SharpCompress/Common/Volume.cs index 87b0d856..93cbfefa 100644 --- a/src/SharpCompress/Common/Volume.cs +++ b/src/SharpCompress/Common/Volume.cs @@ -33,7 +33,7 @@ namespace SharpCompress.Common public void Dispose() { - if (!ReaderOptions.LeaveStreamOpen && !disposed) + if (!disposed) { actualStream.Dispose(); disposed = true; diff --git a/src/SharpCompress/Common/Zip/ZipFilePart.cs b/src/SharpCompress/Common/Zip/ZipFilePart.cs index 9b2e74ca..f68a1f6f 100644 --- a/src/SharpCompress/Common/Zip/ZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/ZipFilePart.cs @@ -73,7 +73,7 @@ namespace SharpCompress.Common.Zip } case ZipCompressionMethod.BZip2: { - return new BZip2Stream(stream, CompressionMode.Decompress); + return new BZip2Stream(stream, CompressionMode.Decompress, false); } case ZipCompressionMethod.LZMA: { diff --git a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs index 590313d3..4566bf03 100644 --- a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs +++ b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs @@ -12,19 +12,18 @@ namespace SharpCompress.Compressors.BZip2 /// /// The stream to read from /// Compression Mode - /// Leave the underlying stream open when disposed. - /// Should the BZip2 stream continue to decompress the stream when the End Marker is found. - public BZip2Stream(Stream stream, CompressionMode compressionMode, bool leaveOpen = false, - bool decompressContacted = false) + /// Decompress Concatenated + public BZip2Stream(Stream stream, CompressionMode compressionMode, + bool decompressConcatenated) { Mode = compressionMode; if (Mode == CompressionMode.Compress) { - this.stream = new CBZip2OutputStream(stream, leaveOpen); + this.stream = new CBZip2OutputStream(stream); } else { - this.stream = new CBZip2InputStream(stream, decompressContacted, leaveOpen); + this.stream = new CBZip2InputStream(stream, decompressConcatenated); } } diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs index 0656801e..4168db4b 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs @@ -119,7 +119,6 @@ namespace SharpCompress.Compressors.BZip2 private readonly int[] minLens = new int[BZip2Constants.N_GROUPS]; private Stream bsStream; - private bool leaveOpen; private bool streamEnd; @@ -147,12 +146,12 @@ namespace SharpCompress.Compressors.BZip2 private char z; private bool isDisposed; - public CBZip2InputStream(Stream zStream, bool decompressConcatenated, bool leaveOpen) + public CBZip2InputStream(Stream zStream, bool decompressConcatenated) { this.decompressConcatenated = decompressConcatenated; ll8 = null; tt = null; - BsSetStream(zStream, leaveOpen); + BsSetStream(zStream); Initialize(true); InitBlock(); SetupBlock(); @@ -354,29 +353,15 @@ namespace SharpCompress.Compressors.BZip2 private void BsFinishedWithStream() { - try - { - if (bsStream != null) - { - if (!leaveOpen) - { - bsStream.Dispose(); - } - bsStream = null; - } - } - catch - { - //ignore - } + bsStream?.Dispose(); + bsStream = null; } - private void BsSetStream(Stream f, bool leaveOpen) + private void BsSetStream(Stream f) { bsStream = f; bsLive = 0; bsBuff = 0; - this.leaveOpen = leaveOpen; } private int BsR(int n) diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs index 9f4817cc..38922294 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs @@ -303,16 +303,11 @@ namespace SharpCompress.Compressors.BZip2 private int runLength; public CBZip2OutputStream(Stream inStream) - : this(inStream, 9, false) + : this(inStream, 9) { } - public CBZip2OutputStream(Stream inStream, bool leaveOpen) - : this(inStream, 9, leaveOpen) - { - } - - public CBZip2OutputStream(Stream inStream, int inBlockSize, bool leaveOpen) + public CBZip2OutputStream(Stream inStream, int inBlockSize) { block = null; quadrant = null; @@ -322,7 +317,7 @@ namespace SharpCompress.Compressors.BZip2 inStream.WriteByte((byte)'B'); inStream.WriteByte((byte)'Z'); - BsSetStream(inStream, leaveOpen); + BsSetStream(inStream); workFactor = 50; if (inBlockSize > 9) @@ -441,10 +436,7 @@ namespace SharpCompress.Compressors.BZip2 disposed = true; base.Dispose(); - if (!leaveOpen) - { - bsStream.Dispose(); - } + bsStream?.Dispose(); bsStream = null; } } @@ -596,13 +588,12 @@ namespace SharpCompress.Compressors.BZip2 } } - private void BsSetStream(Stream f, bool leaveOpen) + private void BsSetStream(Stream f) { bsStream = f; bsLive = 0; bsBuff = 0; bytesOut = 0; - this.leaveOpen = leaveOpen; } private void BsFinishedWithStream() @@ -1057,7 +1048,6 @@ namespace SharpCompress.Compressors.BZip2 } private Stream bsStream; - private bool leaveOpen; private void SimpleSort(int lo, int hi, int d) { diff --git a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs index d041533e..a771d66e 100644 --- a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs +++ b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs @@ -37,10 +37,9 @@ namespace SharpCompress.Compressors.Deflate public DeflateStream(Stream stream, CompressionMode mode, CompressionLevel level = CompressionLevel.Default, - bool leaveOpen = false, Encoding forceEncoding = null) { - _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen, forceEncoding); + _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, forceEncoding); } #region Zlib properties @@ -216,7 +215,6 @@ namespace SharpCompress.Compressors.Deflate ///

_dktVX!kB&v&CFqFUU}fZ)Mg$&!Sg5?y^tvu$wW}s;bU7=j{BbCzcieH)S^3 zy>&fl7kh<&g|A+6)s6>&xAm5;bG-FCRzV%gO(iKjGr* z+BQexv6KH!C-MGOVwWBXE$@0!R2FoFYN>HFBIk|DhFMRF{x8eA z{oifAcbfDQlRe31-E!X?&bRiOe!nlkY?{+>W6ssxuOEc``m?UZuk2aF&k3{M z9x1-j-0$*z>G}00YXh`?|0zsoK4u>|-TgW<`?*&iEhk-d+x&Wd5!*%Gm@5x1-)$59 zqtwIl{KvJ6&);0jOMQN<`?O3(w-o<7zmoI&I}>}otyOX++-BjJd2X7I@q=wvia~|C z|6XkEn(UyR)tN7P`_A2viMC~7M_1o|8}v5$Z}Q3Wx8@yM^u#ANE~l+qzwW+})xp*0 zQWY2%xOGZfA9{Ah=so-UjuX539YiG_#n*Ism3Rf${ag|&I@wgW=pM6bpUatMVCdDh>b^QHY_r0C_dcb$F|7yVc#mFk%nd)=^xDCc zb%Tq`za#Gok7{O_YgS1b-b~>?T^-W!`?&Xol#U;Ey1p!7noqYjdY?a_p!V~{Zk033 z9n-ovoSV6U4besO zL^Jp0{M3?63b$fzc9bJ%VmDs+)O$^M*Ox@`3*6z_SmLvKe^_4NPGug5P4jjlSL zyZ3qS0mJs7Mr9L+OGlp=eZPD2%dAHMi^^yJ-(zS0S6#l%F`WJVvb2zuB9pX@9?v-! z;?W|x;)+d3)Wa*1Z%^OHx^vJO34*82tU#(d{fbNqAD&QV61afL(cjrF{nr^qe+$zoaikX2>knux1*;d39Fbp870RDSkR zmf_p>%e+_4UKTifEGFr|0sGR4I$Wu;U*{*f&M5NyeJzRG{`@srt;_K%b3SfS?Z z+nj4&`DIzbbq@n&)_Bglx%5ll;lnTUHjAB~w8`k%Pd&Y3`ctFK3K@ghrkaFX3rS8t z<9hL_5{qF7*MXpg@1M9-{d(EEVDk6g{1pLSe%5dL7B?@9ye?dJ_V2geH;rY={|vHc zU)mD2yFST9C`)@c+P4^^MBk(w-c41?;unebSnoPnQp&*ub)g`X4-)x$CCprF~t<33HSHDlYlpwR_l2i07mEFgu z*q+LXdeU1uCnR&xz4pfi4lC|VTGlrCqQH@b0ZCdbeb!8|eG?FqUbW+)@uU8$3^QJy z*j5xe@9O~>&lV*|ze}H{Y>?l&LfWUE zn(rCr`wVNpwdzZ+_&?=#@w&Pa>lbrwIECD~GXLPt_pusTxsu6e3AxCwvyel3@Lu{ldlNnUoD&cbBQH~I6;HeHs^ zPM>5e)x@5{eQQcC+l~{b7&C>8=f8T;>ZNZYV3%I=AD-qL4MUgm+32Y=q+?|7<_-gvrB)_2jOv+ZmO zmqS_pgnGW!dD`zRk?^2%Z{pd2y={*f-<6iFXK`OJ@mh6=Weao&k(XvCEonpG7HwA{(l-) z=Z`39m3H=Tmy0~Cyo;T4-S)Q14Xvqy@;#o6Kg&5{k_EDs-xS`paT)vVt-tQAzM3O6 zRdI8_QzTpyg(7c^_!G0d|yI)N?IOWCb zf`XZ=O|sN8XC4d{tyg`1D`9rY+zUq_XAh#d#YQcRk_LNgP_z>?(n^Lw2v)pXi_udsAPTJ zkkagUCRfPSBk%3xYwQ{S_Z@vZad|tBeZuCbs3%N2SDwiCdix~6xNvII?71er{ol90 zbzji3P=D7}sSbtxj-6XPK0P>bwZvXk%ynUyo9SnJ^OS=Vx7CEi|0vgacE@mHr$ZH+ zMKRZUo&!9;5>?znt`x3{t?gI2etG^??_|#3sdqKfx}3f$K2o-?oMymeRHfK@E5=6l z@~0GU{XJ?q3V${|TAaM<2Y=)D$`d<`)?GMzV?lhMReL*k&OC?CjZ>wVWt@MtF8jv9 z(=}(_pJ$)Cr#z^$)wWT~fBwk#R_E)>UoFckWY@o*ss3s4ljDCVd&P+zly6SDe_`p|oi;gkag8xM-aR(|UCg#bONft~*@Ca7^8eu*_aYq=7_$F# zN>3JBZ{1!byM3=~^e0BiPCK@>@0|jEPukepc+2_SZ9^typ*P1@7<~x8_;1;@ko1?E zuHW(5-*@-|tCE#hNFz_h?+@?twaUxy2P}JSzL)h+L9^Jrsd>4-j%&{==%KfVJmaAW+Y28Z(7xB}}{v7>xu6f*Ym>0coVK^@+Itto|y>EHDl$uY?c1xgW9BHJkt(->=M{s9wulklBFKU z{6Kl%%-RM2Zpg58$xSu5l6!qBYoyP^d4B(kELV6|->H43)ywp*HuFQZiN1tUYRaz{ z3(MEry$Y~i>lrxbSI&NBo|u>30p6NN*KXTbA#wTdipp)Q`BHU8xhCiAn5|1Z7V3P+ z)SY?lpqKZ*2zTdfsb$j5(=?Ku{!jb4LVW$#CpFGO&ey)%Ia)sc^;RrH)Oc@U`3Jv8 z$I3QrFIuv0>C4hO?fXD`&ADSv6lyoG0?tY?&otEYl{)B`nXJ6Bt&( zq<5g!-TV8M62F=6XId_J`s}KOv-bia&$-k1`sNmA%~Rm{zp6Bwb-~7p^^5y&uRQlY z+|r~hHe~x&&aGb=vZZ*-xiX%-nP&UW<)o2B`6KQLH|MiRrr!*@ysmZ4oxYyL%{u2} z9?4py)lPqLbN@yQ?x&5rJ)AX8hi}`+BJ}X~gUc=Ewy`O}*@v9GPW_bTnzTXM z&qiV*--dUe&q}PA{7S4m zljHD8C;Q5K;nUOFO2yY5$x&(jpZe){>lqDJEjO_sb}7b2_1jUxE9OrPe}A!3adKhg z;Tq1f6F*I>l7GnaEAdT>Ea!u{8ZFCbIsZIa(k~Ic`rW3dMFGCy*?R+DsD`A=ImbTR z^vC(<{hc@1Eh=hH>|9c~!ro(l$K5%cHnnRGNm%C3yc7KV(52a1j%a+`qWm+fE%98- z#=gs1OIW*eu3dj~D#K|@xYd$7HipZW`Leu!W+Jq4k;t)5)`p9J-!XfC+;nNm)!I!z zp59cKn{d&;YsYl$i5phBH_L?N9SR6tw!G)#E|d3tik-(hsy|xpnq1)I7ji+fo~3ib zMBbX~X^;K4%@*8#?wQJ_91Dd=$sOw_PMV`3cWLspzwHv=)_v{zvWzPsmQlFi5_9wR z^;gcgZcr1~`1r%hDUWWv?V44=cR zqGBzzO!M876<0G{?p1YNeE-~gtKA7_n9LR&oUh`%_5Z~iPpx?8I!|5TQg&R4mBYFI zqtZsLyp3_~e6NpY+!A>8J0fn2l4jx0H3b2=XQr~A`5Jum>BhDN_t=hU-B!FSGFNrtlRo0nz1`QAJ=+w&Z}a?C zcP#Dd<@#+bCHi;qOcu{|cgtKIIeAZR>ES6`l70Eu#co6XnDxAuCQS}4bohNb?_JT~ z)zzQ0m=ANVw&o9dyh!k1=t;4ydz~kI{ncVGtG>9=+4x$d@o5RYjy-Ob#w8wHqB^qd z6VC2>a=*<{>gt)$seA5o>rNK?KV_lajKrQbzn(a7K2rXYq#7TVDj=N5{I;`Q>s6`I zBX;JTtK9R`o}Ah6;_W88Q~SgJzPu{vcbrT8N`X6%*N)iPhx)zB*%+(dWgMS+$MJZL z&MWWajQN>Kb+OySFMbu^I(nnsbAxl5@u$z3@2bwuo4hDhi%)WP-OAWGKQ?SLX`c1+ zpYT~0j+d*C>QJ~nZj)<(+&5XnEA~2{fd>7ikXeSCG0fSe7zt%+Oh z659BmZ#g?J$+$oI+Gb~4qfLK|)^52UwX8bXm4Rzs%m($M`IXleO*nXLHP5m|{Z{wu z4;qU!``es9D}K#Q=g6mjYfo@4IR0-z@A<@#5C6hiUM*cI8?C@*IOCwFVg7|Q9xuzQ z->d|i+6*kWR=K=cnR@)ot5>=!TYrA9R6M@%hjm6v>5T6u!%rQMd&aL-VzYM96qY@| zv)>(L(ApE&`SD>m|B>U>r3W}p%+uj=kz8hAb>_jreSM4}Uz*=2&$!IspO$htB*1Xl z)})`^p)(e&SYWQ;W$LFd+kTKgyX52jXSubhn>PoV2$yHSIoJJFq2S56Qu+3*?e(XreKpq>2DI?YfARKR@bt^sT_rr4 z85!n(fA=MyRoQcO(YxjAYTX~Vt+{URse1KR($T61V!bhaf#>dBD!*QEyDw^)kkGr> z{Zm3)O>ew5y0Gw?SormZi|>LrzntaPE-1QlP2RMX^${WYW)0`;WxMOl=dnDQ?sB5? zyMbWub^S*NrT0mFyJ5@P!KPWEc>U1pDH|Nmp7=T6Yt|(0zcCk7rBzP7n5=I*!O zwvLd+OJ1JW4#h>PTfet%GhETSC`sdTZbuxemAz3-%wZG$R&i0;0~5cjt#0l6bGIvb z*-TdJHAlbX8U!!eYgm0nHfxUg+9hXs(!L${IN)*W$!{~|byFR^n-`=?*zzxp)tRHj zn`UiucX8j}yR#KajxA#Ku{$So^xbd9S%J$l#r9f>`ZG5gZ?+9)_BztSUH3hVJ8GfS z$vwGyr0N$<|G;?9#!BW~-JgzY(f)t>f3bR0D4hc;^x+Z%0|4W-Lh1qS_xAlD`bo#@e$a~9zIhW7Wxw%emqDbZC8J=^sPfULG zoiFWv%A3@DF{T!`7gr@7oXpuG{N(SkVzU!B3{&M&s`oYcnB6+Rgg2<_LT_!#JI{X_ z`xL!wi;7+u^9W6uEAXO3VTJs6LrEvrv%<#@niTxLyrG+&dH?l?x94n-{w@9Nb*`u8 zofio_OqE~v_&qQEugZJJ$R@-%{?FaZ7Y+%Y4(~83y(p|)DaJ8x`IkDa?Nf8Jt|~Qg zEO5W!F3h#U*Gg4j9{)UsW4Bf-zY#f>dVOcari)A)5-X;}EJ-mu>UDLstjsj~H-T$- z95nwuwYyfd=URX0wv!Y6H*R4q3i$Bcpj^Tr<{9_@J5p;mSY}@rS+m>m-J|(Q(QhYh z)fVE|`!elUOU!~>e0`2fSF1!VJYL-TLy>E~?h2OG&w4yf)#QaH-Q2Z>=j{59NR}Lj zGY{s8tUAeXXP)=|R)s?g+gAzrs<#}l+rND|XQq>mP}h0iIwy|*38!72%)I%tSX1i3 z?_4v(sI`?6Tn)MWys}5x=iHE4xGAvmnfY$Zx8<(8@~RTPx~=(8w0Bn4$@fLMNvW4a z&)hIqT9hJw&-hT#?1}aj9m_X7)Lnj+uPK*FN%Xq>I^`m{6#braou#_W`&$+*E2%WG z^LRb^N9l{@-g8zaRL$0Bs&wv>{c-PEb&0t6_(+(9{OzOG2|wCvS38?Vj`ck>T%OTYsLpAezT!E$N5={xo6kQ7aZIuEM*$^WC9~Y*6!}s z`SWbr-cH$SZ>U@1Vs`mP=~3H=#?pu4Z(U7d%J#3g8W=v!dRrn}*UX9j|FZm}pYcgt zjoq}4KYUr*SMf(&i=I#XAb;ju@zhH{R2bJxx?BIQslr`y@{(W5LFbEw_Rl_IYEx2@ zvdc8jai(fdRo|s>|H-xAb~h>TarIh9|v z@pabk`YZ19Yg%R;u-fA_e;sSd<*5!|tbZt^Zv7}G=PFs!^+Lfa-iUu<)OXDrD<{S# zvI>@N*UnzHTrKL$Pgk#;=~Ew0y}9S^;w@pvd)6IZIcc8j>>vG49fGHAIuX0&l}w$0 z8#SD{_|#4jz6v zcVE@4{3D9oRsZxeSG~JeacEk{-3eFKbEX;?P5S3&A9}29Vn+JyiQ?*Jm1geT6|B$q zPrH)uvue#6&uv0qTr`?*P39@~YS#)bellfNt8xC9Z`h4_p@>HkzH|Lgi z?rT=Uua;UeJML$Ez0Fg;!0ter);HBFv#uLy&kf#ljrY$P?NjeF_n8Zbhn{QOaq`Fs zmpOGQElIKYs*Gz|4e!m~+nD<1I@6M!>+`l3m#wi~6yGg$kDIH6E%4f+-ip4mhlb`4 zdC$H~*q-SU`uAPnIjJvp*X|V0Q~JEE_w$6MJHC}Hl>5pkzipD8(XoiU{<>@Dg8wgl z^;TnzfKE=J*R`t}PO}U7+U>$Q@+7ZH#%y~le_}_G2Gi${vR>2X6s>R*NV1>y<(tjE z@;(6|F#a6r5YsY2LhYLP*3` z$rT^I?saJEKa+T30_Xpx9~X0L|4z&GII=fqU$APMOI(+yf!X;2!>Nn*x@d%2mt4v3 z7M!#p#C|n@c;EkdPYgfFzs=*$Gr6^#-+5>0ncrD{GrpbKUnC?J-}6k}J4y)j!daGS07zVDY0F8^@h)j{QRUw^n%9%NNX=HFv2ta-OH zrF7!FL-%5Hxr?X7eR@|l`_&tdBRvYup{l_q)88mc<=yZ5wNOH9!i=vg^W@m(NjM$o z`CT*N$~%Led&Uxt8-jMLi5dHrg(PYlZn{y)^qn(RYfD4TwwJ9wv0pEq{2s`6WZQqK zMUN*cUc3MA^_4jWiCrF*Z~J&ApoFIV=zw#Vya>4dEFN=h9oUv~QbY-)?t zeU`4+(iN!YChlqcbk?jhT2|L$-ab<~Ub9VbQG?sUy5%C@T{nLB+I4L84l~=7{6F39 z{3n=}q=^OE+|u1-`C6vLvIJ!)D!*-b&_ z^OonTPW=bnh~C2g^<8U9&;2V!oM-fW|L`AVOJ8~Xt?9F}dz>eK zFu#~!`Y|rk<2c*%xANB~sj%kC*A)1L2;5+3JHe?c%{Kg*xx>)&<0g5PXFbZ#?Oq7ZTPgYb@zZiUme-1tRm4_qds{hU*V4ExC-?;_ zI=?m4dw#H8!#GRhp8b&}duObhn$yoIlotLjkt;&teCwf<)7!t-9{du;dR0E5;-Toi zW$S#}r`-AcGko#!)jN&`?zBz4lNpybt>M zN?=OhEa^>GlsB4Y|GK?=;Vlo{ZL{3ZIQ?_FYRb*JdtZiJdf%=o4?63n=5CG1w=+9r z@@i#O@ysn>|0>m00KY1DU-~)3$ufO|jR>qlS2dW$IzMpY&XK>$x+fTydAMh+u{h&Af z26LTpp@rqwwf#ruT=g?s@tSGk>wm_|lMb(J+g=(NQd&Yspn1{>HV!r57w# zoiW}1qv24{=_y*l%92aICAVv@Jb0<|5Lfw~Ps<`ZmG5O=)QIJH=-=51APc^jP5Qm}d5ykNgimfzk)tg$vfv16`K z{JNlfTi3nk*<7*d*ZrgEbq{Y$a^!huC%5OXqQZ|Bcj-wtlT9}U=vN;Meq66SL1+7~ zumoS@gnK_Q&aX zZ$0PqbgzrP!=vmUe)Ze;2RHocTXpCnm%XLllKXDq&r&rT-`~lb+Ryz#w*3%KdWTvW=XitdGj)t zrPK%Wz0X$vZauf!j>Spho$iOUt9wl4=K5w{f7=s?!U51Jm>vvo2tcq zq;=DAq1&pWe;&P6%Vk@eXj1U8C^+hL=*PbH!-*khAM0r5nX`D+>;)pF2U5?7tKJki|LXUxFDpZ>la8h<$^Rb#!dP2I=MXDW0&)^EAB`0~cbHNwrnBt9 zF}2ofdensv|CY#?%t<`qJjJEm`@}5n-A5sAk){#)6QT9S#TScn!Zf z=1&55vrmn3ztPS##Uw1O(}6>$H=+I5Kh1wKt`^2V(*)kc%1zr+km)t=ybE*H$pf3C zy%>Ak9~YGDYkASO@nBrO(5^1K$zo&I5#~-T(Z0}UR8a9n#-}O^Q6YZPb#oQ+ae@*{%_kjB46iwq( z%{z8);SOkFY15v*Z(&)tOQgo0?ub9!`zBv7n^O7ju%PbNlaW_9ADfrizNbKUrek|Y z8RyN#dn^yl>oId~>0jjMAsaorR=9enuYdl=`qAFAT$@b~SFAX2Ey==fX6Rd4yCs{lXV!xed1u9}ZFb$Zf@G`~8EI zrLCXfMOXPx-}bro9*mRH`~25;pYY1bIR{RkpSZsJ)uofJv;4AcTX%mCIGVe`SYLNx z!CQ_KTv~okEbB`rF7a)BZuINKmtQ_LjmzI1w9P(J^ybNrzhR$moc(;3iR<|X6~!KL z4d1Y5(se%E-RsX5e$HzCciuMq9b-h2&dgv*(-YUH|L3dO&%N$QMAEUp7tOT%1q6Q- z`>(pZX-0zJr*kU}O(kXRt}l2uS+47tQ0;{KE9Pb{X4l{<`7&wRl&w!L-#9frNOMA> zp{GuHt0hyO_L2`Kjk)DF7EA9qZui1mX_M~CgM!@9mr>=gAT?eH@BbLytt%}GMvRUZ2+;`vc_usnK;%8k|>hlc1c4;HWb zd;Ph}lQkQn`wm=;SU-E)9}h;|OWWUv9p7>kiX-<*Caj z8Q<>xbVK~=r)5uTwb-NHc(5+1^lNx}pYufGob@Z%4cxVgrgksT`|es^qI1k7Rd28N z!@?Qf|ot!uZw+V<1iV5jNV-EUNaPhD&~uvzXw-QATt9FAUE za73phe~;Xy1E;IG#0-1|A3O^Cc6-~`M~@!l6!QouNj97ODR(HlW6bS#k@Y=;Rjr5; zf#obJj>fm-B!=zlNU|dsuzYc`@|+n#CuN}=szCTB~Wt#X+ zF?FBIla&|GcJm3imKMpq;>b@%M#=d*Pjmdp5R|dBZRmK^A@ST}Qqk=UH|Bel0+)Ao z&T$uB&++@`^Iei_)gOoGu^DFuFYm4sD#$ht?`Hlm^}<883DF_SzIPsoty`@%DJ)*j zCui+EOUWX2#YIxLUzZ(kxbWgjjqmi?EBPuuSMK}RI2jEGU0;cmcO=Bn4Z^|%~zhU^l~Q8&iSX>-5ozYc^|S}Re$EsE&+k|`R0Oo z*P^dqX*J3(_}ASSV8R%kzVle9%vPfZ%8fo1K090fC+NJ;5^^+?-xtz#;rad>M-DIv zN56JyfAK|k!<1FJ3x8YQ`;z>r>7kcJR?82rT?=Px1xr<4b>?)`$egR|mLjnkK3wXg`EStC88A@rF0d_Bd@bC?&^I;8ISQ>paue67Xr zEhlffE}CmlwR!qy$x@9^FP=)BT0Glb#xMHev-A8}H)mHO~$;1*yGWdiuwuH0MVjv=v%;1v#DHzB?z@)>HZO zB-5T7mlY2mGu-etx;=BsMESVWt=HrKYoEVpe>Uns375+D!`GMPa*K8!*(TLscl*d` z0SEK_sgI3k#d2@{bk^GQZ^;(@Ze{6dW~MbgNyZ38TGaMGgWOp z{H8iT3h=)id`NP${xRkTcH`zFA*yC4yk+XDVoqM-_;zB#j@}C=*kscxB-l30^hoM_ z-}pCd{${_*6+b3FjyhYZ`z8P3ibU7un5(}wrM|lC^17+=sr3ZY;Mp$a8!Hz)OZ;EP z)7iMp*x>2e*;C(6S#;}Vnc1zsPVt|6w-+kiyV$YlK>GbD7uU0;x|9YQ%y_()>(w33 zy1cc~d)%jb_GSM%@QP>Mwo4oLJ?s{m{w$!Pb<@Qw^9-1NmnVfV?0uzR#=q&SkYcFm zn=e+&ug}U?e7uoS(|T`ijm68?St0kM(!O@S?EQQ5Z>^M=pT$JK-OjaJoab-bwg1zz z69Rf``LcIMHEZN9I)7Tz;f7n~rnRqrv)ysta?x%_;d=hJjN<&QciP`Ac1-+v^`p}F zB@r#F4Uc}!O0yO`=sasxQCJx+(}bSJmn_UE z&;RmizE)9URm9in<{m*Z?>2onHuIeF&`_k-b z8WI-^8%oq3Jc~*QS-aPA?cD5p&d%S@3S`+coaW1#Z)mWilySeqp4+_T&)2Hd+^=&{ z6y*Hov@!m`5s8$xlz=l=p9c%BzNsNm%aC$%hS!#yaK@V(oquk4(Nes7vzqy`MYg=5=*4|ky)97!dqrIZT)#%q2D)6 z+vJJC3ah2EdcIfMmIZ$QQ+sVzp<{o??{>3q-*4oXd=6GMVg3Byf8iJN+h20#t&U0H z@Z7N?d)4e&hTC-KuTGPE_T$OwnO|GVpXD=YOsnR(uFd=?aE`mtj#a1A%+8#MI)0}= zE4I!Q3huc}RW`B!teH53i}p%9*nxS^dEqvkO&AvoEDDz9{P{f9%AYtD5isvO7H7#9_KHd#=IT zXmy^><445An%UP|&6yjo5Rudp>C;>9Wp_kxmg|Wvi)Ox)C=|-E+*i|Z$U|*kQCEoQ zt(5iew=G<=%3A)@kLfAb86_qKP4d#J_&?cls`ff%ypa_^3P%L>_dk52hT{#W)i+Wq~N z!#SCUs*@wSUT2+(JzEiYQTzCm?Rt~9?zl0LG3trl+68l?q@Py2-Y3??Q1Q1l-^Q8m zzl?(T+c|kVxOdFn@n*}d%Iz~kdUN&!R#cSP`>*!d`_U};z~xnH+`Y`nr8b+{SBbAX zXV$QJTWK8Y?|l<;R;+$r${$+KB@kQvELimxOH0?kOLYZrZIq3bJ7-Q-4R3Zi>Jqr} z#bucgMz-2@mu|#JRuw__H1IbkyJ~Dfo7$ z`fcgLt9BY2d4Ij%{4(;-)_5<^1vQcpYrOcL1x+ejZ(h0NvB6=oH%xWir)W5=jttzuKWd$qrNx2Y>1Ap zpUtCn%2TlSRBo7oLw)ak3l9s~6y0Kv)*U+APp!Isw083+3!N#38>XC2*!Rh_-9MGf zvDL-d-}i2B!H)Sp%%7N=Z?OLk z4%=^}DC(Z?TG`c7eD?F{PgghSZWQAPH(k;sy)b`Wd&I-}?vs2^e|NpN*;L7PSM!c@ z0hjmX?l$2$d;I-<9&4S|)x2Lfe=hCkzc8m_PN67o;oqfv%KUThinRV+B=Dlz&!KEl z^InIIN^ujH?pnaVympU^0ox1v?F*O9&g9Z(P4_4aR9N>zQa5LBaB|MV@|h|Y>(r{Z z1g}2zn*aWu;PkH&T&Hwc`x6)M+HvKl<>vohIbX%xCT*=a@p}R*=Z;_Xxh+0l?~B)% z%stKT;~{Kkq~W-6-szu*M}c*ojGadT+Ndcb?q`50_Ojz zStVo~eb4;)H^u%36RkSU9zBuak7|0%(HM4vBeC9PKC9rWEoYwY;TD+BIkiK)^=AF7 z%ES-P<=4qK#JKc{sztLnl=T;e$`v(CeRN50dHLNmDZ?hiF=6*p#GxnrzOUDyuWYT75izcv4HLTdFbL6v0#>;0*D>W-;PIqC_ z%XBHUVX=00-^CqU*qZqz@QuRFIsEpIvSJ@f-0NJs{($DmMef$?;u&`D5Q`V`c;f4@ zCUDl~vQUBIf8L84o{4O@BDuBWo9@w*u~}ZLkEzeqN&9qnMZ@9U5wq&=B(Dx<`V z)qSKykK0b#8TRj}bH`Z+rfmnrs_oy)CcS@EeeS(;gr{cVu9ecE%awlkxV^fRFl)l1 ziIE$o@pl_&?f)GYSW-#3dJA9{Wyz(b9>`TW8ex`!RzZ*Op9w=in!+|S{%^w8G{{-+k- zt*E^GOV!-@<(5AT_gFS8f8)yR`2EezlMzeyY1lr0bUO?!feQmZvsaRUG*?O?kbb zhIiD*u0>|QR&XQMv7s;-+XeU*RpzAM7Z;_LdpzBEjp6S_rt{k)*XBD3Fl zIdIS8<=n9C@I~9^qI+NYQlD>ISf!b8_n6rqF5&Z{D{FdMVwY{(IH8ECGGF=Toxe#s zvz}FKvMS{cTw0|O_hxo&)GIdULr48wy$*hBUz=xRYF5S*vG0CBkkLk_ASuPmo1c4K zdCPX^#8VHxAK98`JOY)PyR$Pa`9se-Ge+KG`)#r8TCQKH{?_1sD;C>?%jfeb@pPp3d2ACpBVVtR;F`YQh3SjW5@VHk-=c4Q1&;0FA*gdC!{?JFuS&;TqAY% zg@^UEzZ)NLuRgdhVdu5C;x&O86GUw~3?5vc62&GJJ)L#M(VWW>ET`vGJ`$)8%z6KG zdiCLZYs-^m!`Iv=b zi0o8J-N~0%w6L)-B)?p~+OR`?&6Txsk7Ktg|11$Wz_@s0Tj+iEO$UW7l zaon#kOD^}XA|{p7p4z*%Zheq?LhI3$L-m^&zUpf8q*b1@V%0ty<2|KlU*oz^n>_*E zBD-!iIPO2b;T31r3C+%bd%_NLnNR}^(|PriNP&}Wu+&mIVI@N|T*Z{yOiWEXhs-?hTiu#3eguT8Kg zp{`=Tp1#91m0N~s+to~Sc0J$;n{VP6^(4S|)ra5ysu%dGgfn`VdVbxRVs!Gr>B+5S z&o*nO=DwX(sy3r&tqR9St}qoR#-MHPybr~59BXq#-aL`A=BqUchaT5XVn`5e@Tj4z5KZ8!_?LK3Rfn?NbD_? z6wSTn>od16D((RW-B>x$Te!aXrG{7(M^C#gz_MD4-#u@)aXO}q5S2~s8-MC&~?#AOJ zz5azZ7Ow(d95lSv`y?Z3N@SlGSC6Xj-L8)cN6#@d?sN64m2q453YW)me%&;P_f&Wzo!LnGv}vjEB@Z!Xk9wv>x|pMPFcsqw;uT~-*jLphx)JI6@Ok! zHpPd_%H(?R$BJL=v6HPb_Yx?0pCc!r%p*3dRItU4~DQ4ky4%ym2=2lD9+}GfK(t1MB+4#%t52drUO6xRbbK@kNIx-!1 z$)--~$-17t`GMsh>yvsnLW{NdzwEyws1Q;;^Yf}TTv}6EbLX}HSa8yOr_OyY{vDSV zcXis_soNClaP{@&!*;imZI|x~4%uB1J9T=acOyefjP)Vrl$*N)X6%X7F$@sPDVf%q z|DZ={`HSoCXB8gHlwhpt5Z0@8o^vmIru__VlhDHz|87Pe&ULK@_b-wrN7v^iZ$EBrA{uJCB5jEC-a1+rdfAgzdh8|RAOLR-KFPn zEtTu!b(sS!aWj(QF^zB)) z!18?WJtKp=cV~*9Ff4j~DDA6(i|KEnr~f!LjX2a_EG?7NFVVPoZ_mf(XpZ!Er;h#0 zX_4C%`rzDxp3@To!c$E$MRz^VPg*X$L0V5c>9qMPo9{_=pF<)X4}bMya%vDT_IPF% zv3W|@x?geoy#2$z?5odLysGiwc**%+rxVYfo}6;rR`aFtj#o_LSG1a69FScn`O;vQ zXG3S3xy6?iGi$S_yuLg6vUsen#m>sNGnV(B;7-xracS1O20m}bv(M$qW9Qo>AK9E5 zcG2)_&-F>{!8KR^8c(>oj@pc^2;~hRN!yj;(2jmUQgjQJXe#qcvZyT8F4nh)}P&ERDR1L%wkHP_}uQi%}ZSqH%0yV zwM1L_%fzYP$@f}j_|HDVuqEfPK~{3y%9Rh^bMEkw<~E;wPVt}7^`MlpTjf8WEs?rA zbIZ=P?_Mu{HTm|0a=xpT46h@~?k8>f;ZS?bEGulw{(?r++DFxz?dQXqo@K$eJ!*#`V{|Q57>pkxu*<93h{uSw! zDr#m|`$%tJRM*NTMn7>W<)aUz9u(Z2P@QCRBecjlP9^1Rc2Uwy%< z4Z8~0cyCK}Zn(8Cx@~6ED^2s*NArI@49b#x620ciJLiP@DRL5zcSNr4nxfC^)qk{D zDDz^ti5P#;ry~nxt#G2Qe34d)VXUzB%*)B1AhrQAd^<9&{DMm%|xxRbSlY6d1 zLt1TLZn;Ho^%LP^mb0&Bt$2TM<%^c>%2NIP&t$Ify52qa^NM@)KH=Q*b3A8b1NCQ| zs`2!-+7o7f&Ua6`Q*Nc!^54@Mzg%ZdII_hz=GI|t^$GO`*)128&l~9!E{W>aP&+K< ztF=V=jej@2PU@aZ6I$JE7wC(ox@lez@b{0Km74X@(q!r+KfkxK51Bun z+&-cH`V7H2u~R;(^Pdfxm;R#KB!1l=rI-?~je9TaoUU)46n${V5-VPj`TxT@_}{%) zqshMOcJ~~C+D?gFQ;nx*SeTBnC$BLnIudcW`!jR8M7wr3lY#UnuW+?IiL3thf-mB~ zRi1srX61XuiLr@x6VEM&qV9j zJ)O{#%K`!Jt1 z6792eTW_`2&imAApQ7H$x?047=imFP(2vWicQghy`LItu=JraMd$!&CE9Uygn3&@i zWL{Wi6qBTAm%MPZ?dLOM7qt)Xn{C0!w$X9L54P>GKF)h9!rfRjnryGkwa)n`zcs=; zIj?Kg(;KW0SWnkoI#AHO{bsJ=_t%#Vqn`>YC2J(C^HFy>ySdcrZ0?+tilgh=SM;jt z-sno2t^473rP&ARh}fXqObv!N8V~e;z50FRs7}>Ag)3T?_CH=LdC)d6St2`Kxw&keJ3W14p?mbKN5uIB1(xf|s zS+8uaQ|m3+7n&ZDve)iDV*FVq(tp%gUhV8&Rb%#=(89!krRpyi#$JBw+?KANVEsCG z-D8uMgEBY03al<(`+Yq5V)Z0(^U8};ja3W`o~N#1cyprvm1?g3+ZDB2g8yWjsQn2p zs?DfbcQNb!2I1=sC$E{S9}M)YaG!9s;*-mD6~~Ts4{Q$PE%r%Umt+6+f9!Mo;r+8C1K0b_L zxhE@h_;u~ltqa~rF1pANWVN=%n!hagZPt-TljeIea?D$y(D!cAp@6@JdrE#c=yU}> zzsvex)L43({Dz>uiT5pke%l#x=(g(lt}|0|Bv1VKDd^lA93nOHB)2 z$LlS2xc%bL+iRBJMI3}?UU}p+*X+$UZ8Levz7@x8&wTg&?UJUWcK=GUVfD+OX*I?( zuc>dEeL*t!W+*G?QdY*zrZy7~JP2NyHM!y6)oVR#o)|d_91)zIvT4I9d(%eyS0arv zO~)UaaJ@_VpLOzD?VH8ci`NM0eZSLr$b|9KkH*(4P6?T|@3B07x9bYa)g(P>4+Rk? z_M{bWl{Y?`ar5gO>syy@ryE4uORiM=IA2fgaMEP<piI%}DxKbtS1706@*gBK%+L7~LXI=I2dOd3w*YrC}6|7$@ zmiYTKle6%-tio%*3R9Fxc6Ivp=ftny^OEB>+e`@!V#q+F3V z^@wTk$IilF$yb#O-0epzvcKB5i-y{U`)uFiV!xYdO0#kH_f5rNxA?DKb~rIF^X7_- z+b)fFj|!b^bv|YMapLL)-C2uW54M%XuRYIs%VO0DUXk$oRYiPHDx=%y&zckRq&mdnf^K9pY#5QUpZItCq`t^ z(=K<1An!@#Rk0OT3A;QDY{e#toKBICFgsq(RM@i5_6r%qwkXsT)HFtx#-Edmu555<6Rir z9oFXWzV?>=<;>{0Qdy?E(>`wBrk1r~hqchhB8h`}>2WzlmMRA&n{%vh9{C;jDq?cm zJgb_QeBf}?bi~_KkmBh z`%!-Nk7(`Vi4t8G&4Mm+`1R~IY)e`C|Ec$hIrGmhoFRYtAy@a-R}2$(-J2Zf^=_7c_G_zeITD2Hxji7-I+QcvyMwq=*U8&9oO`X^ zJ+;c$DGKHGmoB!A=LUTSFCCLivYqIG?f=lwRZ&5vgOf4DhU@W>IP1WuV~pV*vJ ze@>qJY$bTe_1mWVz2QF>Rxu0TXEe>f$t?4He#uUbb^o3_?_Z;_vf}f;h^_hoGk?7a z`Rbi|Kl!GKVn$%xjW@z_b66X$wsWya^S!l?l!!Af53v9A(aG5DuzI&g^=T!^dfBD5 zoVngYvlczts9o1IQ7Sd!!(NuoEt-?It;yefk9p!Q%XbRl?;rS?re9xn<-QfyXB)HG zciqIou6hU8eW{Y3t28-GrBnaK5BI}ICq3I{tGE3A#EXxbXZ|_3>9GwmyH+tc_?=E2z`E*6KLy)kp1FPm9=KE@Ea!3f=v7AT6(`4aY_lshSV%t4+Ebcwrv3AFXOP?qHasIyR zYI^7M8%Magtv_onJZj}$$@a)5Ynf`?$-w5?Q(m>dFNiwIF1YK^f62P=BXiM~JU`~O z3=@Ce-Pb1e^S^|dlSsq4y$hdu-0i=;S)%V|b@0_o(=0B&3Mfx9*5%@xk`!r{w$t>; zVW*?*Tn)1XZxhru5Z`}BD zLPn*!qh-C<>b2`^j_i!!k4+Mf>57Q!Ns)WHl~MH55xzjf)C zr4@P7Zc&k^9x}|-x?ufn_nNpfo1@FbuAY{vUbUGiKS?!T{fgKXmjkPt{U<;CaI0+9 zoBYKQkA3eo-q}!hw7yo~l;np?t3Lk{)mhP}8yRp+Ko zCCj;0%`ty}CoV|sP5<@W{kQyz*HUkS-kVu9?Tnild%IMepXbBk#Wp@KkLt;vv^W~| zC$sR*U6Ul^gk>+;J3m(`YsNpgwD8$aw|@&SJU?N))YV(7YmZ@I@0IWXHG`)c_!k&T z-SeCIZ?B)onc_lmk1c^lowBPx+D?~wZ}sbGcB$A=#oDhLAJjI#YVqFMl=0;(Pf+BG z(1?cM{ds5n-?RP5Q;a`jQuXRA->0r!%YX6hzROqK$EuaqcH%kX6zj(GBK`UiHBXLc z&R|_(^6RhqqGg8?9-F%#k1{@Lz3lCU&CH9nl_!7GcD=z7`bT5I%y#qnSA*9b_ED{I zzM>sgb$FH8;b^<)tAC#>&G!2`W2?B#tKM}%&fCjvu4SJNI+M1gVo!ym>*m)ckjs%-q!lPCqW*u$NahzVyUW zJE6t0?A85Kb-_%Tvy0ZO{;VkXB=yZT5f#Tv=3acSn9M_t^1gk&S9$HdgF!2^3hHXk zocMJ%{=*!jwsjwuUEy3QylLmTAW6rwhmW4R%GPzIh(W02NRNX2pWm!XrF<&`FD$P} zJSn=$BjfrXw(kP9+b&!ZdF-84v{-C%UN6`B$*UU7k>ihyqP%y6$?Uil>%W7sf2GC2h6J{> z=^{t^60FxhDLa(1;WD46(ZXdfZEu^|?z_Hi;wKjWTiG{G?wchcTcfe?e1i9f_vgY5 zIs4leJj|b^=ISVT=r3E)^(xbw(k5>|uJe|Q*46c9vtKgpPPXZji*>bD<=Phm7<~%v z&M@YeZ_&D%d}rC|tAav4m)C@ADKCmW@s0ay-~n6lNY?LDeW%&aUcX$m>B}OEqJ>L) zK1n$%bKDRxo9}(XBh=b?uh7+XR-avYR-d@KV&C;wE_;tiJG$+#S?Arb;>?onAF&JO zHwD%6Oih0~^Vkk+-aD66IQd*-?Vl#(MQg7M_4?5`?caikO*UKP&TYPLE}DDh^oyfP z(r!8n4OTsF^e%g1Z@jDXvtro^*@GLGZhMrk{z^ad$}b74iPxH#|8J-Zn)*e^b5icQ zq{aUj6*lTs`^{-_?biO`!xFqE^Kyvl^ZO?seVny;v3KA5DQ`9{o+34~cHYKr$-P!? ze=hILnsP&M#%7X%T=8wnVu_s_8fH#^91&&OzWYjQpPS+S zdw-tRavC0)sD+R?TJ4ULwb{}%H;?Ns(q&g=S?%Df>XC9tz|?Uq;CoBRqr z!sV97J=>*-FNukJoxoY z%&$n*Zdvhb#}@>s^sa7=-|KvAPsPEt6V~l1HNGyN^Low`ZvK;h6s{fM`gQM??N{b| z`WagkjGk}U75nF-=m~*$A7`nb{#m)BJ8Zi6+SNY}%@(i-r^pK_`jWzo{8owj(pv&Zv4CY-RJn7yK?X4s#j+&=(@?- zc3aMQ_Piy|{LFpF^HUBw@0oZgX`Zu@s7sgXqOI!%gA^)X7a#ntS+`~$qffVe!tF2i zn<4{WG#j<8uVtHD(gl zrc2K8MzrhiMSI@v*=;nP{k6o=9VdGPD*{&sdEN>#(3QTnZ%db2Qvc5bi|r(wCo?*n z4OF;!&Ed)JXZmym_uA|NLa>sVxp~uD-4Rv*4+wPFz`|5M!apJojh$^Gs#j z3^x2fwK%;=jz^;MS@@GRf`M~(i5`FDdb^90+4tG8j#a0NJj-F3#VAr9e?c< zIK4zn=J2XUo_KiZ+tvR*p zUodDE+<5twd#RSnE2r9IKiAM%zMecio6F>~*`{u^Q+N{e|qIMTY&E2MdVS&b~L!76tSM!|P$T4Bs+{T**rHef=XM zvf+CC7kRTiNsmr5^j`DRKC+b6wIf!JV{yf-S5Pn_{*|9 zO7W(XS1wbwKfJ0{qjSl{tN-lHxssM$+xY0@YHv2qAlbZl1)Yb{#~5mk?KxxJTVP~+ z@s{rQ>t|XXD12J*C6-m|P}$VIGBRsq3%=N`2(no5)=5@6FM6q)`lB^_UyGXj^{8m@ zSdh0X%yM^QyF%e#bq4>W)3K1cles!RG<_xztDky`IumGL#S$7Z_jF?lO}p=b~JbFOR$Z%;n) zY1_47<$C_QS?(CKGX1Ca5B{n-&UQOos;+R~XS$Hr(F5jTo zW#{x&`sa7=y6a@@#xP&Om1XINV zeoC4A*zZ5>(!1wE?zw_RbJx~stLd9o25gr;)~k9hl2_O9YJ+L2{_lTs&9Comz0j|< zoVoM1U-bFozBi+;mq_|o-Bj@syEDOI%9~{$RxI&YS)(YFxQf#~V&(1WcS4VEP!9KA z*;4KB|i+L@0J->k@Ap}Fa{!SfGGcE9`lxvluYN;C1rS8`VTn15oyfx7P(*^~iqcbOgTJSTi=C1WF4kn1TeNH=gJ*+E zsX?_iUg7gYln@$GnzGMshbo7Mr zw!RkW)HPS6Ca<4#q#$sy!V{jtQ+p2hF7H?vJcm=c^zWS_y_NS%iVrJt-kh_d^^V7r zJJ}`+j~x_xdiq0$Kr~0Hgw`*{my=6GW}fRfaVK5c$jkj&seQ4sd&4rbqK%@HE--zX zec*t__H1W1qw}sYTfWMkSdy|>`H$|_)@Qk9zcd?;EwDA^oOx`ufv!zgV>{FFD?Rs@ z6l-W&%DBzpxNV$#@SxHZ(fYR=pQ%Qv$nZD#6x>tO42xDez!C6_?Iyz;8P|0y>pwVX za(&OtI$WY^x$8(k`LWpg$Pq!gOhAOS$NaSJTC= z=v|EM+L-;T?%?AqrKdJePIuXt_}_swVA{2XGgfgOsek&1O>;?Wylhmj(-b}B=1Y># z-~Q!MNz4g*Dj8rteR=xjJ!|ZGy4j~~KbQaVlF|RB^N0Thinpd~bLdOyUv*DA@@FfT z%z5*>^OJj>JGgF_b6bd~bJwdXnIvtB-x4fs5Wta8bM4A|1G|-{c@!e@r!y}*U%24Y z{$nRtTt%{s+gkUUw)H4ppK*^Z|EKknnl}vrT~R6PHdTaWpMEs4?9cAF#f&M(i#|W^ ze00h7@guilQ!)M{6(8N*hVyJ3b$uouytO*z=-Ggv*A_*G zG!Hw}RRr<3-aU3r_OGYk{~Z$|IS*tqIUHqeyWM;I#Fe0J>Q*5(OW1BrJ}gpU%T}4O z;o-AZj_-B5v~)t3*Sq=$$V@#DB3a_$^W@pA-DRyh-p22`%Q++6Kkisw%dvOsk_7o6 z_jOCdmaMBz(@J8<4}B!C$@tH=z%>&eXx_T|_o}4Ve(8y`l&37_Xso}ic_FRt)^F&s6^Xb3w*jd{XCmb+oGlJ88i0ECA^(biH1F#5GZ%GnewWp{WSc0i*|}xfOl6<6rhtx5k=_Bu zjc3+&t`A90-x^+!yD?xx$-Aws@8<2`ou$=zOYDdj*KVr?#%d+KPqUO+{3B)L&Tsbr zYq#cf+!Jq)Qsso6C$5Wbu)8~@@x2j?e>hzt&-H_xSiaszU5_kjg@u*Yi+S#s@Pscc zzjgQEgj}tYZyZZfVqV<2@b}8=f{*`RsO^0IXNLPdp^t|jyt=t%-`Oue^SZ6qNG@4_ z#U;{LS#!Zb6`PDGIrGYf$Yu9eN-f-$_BTxS)Ti3l_a_GQ{QbCzDP5qQ`B1os#f8o;CXE;An=KE|_`U5}lGmhzI+Bi8 z=U2KeO}hG<*}S5W<6!*91MZuq@bs;^6_nsM!6|RzzL5B) z*Og_PIh7x*bX}5q&~yGG%d&|5{kHjQCRcBNmcKas&Dqn9wHEs66Ej1$Xf*3ivY(jc zdT{FO?FHxbA_{pgPwR_{c(HlmL6b$L4}VlYjS#r-M=&9_@3f|>;;cKIE59AjOTQVT za;dg^R)4H>$w|?>((hhA>^bta|GMM`lOjj`g4uI+97{NROGfWn8~^6hRtpMEKeIL} z)o$K1Q`fN0`Iix+%dAD(ydG*$Wn@`?Bs=zwK+@Utqx_xt{xA zr1$1R)wL!v&wUv-PQG;ZR$!tE>&u-_KNj2<->h{*i0SXmHbc?3>c{1G7oJQo2)*st z=V%wVQ2p8mo}Xt7oR&XY%g-z^_2@aD?>jGW%+>W=q&iK?+N*Kcbps^HiSS3iVTeT=`_r9=FzB)@{z*g&xfM zY1thgBL3*-Z%Dhvnmqk+^1E+euD)BvpmC?Dll#K2)Fz`_D`VPAcHQN$h{-4?lh7dGJzy1@}eCO&?QMTV?pwPE;qi2y>k9JkFOm$EMPzH zgpB>g7xT4$ZqU8U@1x7N%WOeIs7sySO2$18<`%oWE?m_<>5Q^$b4LWf-R|udmu))d zZ}ocGndOl#Y%}=NXLqYIoSr%JWY^`qxielI>D>^0(CX)e)QyRK=?cm}{L~b0OMI(x zu@t_SMQt?q)y7-^M;WcNwKMP0idb{~qqnly>iJ~*EU5E0y<{#3i zetvE-cTK@j@!Gm{Lk5@naHorDmqq5zVAT9l9?l+AyZ;dX!W*^R6An}#4Jr6~sV+Xi zMYnA3?S~9y%jVj?;n~h_tHbkmO>g&9=}k3LB))BVCAy(M*ROs5mfi&yiasnmb0jLi z>Qu4EM59|jUo~sU7fm(W;gl_M{1Mu~On4jw*`Anc>@Bx|3q$h|#nWqgaOmYjYs z*WEs0?`!+M@*W$ez8L`pVzrjM+c%qxCh=hrgOC4_)V+%{dCy&Jaaz4MWa`3Zr=U&Zo@_^U|7?%)-EllW?#}`}onJQV zn(lwzSzPBqiw zbJa@>&smSV{J!a@GW+{AKSzd={ER-Eo?CJM-mhjpv7zNo@heeRXTwa+bt#9Y%3Lgw znt#lAnX~Hl)OYsX&$c|=y4<1Z#$6Hh?;JUN`g7SH9Y1vC`0LGgM2eT3oftoT+n*_Q z9uF$YZ}Zrd-`E&_&}3p(=6Y@&u~$_IdcBN{EyX(ozX-hiaO>ug`xjpd7?-#hub%y) zETXI+sN{0nUycTYvLh!y)+~FgK;{>|3B|3pZakkkJ^K`zPq?feT7`M;8aqY@c$UB{F!=u5-pW&TM|- z;i-8bJn4sD%zuq8vAt*2^sc*dGM%>W6+QPN`tqxTWgD_zH0`)P$4~zO_dQvrE9?_C z7TY-Nl{pnQomFK^%?#tUTSZ^9*!65)=4`uS+AQ8LD{Wtvs+QiC?Cg;1@11&b9&1XJ zr>cTSD}%Hi|0>V=lTWv7YPxvZiJ2#R=6oiW4$qLEs}}A}x#@L0`Om{=I*0k4R_3^f zw_K>6KmUrSl>pm@l*Jw``_69R{(D)HKdR_Trp{t&!7C%4dAY4UnIROJ4RD;dO6R&w0xtI z5(U<^j}|`nR^$nh{(7s(<(In7 z;moTw{Hy-nTNQd{`rOEw{BH_GS5C|0cbu|oS;y%r?WT*;8-A{1&Uw*kAHftXuBx}_ z-}Cn;wwF#k>i#a^E1P!gNk;a9_lY|X91XSMoZjucXy!3PW2SFKsvNOB``*`OxY!-y8E&R@?DbTs>mCFeu9dRv&9r`Aksym&-PVl{m*rxTSY2T*#%L`e59SGSb zBxrs^+HB8(FWkPD-0sP3Z#kuVxwv}6p7thDF{^zy^H?4pP}o@Kr6hQF<)TTt3vXRq z*7HMCA&JpsujurvS6)kgW?m%Drar;-o9e6zr#&$yS3Q4wRvx+fvp7rNLY18$pUTenO{Y2yb`ys`D4}u599X}C#Y?`J#C@V;ggI}Z`8xTH0$lY%fRBc zTZeg5SEgo& zc-S754`6FP`C{MZb>6ET7hH?C`I@${=9rs|)y&7MmtIMo*RW6XzV-XWPk9dCQZ4__ znXK|PG@#}An!n4Z`)qztc)G4o_SEeUt*M=HWwt_DH9gPWo{9-=czpHDB;T3e9H!~H zUY@Z@ZTenA#!ovXQ^h*B-U{%0e}Hve^J-s_<#!C9&voo_Iag=Wwew1}#vJoZe#c{p z0jxUWeC}y?o|(qfr8Tzx6EpBXWc%%8ciX1MIt8ovkFQ@n33)Ze)osh6z|&gKxTS7g zdT{N2p%M4NQ&qwrmZflc-@bF^%%*p9LPD7D>3qsHPQBP8n6K7%Htp26s|f2OMK4jT(Rt6uYaS{rZv8vDc@zYFFI}icVNei&943z1i$_@k8S4; zm|tahV6r^Jn#k2s7hbJB;Pd@!((<1Q6YQ65TrBda&u7ME9shD)eCS~KdP}{xPSiu1|0lm%z0ay%2XSV-yejRo z!y97OpNv^I_e9T;?FnzD$(PA2mhe$cT9;k7G40vAwe5*tj!TMNZ>~AQdGDcxT)o_u z?j5Iu0&~p1$_M;PIh(D~`{c;RCV37m3z_APy8m)ZE}VKe@qgFM&7DWvl)Kiey&hT^949IvX2Z!ua`x7yu*Z(DA-TR5gNT?OgHmzv(!_OE1D?Ob@`&dnu)Z&aPNrW#qy z(~8pE_2pgulkkJ$7oWtm{koEw&KJq_?1KBspfh0wJ2tm(IFQZvP@?SlIrdrS@@6j9 zJ(Qu!3Xx`#mdm$G!Zb z{-Jeq#i9fOt`?yHhRoR$7C9+u1PLry_GNB1-(J-ECX%}IYfakbZI033l$==mXD|OAZz$9@xmi^FN901+oos&|P3*pxnqd6D zruN6QTIBUOkz#ceDPN-c+ThQ-gnQPj(L#{cykJ=&wJWJ=OD-4m+$)U(w8c z|G>HR!q22`X1N^~eRS5}Tu()&YZYHSI5@xd}exrbf#}Y zR9X9z)<8W=vx48O{ZDWAHt?J7<%zJoHL*+0#GmU}_}2GFtIjf7ZnF#cv07pJJg(Zj z8GiSev8Y!}`Q~;{5rBCufM2Ik=!vJpDh+-Dq$ zJ!^d}A&v9a?o+`D0s-$58S1W05zwE&UlbA?X5Yy9FLPPnek&e7Vi^& zPwVrX9htvFGM(D0`c*#td?oGVob0108+TUcQwsN59%U7)fNBA)jT0{T-FTU~r8-nd zNP#VEW3`3w1?Sa!RrZ~5lU458?3?uHxZe%iU+fvZeoME+_Gzl$R-E!`Zu&*f6;dJF zu4gT|TCD$N!s#Aer{CUI%zv{Vo)=l_8Y%cDS3S+~j%37dXXo9`-vo z;p#f!rllvUWX+r>I&3ftEmsL!BJ@^g?v(esA7z|0-&)n2Q{dhof7-R)I%B!?hTH$S z9ux#y&p7lvVn3(s<6Dl$QbL3ZQx|>z`$_c5gYaX^xc7v+tiNX@8S*pYiB8MjMK8T( zZ;X00_uq^o9{GEwIs{GXf2pjvV_#*Kaq1G!sf@)lSk^_wa+gc4IT96dch5~`F4ZkY zRe#ySHZry)?035_8Tm0_*#_r(0y~adWoW%H`nv68*vFgJCT|YhiPO1tkbQOKKQlkiwStp!%Y90vIBy1`(;^pM(=$P>5k6=}T#*=#WI2p@BYNZ9a zHm(~B6T+EK`(;Om$W34Wkh{#G*Y3lOOAn^s%?O$+ezH7q_Wf^n?W7kT?d+a8{o^l* zODhf6%4quRonRhV__Ql()1h)X`%m{}4_BQO{hB&OYU*3wm!;3{EPvLW8p}O-qST7& z9s1IfPbPHhO)6J8Hv5aftD-rxKiu$KC%1cr_f}K!nV;sdGnJ++|2w_Bb*q?#z{18} zm)yLx)(0)8A{V~h$v?w?YsaUai>yi)|2}4Ne<*p*X$rU1cV4xPl@a`&i}!R+n(}3d zpodeB_JX?%=`njUG!9<~3v0Da(VEhpKUs0Yt?IjLGQ-tBPY)DTsQg|qJ@uW!>6vT3 zxYVdvi_JNeUL`a==8G<0_+*2g@cUDAR_^GPKIngH+X9h$#@zNnr;b=|vGP1udAvEt zyz{~C)k>CcYvyljx%=!uLx7IQVM{lzKX0$+cRiWF7Wwn*sfM72J>~oLR{T+4r`3OU z$;yP%t_$h2B=Yx)ax`^$tUGXY!M61ijJ)e-xj+6Qj|}qo?mgy?zu@xUeRXD%fx$JmV;?1Q(#t&-NA^q?VsSdMOHbkI zhQh_y3}Zc75AJt-$g``(f3vQ0(lP;7{j}xX8=kZ$?mZg%;XSL&l$`XgRllY$Y5E+S zt5>>HJ$6H}Z`Hl4nhaY$U0N5(Fk#Mnv47{HjI9C{eX2BU6Wpz2>pE0LFJ>j56G;|& zy57L=nBz5@Uga1Y2L%JE+1K+UD_T<>82HM&xjy=|i_1P-b=Y9dPLCz;l1krr{+6%# zYJ4K-Q}_8En)i^}qE}WY`W@{gUtW5eB4*=uaVyJ{e;XG` zS+cYFFWAceJi4;(2k$PPp9fAjTz(jC=qVq5Vb?~bZMU~;zkj>Aq4jTo!ea&IRUR9Q z|7YZvAf}oz>3FHzCDOz@TfO8n65lLy4J<|e`j}fj;yxL_Oz!7#cgs`Dc)Aa6 zoswSpVrpR6pIbSbJdQ84pY_7>{Az|5?W>FLtc+~?GG(U39*KX^2`o9sFa3Ar_ulzmtefrT@|L9A@@9m;TPe~kQ_&z7$YE+8veZJ%Z=fA6;eOP+>N%U@& z?N3*HKRx|T#>8y9sdmqOzG~XKh+kOba;f@w{3$zWU$eN1hISX$8@KdoU)5Z2H(IFl za?SjT%e%K3%@9y{u9k4ac~!s(CVqcQWC%YJtS z`u0wVw)&d3=-b{o=ePoI+pQ_>m)9|pRxHtW{ih`!+)s<^$3^zt8g$Tbb;b6U6_d{9*lqgEdG5r_E6Zh7 zamdcgO3v4nYF=q7isu7Z_3M^>k0H1sp}-&^GJ zD3kZr2j!!Je{J@&RYWXMa9Et=c}~vsnPEAay$LEhhMr5d)xK@4u_nLnIvMW zEMGsnAa4<2x^S+rPs}V?TVst(jr6QI>wPYH-G^(x=Itn0 zYGiawWnSukS0Dd1a?izn*8N|AD))yXMp_nQXuGe@WaXDJ}LI^L@>gniuYV zx+_p+KXaIF@I;(OxBeE9fb<09$W6NXbN*B)PV&9dq8obDe*nNL4& z5r663YIBU0sq3S+y5PPCwUG)YJH$Syd%k~KG$nq6so3{3{t|*qJ9i0Q^5yRT_p4*V z5uO8&X1|=1dU4I9Z%VQH%MD(B7kPA{Va4=YlH1yjhCAN(YOcC{+2u}$1n1o!sirTV zTef#Ccdi!6HqcKCzqQD-_VCT*=W}ejZtgNqU;VjU$?0Iq)KjNdUJbJN-FBt+*=q3# zLi-yxsNc!qYkDSRr9VIJ^^IWW4~vbqWO1+3tM3z-CUE5U!;3{`H`CXsdG)M_Xtcd6 zn;MtDxt#Bfh23+BG{%IT5t2cFnplbqTQ=-Ia3l2m`PZ9kezheeRjpdIx93eU=Odea zD|c-^#%1DWx``v8?);3$(P^3A6?O!D*WEuer^B`4#rXqQw!goy@TdB6UjJSvJ&9@A z$4*an_DOlcaJ!Xr#>o`>C0BpCSbO>9$IXj4t-UO{QfJ0nf3Br7`}j{xP!Lg_dGh@| zVSV2fpXQ%3Y!77NTK3C*%IfHU-P7M}Yc^Q(Mt8ySD{dDa7mA2q)8D4N^3l>=OjGi1 z6~6qh;5|+B(8IEmoBccVw2t_n+Ud$u*xK}Ho9Zi3&ple9-b-TF@XUX^X!X-4JA#ew z{&Ow;@Urc4pY&8E#WSa3LU!yco%gBnxcU5E>uU#3g?yd6^ir=)daU-svqg7QvM&EH zd~;c6pW|)Y-_tf2cYlz0lI6D1!fdB^#Qwfz-*@Ff+>Gnu>&~u;Rj9$YS|2U^)BVNwj>=!9b3cB|6s(dy zJ7r#xyW{n?;}`msQ&R0pt9Gg`6YflXcYWyu_6a>U;gLI!wVuDtGV#H*2|;zAk6fJ- z#pZKoj%DFmzKoTfSF*Z2&T=gX-oIDcoBeNKW}fEPfJ(E@g%wVpZhupl+Q7|m(OE#B zB{u5C>4#fqy`C2_UFOQ$e|m?cR<*v|d?>bF#+XMm|JlKw4SzR3EK!)k%A=Ddyt8=C zd$H9AJ0`|O>z(y7HqQ>3rMYa!YT-NRDdbe*Bv^8wpe`PRyb9_g>d^1bzGsM0 z+9}$cAGFt1afW^F{N6rQ{M^a5QwjNZ<0MSGxc4r0^vrHO^GL0F$!EV7Gdsbe!`m-k z4q>y>u)e(R?{WQmY#(37eQweApPP{N<$~|UsnV6jyef_s5Aw z*$z*ppi|;c%@e2DPuMvj*KK{B_JxC$61(}No@=c%(GzHL{oVAe>huFHw!n)5Hs>z2 zvTiDgoclO#+kBqZgY)LL{GM-c+f!BO{0}2-r{y|XjHh;3K9Aa77vr#GhV%Chf7{mF%}m&y_r4 zXH}jlb$Qdm$!S*|*38)h1o4g$iii^ zqTQa4WoMK^mu}Ep_iu5jcFV^_UGuqO+5RVXsoOpNnzUAf`Q`_$GZT+rYJGU&k&M!r zGA_m^oi6J8&sVRybwF?btF51xye#GIFtYrxbEA~pe&G*~wG>ou-r3_b&Chz@mmcYA zk*wUQ{_Tr-Cr3|bPK*spYOa3GX4Skf-^4TL_Pv(R0d9U_6>YmWHJ?8H;LPC|z2C3+JCY4GpU=6~ zcuw9yKRxZ@GM>JQeHWQ6)>cw5` zoi}1LcmI|hx(|eHe7*(MbjU_AzSsM?XmOV85&I_`A$P3=B-I4vTunHI5)EGl#dYfL zJgX#g(du+o|5AMk>lIOn<$~P#?sGi%%xb&*Ae8rf&C8<6*F1kTCR@rHl(SG_K`*s_XqZt3PY^(p!P-We6?#++GtbOP7Z!+ARKo^R~lmK>6J7Jm7k z@7GhjhxVzLUA{SGvtNw!q)jFbirzOL_3vGEwCj@n8NX+m^;1;VpWWD?=pR}vld$Ij zvuMoANhwQ~sCj-CmG=7CbvS0mlFzCi!~bzewDk+we%bzn-%sC*ud_eT{PDq?M|~Bn zJ{31kx$`se(__VIqkWT}6vyvU$!|Y5JK$mJvuB4*%L@%&Uo9=XlU}xL0+ZSykuLt5 zVf9Imrt)*mzT0x;RjuyClI%X|6SsYSeeT-`fI^c zcBAMS{y`j9jylh_xtyR8z9gddlu5($lN)&3%S_YTztZikm&c`-KZ|8fI$E|Hsj4U? zCvNcdKXL8CsaCapSLX!Vl<}4Ivd;7@DBIChq4T3eFRwYkZ~G$OIe)#L3awo9@n-7h zl%`7Kd5bNi+yq2V2fFU~v~K5}Ka2g3EJ~R9R63aHc)~G>2`+|gegd2-iK%?;_Lp~L zt$zH{``Lnn@6)TFoe_Mw{PptnHCUz8!O^ zKQ-LlPNtS~@u$fljFSIegchA}zq%~7smsD3hj0INZgo>W_SJLu7oA^s&G5a5ILC&w z$3qNlK5;BMc{3#{Wv2X#W`{qCyb5W-ep9>myu9^R*!$+QxX8KuiaVZVp1T!4djeZ= z!kzYar$jSu3C%CQ^YMzi08@j@qw~VLe-6F6V{fmkee&ypZ5~Z)^>YJbl8kslFKxQ> z|98QyIi|l&-@b~Nkliyew0mk#*B_Rta!eK8pG<95b0iws*kENw+=4O1k>+QpCQ~&Jf<3sCo z{}qZq*i-fBm?BFuqdQ~!+m%Nuo(r>2bzM?9Zy}S?@vf*M#fJG(m*16KTcF8d*=ygp zqsIK=75f#RYmR$S6g-K#CQH!1zY3u z3A*0`P57mRZ4Ss+D&#*&PUKpq&~d-0oaHEg_ll1%j842(?we_$pKG4<Htp5TgPd@eY;(=E)lD>+Rzb!mC(|C>KdxtQ_Zr6pG z#(W3vaeWmCzEStwwL$T~kIS0`dL0iPW4V}kBsag$V*Ss9x%WTbt~H$afN$o-d+Xx= zZ2ii%%i&%4mX%kf?j4cqT=}rQc!%{hsb4uutX(3u2F5T?%{f)dr1ZNbb*Adn${oiG zs~6kwzOVbPxv+EfOzjCrN*3#g?6_q1(0}Wp-?ukTwG6doJH0+(dD|6>dY8DV;%r}K z9paCu^;b_^srF!D(1qJYYpz6P`AZ0wG%T`YRZz!<@=m&PW8GS zK1c4-myG4BHdX7{r#DK)Jy&7e3T3mTg`1;P-mY1^!y!czjQ^Em^o) z|LaUgk(6E65>~HG=hFSMn4RsW&Hv*IKl?vRdZ7Cww00@eOtbJ`?(;8BI&_u$p=nJ> z<>ghGa~FLoQ2I5o^w8>-1+fv5J+V8ssBl{>n1A!$LH(}8Bfs89xPPDDsuAJvD%j@D zmc5^Rr{$hI{lg%Mm%B&t_wJ5TrE>Y+>lu7(*}e%(kxE{5gS9)YmA2^UVz|(It}{wmj{y znU;9@?%svid|!?xFVZNa7uNqHwWUs0I+%V2Rx(BpI8H=YlB zo8Z7S??B8n2M@RYx$WiCLrVF+gclfB&r~QrbAZEZ)z9^F{T8Km*>LDsM7k$!xO?-Y zWuwD|$g2Lzw7yxZR~@~nxA#@rD$a&Xk3%t@X7@$6?zH2b;`Z{^w|dUylNY)r&9c8D zZoYu$){oE6>JC|boLV*eiSQQT*9JM>e1Y2`WZ8HAsopeMcUx1FRlJ(% z9vS~T0>AHluX{g5O5n$26M?;faSKm3ws|n`<6#sD$nQJVxMyQh#KLoz1frA{J4Gpc z>#)o@vg8)`y3@zIZ+JX5Ef1M>{Eck*bHjeVQ0aG;%%|;+^?beFSDYtY<8kf6k*{~} zL@wFDH+#+T=oy+$S<5($zh^|-2_<%_bteQ^7PU`p`jI_I-Mf@>?#yI4`E$QhP4V^z zCm1e&>~FudMe&cCNO|(%%_7~cH5M5)wzV8vonvo`7q2S{PM!QT;=S4BwZ~*qj;cE7 z|CwlZ>DJ+hLx!78I_|sbS$ZV2ZLAHM``^87vK_zKWQpgCc8l9zixs)J^Zm54e+M^T zKP0 zsd1;N%HJ6FXZgP$=)0f#vg?(3;vI+gC7Ktf3dH#Q``(jkJ(=sv3*R{xnSZM8)E9|; z8oM<7TLk}?EnZzG=C($i=iA73H0fdIvsH{AtR&a!%HHN!@^!kvo2qCRF0t1})a7|Cc;-zP!>dg6XMF_ARro$K%=CYJSbSUUftMxW_l`ho|oww9e#i=KA9qq#5~|`9b#ceP50#SLN1p z6#s2Jlt2GMW{8Ajs`h;`*8P|Mum4xMvY4AUbFOr{9eY6K-m8-O z7j>^*PKy0{{vq4HJrA@4mOYDMGGP9CV&~N#%!_U6a;`Xq_HX37W&QaygJ$HENB2HY z^L-F;mOpvI%AD_g6Hl+~6j?t{zW8d;=69(Jz6hC!UpT{`yrr1s%KjU#pVVKgPE@}0 zU+`Spn#XIV)buAeELrJ%jJHJPGP=&*iX}JALrt0bVQbRHbXGZ#ukP zFNTDztyv>%c6Xb}7WcAd2d^l8HOV@0<)Pb46D~+w-D$^N$L5_UtF}x_2`#PFAu%va*Nw`?ETU!T@xNY*vY`S^Pf$YL1)R-w4cI1_cX37 zDEn^gvE+C~*Tt|V$N$YimG|@N{vEj(USeHeV3Asry3l=oaop;vjJuhyC;V67n4G8k zT{Na_eIDyduYh%7r+3_}Tgf;lvO8?`-Xqehxl791YE_hX=rh=i(EHXWy1lX zf=|9Bk0S#&r1d6-h^x1+*Z8zcDXDcOlj8J%?WYcZt)9nKIYHYkP(|0i>Ow{66OWf_ z@ylv|F&{VCKJ&(H^S}Ao#jF`2kECZB>6OngEX%!JX>D=)R;!^vMDRgIvq^UAUVYkm zyZz(L2a-DbGA_<~eeKG1>8;P(e?EVB>m`G8a>n@-ujze?lT_ZFzO^ufe@{^*r_2%` ze!Vq2KNPF2;q;F2u8*Bz96ZV5ZS#KF^;t?X9v2>;y(a6iA^qv`^2UErf6rCE5ZOF7%H@#Vk!hxV`V-hIeK`s1oH8>^S|nhseT ziB=bGlD*K>lyNEe+g1NvPnRp)JsI9QA@B08Ni0o{E8q54m`Zh9w9~knlW%LnUe6jFZ`<}WFAsdtHU!L_#j;;Kh%(uhy zU`1{mx7v%Nj~?tkm0KAeUsUWe;}M_zfm)fXi}ZPZbgA*UG0rl43SWDl3Je4KXUlV!{I_2)e6re+@~OJZ2-TyA*PWZ}yGFUBAIla62fKk0$$ z&2atoZ8!H#t8%Xl`s(I+?^ud**p~w_KbAk&dD(et{u_3W`pq(z*K~b&+}q$EcY|$m zz0KnGc%QDWTE?T#6OLWu%rPiWxGA)e+4xDsHdW4@`B`Rvg{<9l)~h_ri8y8`?z+Fy zFl#Miw1Vzyb4QOwkLxe}3fyxi<)qX9;K_eoE}DFbuf6zoJ=-i%gJo0Kf4RF(Y z4J+;PSU-M|{rj2Ewu!6GJIt2#^`cMseW@4cSvHBsd1luv+LFHck+h2DBZcFH=m!gnP$Y^uzkzr!r`vx&O2X+ORqK8n4+5NSjv8`Nnq(hQe!k0s_$M{$l|9}UuLiE`ttDVF;CGaNz(d_7f#3R zT6VfwU{#S{{Xvz(4M+C6^Vg;?yuHM^KkejR#tXF-$JHvL%ND=>!Q1=gWd1|8ZpIn? ze$O~hy*TS#*S7oV;=VM~)`=II>krr)<&}Jxo~pD{p!|hxnT7v7=^LKgK3%w62 zx2A%1?&94CidbVhr8e^}JaxYD^@5{Y-US_WPhak5xAVF^Q;b6$4R{HIneQZmFemb`Bt$XezlC>zdw9C$>)yj{aVSK=&~ho z^`A2ZmTcO)#eJFY<)wu`dnB)@i+VTh=KA}2Qgc?vvI%Ez`wD*lzjES>jyBEzu61h; zaIM~EuM)el$ZSG##})48$oW2g1*?QQGll(8v%)Qsq4an}ESYz?QNrrWL; zyYIc=*)t)KeWJ>-bKH{`Ygu}nJ7@2BtnYrfpLmtMf82!Nr}MAP5EOpDFzrf|%ju@v zWeeQ-KK|RdTJ!V)Ng?O&x`M^YL2fF3KfbvaUf16-DRKUms7vxI(qtYrY_MmZ6xK19 zB~qjHbj%^$qZe%?jTsFsUFE)A^mCtgyxhp$UT^)8_0!qny{jv&{0`nf=Q5Q`xj?x0 zj-V_@Tg;w&lD_-aMaN8k)n31Tci7cu5j6pym%3dw+wXAeUC*wJzXFv%N|G-x_CC|5 z-P2aKVRLfDWU)=0EP85xGZuxkPWHX~+wRheU8!Gp$voudeSTLdesSRulmB~8H-BP& z=+rYo&nsu1`YBe&MVA{Ei=E!racseJ&eiJ7`#%P@E496_(!VeC_;;n9UjMrH{(s&~ z{vzN%@9BY~KVtG9aoOAzT(hb5wWx}~ED0lb?|Noszq7%|vH~uaBqid?O{JpY77dU$4TXCrnxO z>5cb2=0iKJl7D#LncZyMyJOPjFNW852Cb};kZJiRba+y%_@)1cP3C;Nur}I>MJ4)~ z{pxpL{3=eTyfiSMJNeSOJdtyW?`sYo)BJQMLt?cN+bbW_u9DJ-kkt-RGQP_=cvpwE zmDMY2s)Z`9{Fkf#HP7IYmVL|B+Yc1uIsA92ME%(-CZ?ystA zjWjZBPJ4?Qf8PATwXj$7l5^RvnK!s}XFOP{)pTI`<|$L6?_JQ%oWU?R>YB~EryjQa z?Xx<|?#??@uBx+qOMYrwZ;|e3)S94JUx=O z`Q$R+O`1y^Dqlof@B3>0`>^wOi=+kDlSRV*v%b%4+G>1o|Khw;u`TZdXGOl`I_z<& zDPu{pcCyd&w&oqjcAnW$Ch%&_3aO?aMN%fFmuhNj;D(d7MA@L8;_m% zFIm4sBD7c~iRp{elVA68nY7kEkBvUst?)Xs@VeL;Mb~1M`l6?cKl9y55qMr|{>9kX zK>6c)p=}es=wDpQ=VP(i?(d7hEgxb(o~oO+g3m)T;ey<$jHRkGMKmvL-~Mv`F$Q-} zm6y}m{!QMWA7^>eDl4ciXBj^;%S!&(d-AL7-I~0TtKuEio-Ozt#6v zvTk1*&&D=`O`ZKYcmEt|$8*otUA9T~ah{NqF>|TKyaSv5Fgsu5y8Ti#ee-oWqlFKZ z+#fbwVLIQmY*u=BX>O)r&oz&omwqv;A6V9}5T$?oWzjjGWxu?CFPMJ3+(}A6|Du|Q zhg_KD|D!$|zMFHK?Ot>6x)ui6C_&8s(nK?-*2`FSi`(P zXfpSSC8syD9iMmT!k3rF&MH*}uhoC};E=O$yv&2$O^%zjmw(&&HRU(!id^;QZ4PgY zbCiRl*N4g7wYj!Bv9y4B{)L?z!!7U3j;r^+u*`b<>ySw`>Oaz&*T0Qr@7R`g>t<^3 zBHbfxrU!(y0wROu{9{yNk6FbzZ~eYSY0I?O!t#s0*;@86&8jt`2LFK3i7 zI+YSg=bxwXBF(u`aXPfula@yiu`Sa#gzV2gh{?C-Y&1<;e?7Q!mHf-4ZbD~|O zE#r+@-iAw58s<%vOn!7E(s22`3oS`86IQvgO=)q>V5%v6#;Z$F^#e50{=QStk%8$O#qZJsD6VzWpoCQ3rFXOY0+$ALSV^yf_wIP9FH zH^<`Yx%EHJ`Z~{(V|-w~_G;o``TgWwe$AaqDXzhScjiPeuV#}hX)q0{pL8n9?NNzjn*6oPsn*92;_Mi`zVj$XWIS;=?~s2zkU0 zY5inB>+7cE$^Yu7RSWw6J%3K`Pw&E=EN4aZ?&+L9xTEj+Df@?;m;^h`_;mHTFZ9ak zJoUL=EvckycqQ-mtO||wm-l9@3^A(RY;$3IJHWA6I}6Q(lP z{rDF@`T4AWK_7Z7KCDl#;?1w{Ewpa6JRrPhg$|?ZmE3YYn{WG4qyGzw)(R-=FS1@O z6gPYNA(hS})6%c3YT3lX_I<|jF@dy z>#jv}w@VSHAF-X0R@$ggDzDVG z^ytwlqa&G{GGc6vyOZ|4FWdg3Sv^Ina!Y`I<*Fj@9Y^07?G6bRn`+~D?as0@i){aI zElAK>V%XuGySLxFQ4}Cgxkxj!|=iEo*X|FA(EedQj*|&Gyvel|4Oe+`b zI_h2h`DQk|=Il!z+{zn&cqp;_BivSJ%(3 z*z!r{lBS5qrK1fC{GuwCt8Koz$mMH&-Nx9s+DSWP`#dK+nJk~WLRDP-b*23p=a6G- zd#)@<)4W-*?cMCO>xZ)fHEXBrYpI=g^V+7@f0%gJTF%w~@%`X_5ZYO``esq7JxKvNxs7vrpDEYf!}D(?eB<40KYdTmTT5LI%_@F-#?U|3U*GdbOS`fh z9HH7D;-@awIM5kHrl`?ocoN;^JYUoye&JpY+#vmDY^b@VT=lwbM0K5Sner{B9z7jJCNZNAO$ zaDI@}^W8`1&G(qPES^j7&dc6c349CE?=EFuq2Va3@#@Iw**UQrg9W~Ey|FHm>&uGe z>JnE(_gXK7GAG~a3)VTZ8&~=Tna*cqd2oATcK)jS zOZcvJ{Wkn~Z0C~5{!fP{hu>KL=el2}Qf&JB;P922Vs0UuzgPY%J#{_fl4VI*V}HS= zSn(*Y1#fh&W_7;^<4@KB{0rp+IBWc-%f{Op6#v1~zpHD6NpztF4iN)}>dddM@C#oQ>a z-*A7k^TI~)4z=cp|EaH*|FKxJPR#%L_FKCeD=u5F|NQ(wY74v0OVzS0Ifb3R`Nt=@ z1z1lLO?935JD*khLhP4Z$-ci%?1mSrrNR=obqa0>Idykl%%7l*CN@*pGaHn{)MD4i z8L#H?Fm`>qwvPS$qZL<9t^dw(Qgh+4narB=YnAtl_&rH4yvud{_2!yu@$iJDyb#;2Lq8e{k8y8%T|VvYzn3R= z?UH$L_x-FHccP5h`SqOCx))4ZzIbh|x?;M9rGrI5zDX+M8+q3>%ur*l1>dp~ZE$KBA!dpiR%XX_?ixb~y*(=F?p9PNUIWxJnr8g9R~ zb>B3mZM)7F$86}EZ~IVm$7ZL!rI&Vk=kvXjys-J)#_}{3y{}UqtopI1n1NsB!G-m# z+Qt0q50||T>X%`6b=;`ARoCxg%7wy;>b z{Pb6o=d6rb|LnGBo1vliv^~0cKA%FvTlSvg5R}qXzO^MWBV^U`uRUAVthhY!rl{`k zQr@zL-;r(Jk;UFD6|B7Wa~jHcgCDgVR)4Thj^z=@y9`dP9|^hlKZO4YFG_4#TXy`l zkl5OS|NCtn4TB~e71=1dX_JHYwavjMzdL44IBT?jj_3pSOIwN;=Ncr|N0!vD7QMKA z;p+1r1a9bWN)vzb(KUTm{hGA?|183SNmga0oW-{b1Wz#@<6j!D+iY<1dCae$?k2BU zS8aMwXu&!s^k@8RLk5kNe;>|qfABzC(!^P2egt#HWr2Mms)=U-*;lyHYOZ^Ni?OrjCYur>4tECS_?|Ui+lPs%~lS zyx-Q<-Oc<-4Scgs)}N_4qVu<~vrNCQNu57wfvv{N)Z+Cqu8t0u-h^%Nj{cl0&-WyS zEw3i_O~?6PC43oHyJZwDlXr*uZ=LI+FiV}K_?6ujsg!Md9nP(ucE6}S+>EEn*PZi6 zj-g}0y+0~@9zWa2zkABNX=fD6<6jvS?ODBz>GbROlTw?H%41?O9-dclUa}!ovkmV)ZLOT)A^_+12zM zeMMuh{;O}}R)zmN+$DG8NUi2sTmRI8!+Ogm)v%tsuD|$knn>vK%g5h%o}BitrFN~V z?=@9P#`CH*e^djl*|UQ-9r!4>aaVw^`E!1aKGW6zGA~U1tlRkN!vu*CzdS=@;jg@B zcK*@s$tY7zk#dlk7}|JmZsyMWQ&+Dw?(4sPvFx7Ll~wcKNU^BBK7QFZ$W1$cj^b9a zPn(_dcWmFE;4Rznd(Dl!+mdnJF@_y6UsVsp{>|w8E#ia@*B&p*ci>a?s3vu@zTA~dG&7{%3WVR?ELz)i&O4r z%CCJgZ;xk3{P4T8#-QW4@x1xh*t%sB|9h8jefsv^kKFy&c1FGX>OAMeN|tCRk9X#m zo8?Y1+kSiSWWu|vt2F{Qv-6l_&Dr_OjrEy@V1wl1H$_%j%b1t@Hz%31EZtGQ@!h6< zJ68AmF7?n^nbxV~xbJDyE{WA0i3=7pDW+I+$XT1)>+G#Iic)EgwJHxk_OJ8w|K0D( zf9>Eg-j;d%ZXcKJr{wovFI%xKJ??475Vzn!glcU0tY1&m7s>U{f0l7`UGl5rM*^Am z9;j~P$hczndv*H7`TrYN{5<(VQS)J$j*ovH-&sowEuRZHH-cCgu7_;d$EldV>e^~rUg(8RqxwBvoZ{IZvafBq~>U`wt3 zy5VoD$2{}wp5?_|hZM}eKe)8t_4t-1pou)0N$C-#(w29Pb_{ z`Al(SE1mu2ae3#8?=rpr>L%S0QQsl06V`HgY3_}}cct8IE{=*EdOnMICJdzi<6~yzZ;f<3kfpiOyfG@k3{=^q0Tq%|5M^Z4{XEejUTCEx%GD zCk32|PTVQ|#%Fbg^voYoEKZg7c&%%1dXSO)gNilsFN-$!ejYj(5{< z;pnH%vAZ8!aIq23(OCC8ku8lsbK2}oi<{FX#B;3*JK17C{oT%+m!|%DeREpk+QZ*B z-994s(v>eyZ$o$eb-9%5u{Ha9vu`wQPQ9|-r?MupyXIV)+oq0s+plv$r zpEIx5ns%qHE4S7$eKdWQ?_X}sCla=dkNwD;@C^S~uWJl{Zm&4Bup>m`<~)^meiCm! z@4UXiOuuB$H(kF8`zF6?-4rsLQ`>8oaoo$}EB_iyR!z@k+1EQKK|5u6Ys{oYv03v! z<{p@)QAl={yEo_0F{XPfsNtn5`ktYBB=ktOrs&z>I_s;9Kv-zu_L;+_^? zwKz+X?1eoizfP9>QG6=ANvr#`Hf!(oH%*<5wf(Ett-f(^^}juHOkUl8HzUVpOX56( zqYS-)(-%(?l#+(XFKs8m@AS4o3cFQaG?!DmAw-qlRhXf}&Nf ze_ZbCP1?2Fau4@e#Zvj4V;28St}m{R;@R;&x2W_))yahDw_o&))7IPZxSh-8Mfij%*(TGEt}ROydX?_c_#?mLmX(^!jZO1oe=6LKTB4+~?dONp z+*ScwpO`X7x#w9+m@s&$SjW zxx1|3Ta$SH^2ij|RZC0+l(&hgZuz{eV*Wg-4?zu4_fHGN-ulO@_D#z)H7VwV`p(yZ zDT>0M3r=+0FjWwBDv15#_&a9mOSX`^oU)=nRF$k(>**CLU({*z>N%OzZl`zaY=T_d z+pUWxdM{hcG8zI%sX^C<0Vzkha3=FIHgu12HoRPC!>5xb}K+DHFLmve7_ z)pqjIesgz&v?bF%Cp|5Z@N?|5RJ>4q|Bj`)-?<&3p_;$9MeRK?<;jw@+{dcs&E#Bl zWvS(@Dj~&!JV{TZp1)row(a_L`Fz0}_C%`>mM==;j_&@fz4&rPo2Yz{Z$MV!!leQ$ z7nN<}y>s3^@PNKR-Nic>jn3^1=bd(G;>h+|IRz}r^TF&Q#$@!YVXTw>*l|B zdhy|%|Dom=mz2HwB(ZnO`4a{9T(Z)-O#3`4+S697E7)|bYJ%iiNygS3DV_vw72Vej zfBx)_Y0X&h{GP#_#*f^WHLDxmoY$J+?q9&~raIGW@&m?S>^;v^=52IYp(4`I;I*$j zCTmG9*X`{u+TKU8pJ}o&S(fgUc}aMgF~@~h$tiQ=zO)Ibzdt*jk*Bx&(*%nx=XqM& zFIzlI-lW+3V?k`9(AsFF4Ltw$Je0~ajJm4xXV=0#nKN@U{~S=03J#23mNnt?bnSb6 zbCNbBadZEgzI*a(^%b8E?~OQf`mEny-|4oU9F`LuFRN)kw|Sr(SY_3{xnfD4^Rf4( zYWipSep@|#`{e%-uQ!ouP#-$%m-Y ztUs}@LpR4ICiTqKvNMRd)~We*%S(~1Ug6s%zAfPD+xO+bv7#S+eLMEqaelwQf46SV zq-T;*_f{WdGrjTd9P^d9!|Iu?A`^UgDNAUUy$Jy+ILn%TXI{%~;dsZ;I;o48YhcNa^2e6mJ7_j>j^=9`PG z&avr4rAQSBC>$>4y(V3M+HOK=1~Uf!BZ_pdn5JuI<^uV|6kt)TR< zRpv`=e;>;^(*E3$ZE~rqc-mhDqsZ%XzpOeMv7_9f zEvJ9w-k&O+MN*S<_RhA|3|Zj+!Xe&Vxc?iowxLZfOHGf0SW@wx#{DcZ0rv#A_4{0( z;`YZ%A$l{{cWVVFs{*qoGxtRYXB!GVpa1(9&zZ(tR&B2Z8Hq*xUOQx(rO&#(sbBTj zF=~lrv*`b}m|5;7b9Em1eDb+`pevr`%g-L=|5Z~?NGY)N^8DLY`{ZVYeWjo&bJ~=9NdFhYH4ufd-e@PQoG=Ggtwq19G(Rt_7 zw2!;l_pZNa9sGi|D(Uuy#Zv@k>5DWj^WrVFu#QSde`2uDVB&8@A){pNOD_a3?vp!I zG-tv8?e)r&pZoE!RNAU|_ir$%IrF~f__-sh#>!K-<=bzqEHs;Y`uKlUrpv*`$Bvcg zPCfe~)NVssmF>fjuj(GRo1+%o`SImQr^Sv~b^U#r?ap>ZV<8t}J7HJmvA6;>NlH>C>}+ZT!#ra<8!7%LNA*OWTcY zO~XA>&&XJ;nbyw0pun5&QD`;etZT%zGY`_`^tG#l{M**7)!<$2q%Hq1cAI@e+V7o( zii}l?nqHn}XI34^)^)giTI-g1`Gp0a8GK8Rx;{7Hac41_$&vb<{at5P&Ci;x`m5)4 za4wl(DcT(>e*Wj<%j@@_^FJt&S6kKCY$A2pCSlRtaEIo&8`H_lemZ}7j& zBlgkn%KP50(XPJ>_1sbpI)1;C>QTEnx4d$1@Qtn2iD%O>3Ow+BUid z2uRkjK3Mohd+Td)HQpa`OFsMc{tH~Z;m{<3=jl3kpC_wKOkmy`u}!sOTe|NFzRzqc zr`*lD{5q%i*HzoozPD!7GQaYA*_%)tn(Z4hve>%5DJ&kWbJv7Ws3b)S=Xfd0%Sd0UklR!;o2 zE325Bj$eAr{ZOzUABv*_`Aul{;KnHA914 z(DHeKuiW{3(~4(@G-`x=SN3v*Gr0D*`7+yH&0Opr(j)5QD2Sp37oW`2ChG?MhX+p;x7DywGssL;b5Cm|onL z-w@LBU{it(@09i%(JvW;gZ}BV$W^L--|OU+7^+}gRrOXTOl-NBw(Vcl1p;55tNQuw z@1GU@bmfOgaf$EQs`Zh7jz-Drz3S&)tZ3tNxL$ktjvsGb&QJ80>FX&vbIU;eiOpuA9|daT;)UN`dirE-Qr)YJFR<$-j$UTy%%~0Z_7B4Xy2M1=3nytbEWLW z80*j5?ma%Ku;|Y5YuoNU*LhK()5~D09QR?G-I~V_Jx_2=3}BizuVU7OXPxS;omJ5; zM)7GAbrd3h?3*;DbPl`rTc*R>zi*ypkWq`8^h@|AYxkvk=Vw296OI|5bZb{Lv@Y53 zXGd5N)7x9OOjaC?Jbp@g)>iYWxB9e&r6lH@ohW;}_4&V>WeSO66N(Je9QiZ_4*JL3 z^4}cUZ+XJ;r$MN&*sA&6bM9{Vb?!?4>?bZpw#`KqGRf)Yxy!vbe-Aos__yF~?ECtK z^_F#ymRR?EC^gu#CRRjfj*-w}#h)x%39heIC2z(1&cAYQiO1EXibY>euEtrWV$v z2?Z^Eshi`%&N%b%#$waX+5PttY_IzzG|uUeT{2^mSk7LH-!Ur={eBx^nwrDFP(0Dm zg6ZIwmZ|z5E>Br}i(h(uxtO{WxA`u=vX&H`Ll@6}%gpk)S8{i`>dNnNUUB)yww^PW z5qf<=C1NAjD%(v*3M0PVQgF+V`s?Ims}t6{rBdZ%qV=g$LdhR)PsqE)^6}06)iyD4 z5xbtrs@2@_|Nb}Wisf-#wPwMZ*9UWLOm`fTFMWSk=lOxRCzkNluhS_gJz3a2y?Xfp zf0eDTj=#(gzsI&~$)7`ZXWp(kd&RMG4|_@9Wd09}?rgQZ_Se8ZZ<>T8gPi4>Yo2v? zcTbq?$S>4McotKRZ5e46gO_8MpA0S(^>wZ#>pK}pkQu|KOg z|NPeM$EDn!KSS~!YgPpwQ|aCj+#lpUF=Wk}n=|Z>?a^nr8_}KjUt_KFR~<1YZpTf% zwQLSL9L8>|k3?0h5;?RnZcBEbH^XbN}Bezu`bxM7MZos@_KB zAf~h57QUMA_IFPC`kKF!xTFu=YWW&$xlqjah)ziOgwuwO%imb(ocHL{{Fu4<^o?_Y z%ig92rK(@Ldt=g)CD%OWGX-7n>`{7SA~A_U*l2eD#l!BKXY5j3>7ZNOkRfwft6Elc z{i$}Z7{|<_)qRhnCwYCFE)vC@TEt+)Zg0>lmGn+;8|$}&0*50PJnwq)a&_6%Ji2J|Ky?;#X?Srpo&5 zA8Rh%GfJ!w{6(_buG zWEe4b!Pjld3aNp8T22?9ba`%Q>^BJUaW+>L3w&+Y-?vA~RmNY&l6%2|qg8)bSUO!O zdS$3;e`WDjPjRos|Mq`Z`_nT?qVtALPxqD|3)}TRJZOwdJ6qZ2Y4LFb*KrOfr`&n% zxywqvM{1Y4yo@V4_RI*E5c=6!@s*K{@o+Ch?`&fiTuP_@oi#e zdyc%?qGe#1|HQyKD|KV7Kp~Uyua>AMM*|)^-wEKpXjrjo+Ksv$d&?Fm@*KOvw%B&+ zr6~)N|D^u#7h8X4bHA6v?hSp@vaf|XytCME!Owm={BT>YW!%(Y^U%4o*Lqy!zgPCl zebL2T*UeTO-(ljoc`MJs-4CPKlysVw`OaExv7kH2e&+s<=0TR*nvd=BZGN)Y?e5Q{ z_s#(dMA|S+^HA zF0HgysB@Eis%rh}=^=?-!gni9yf;!?D0un8stvo&9ozIju~kfS+TpJUq?|MI;~pPi zoIWe>-S-L0+FVX9te89_JUwgu5q6CtUZJb`;@anyg`JCu%Dyu!0i(*fG zYN*)p^?5j!hLg}>buUcXXMRVp&s{P^Z?QYC?XTN_$U_`=>b{ZisGx%6#yIm;&b z1uYg2R6i!PDo(y}Q1VrPSgB|Qeoh+Lk@EY;$ z@5m^hWA$P6Gl{O8hHa)H_A*=Mn!CHbfByW)vj*#n9YObwEjb--laqZ|^h>?1z=zhV zF9owA@+avV&wa%c(S9+Rv14yfY21fp+o$b%ap1`LqZupjoLze|Xd3T^%%_)6&r|wQ z=hS@mz1VCIPBZEE>V6@4o7c#GSh#!Z>Q!+|LK|ES;$1Hn=N@|S-cm=ENxtr*%Ldno zSoWHp3wP$8Ot#z{Tu)cc0RoX9e0@KKq`iVd{E2-};8rjjlu?^Bu(-;+9Bj-)ZXpS2XQaQOI`HO*K13 zw)BPm6SU)!@L%!g{P_(Q-@N-Ois)Z5UB89m&0BrtQzyUZcBkR4>LQXnodM~EC1zR^BuNMd+g%- zWB0BFQv_%3aQP8l%U)Y%(pfN}Zn3@Wd)H%Yu?>?OD#< zHtn9*CC<;Ux10=cQ9q=2C${PRsiz$;nl3M!ba%_Tvx~j&GyQhjy5Ayg=j*VWM=KL_ zHCCqWlV04iM6j&E`^=e(^(njef9>k=$=$CwCwwzsY(!I_^U{S)qGqm#doQrFDy-0~ zdM~|v?c0eD=dnI`o+p#BcAjg~7nhjq1tLnkQzQ=DyT$Xq;pcK`se2_Zlh?NwG<(Nw zdST^qs?XJ}WF32ZsME!(Q$sd?kQRLAqte_rSNvmFrdLG65#EQ_vURzA?Ybu}n=|?D z^w)Da-bu@Co0=C|YyY9RtNQ$6y_|{uo^8xKH}#1nI&8iWz%KeJ-05`jr3JogFW=4H z2Fck*26tZTR3P@3SRlKQT7$&LBezZcG*QWrFhKfn77)AeWk zdASLC8Y#^eZtdEvl~?g*Z%3p4`&*(Lx7_V_lfSci{TvIG`ZdiL6&@6+=(nVNUwEl6YNBQfDdC`rZ8}+QA8wQ_ zo_1m0TQ3K}ywEs5o3GlZxg!gCDpxjq<}vl!;3InE@poIXbdQ&fL8XyS*S<4OIplRi ztnL6`uh*?GbD?&3^~X>7SRb=^d|G|#n7nHETrDNXy`GnzH;G+TY`J(RW%gawKYGhw z8tDJL>!)yfdH?z;7cLqV9e8kHx6|sg6HCD_%^Wo^po@pzb0?pY=trxxVS{5l9 zzDVi#g83y86&vc<@9cek_Rpi@BkvD=4fG5MwpHj@w|UC4p6~Tx)9Xy7^)xOm>zseq zaE3m!yn{1u$(~K0z~ynCQIVbDpHWI9bK$fdrWc(%G`@J&uC*yU zcv5QHF2>_@*>G2LjR@dIlYu>MZRkkiXW>PEAdX}>~>(ia5 z>&|KlJbJ?Pc}J9VhN~;nrsWTdwV(ZqtGIkF=-z`hQ9C?eCc3($)~Wxk-zM&ndM;k` z{npg|M%`--{#@c&BH#Rcw^P@dT;s24OtbtqcrDwP+kNH+==_-r8<<$O>}FE<%^21X zI)5gsARTi4OjbcU*7IkUsB&>vt?p^`Z9KLhYU;n@uc+tG1h>5D3bQ`Lcsp)pzTRt< zE0ZmI9o2RIN!~R3>*q7=Z0)@J;@YBuj@sAWf8Hn>y=i(4^WFO9K2y%wDh^3%rTkls zK21Mtk$k?jKjiv~awGMR7dabaXHWIMDfnVnm6QvsruHHipHPKjzH+sdy%8G^H9mbB z%(BL(sepeew|!IT7pBRl;;((N7xPJ)cx>AAA7>XUK4b6*e}TB*`f{xJ z|DSld2N%`8%?auCl-2BWj?R#Ztcp7I+Ay^^<*~kK!KW#6{f__MnqXC2a`#15(vqM{ zN2faVXa)YVFKjE>b9LFO)X&Es?FpT?Y4M~`-Jd5G`=61$<5?Xv>zxTtsek<2XA4U_ zJS_irpEHciz3}sUMfBe<1%g+Su5(v^IBH{Hl)3f$meR@@-pAh^_~*Q7dFpA?7m_UL z8-GR4UA&-!>*&1>cV6|1twuK^H?^PA2-JMoxFJyPYUPrC^H8C!k5`^vVp{T@vv9(` z9{Ia7RG*jB8?U_NB{?HX+qK$fc~w@@EH)0l^{f0Mh5Wn3PaeIR!{qp!byhOBE~5S9k%>Ih|C}g^)2DUxxH)VjnEg7$F719$2Ps$dEhj%30lv(vB^n(#gRN$Va+8?*Yn5G<+Q@Aa?E9dp! z$vf9&RLqzxz{#?FA=Pfm%Hx-Nn-4}zT^T-4;7&_sTbcjf z?3=b%O*U*Q2(vdR7n)%C!1$N1RK3v5%bl?`p@JSWej1saO?eWzbbm+GiOmd~c1`{+ z^R={MyHnA<$6|bke1o%Ha*ARW=ltaZ>ImY`C6QJU&|q3&otd|VSApn zc03!Na;iPQyI+6)RF%Ak-{+lvYE*cDvwEvqVL`4%M#t8ocXtbS962o(WBB>o{m=i7 zntgm4wQtGOg_|c%vrgH6EN*gqWyI5sq6UkXZdRWz!h46?_lVUt_pg_=V|u2mVp#;&59{%iO6d%iv1cdp7v{3x%QzV%OoPnyA{AAj3&&HpBUVs(u{Wcl-p>W815 z>k>X_ascctqMnHBqY zb;p8hTi0lP?}|^Zde+vQTSKT3>_tW(A z)NUUs-M4yfnb#%!hp2s_h{(?2fH zR``9n)HW^jg=g1}xTC2)p~-KjZ` zVlL|*{`E=PSxF{HRKTS}vqEy>%z3}IyzOPlJ1L*hDu4B(P21ha>t(f0ezHz!YdUNH zORZy-f`7s7cM5kVHF8TX`tE*W7usb^A0V%atdt5nc6ZTF0X=n>;MqEL<+M zOH~+3e|0%!RVKc&c*oBxbMu^c*MIzAd86p5Q(NUU@!VA(%(Cju?}<8W**tyzS!1z< zEc*k0Ry^*JQ{s&|o$;t7AxzMs(q1w4)s^3T0@L^Zx!<`Y-8<4ys%p>9gX&4fPuZ=e zr6t5%On(_($zA+%$K3Vl%!`(!3piRl;0rR=$T-gQuzKUP1xKo*)-iUuJ%6JZDEsV( zoc+n}=pnm+_10Z(BK#5aznOJ1D~oskW)k$Db}^!Tt81)+ z&|lk}HHY{7xWJv`+xMDh)@q$EVob;OwFVXQC%$*!G2Ox$G1wy! z!JmD@_~GI()qCH?)10cCe~&eynD#`^!L|kMR&`#IR`mB3vCyE&-cLK z%!%Aqo^8HG5z?PH_b)%HUea-Ptwh}G7xAle*WgB$6@U9OkC&{} zbdaj(elB2VuiMs~FWPK&ZieR!3%#)2M`M{U@@)zb^zdq3QN2R6hqtXK+0OW*h4**% znptIaHN4$V-qkH!+NH5rwEPC&4ChkY-D?g8d+Tgmow4eIz2X(GnJ*juxHB{D4C~Oj zFH`n4Ja@;L$~fbR{gb}v&%gOGoT2cXu<#q(Ur!Z!nlhQ1wt3BZF#WF@vk3S7mjT*x zlFSSdPHnQ||U)$c1-A8lB9=lRNs@hhY!xv8JrzJE@K$N$jN zK66R-m}viD_#dY^w?&}(*8BjUwEr-gDPWz{_1n;&m^8~EeY|Nu;hWBr|X|vvl9}&{#vwZqJZh!gMrIJ z44HX344yHizu%SMEUUg_fx>ss;}0i_6k1%m&mTBn%P1{-wR(2Ran*x6_j!F+&f`02 zFC-j0r9QN4smaUa3p3Z5uH{I4C z?fk@JvDn}5S1**!n7e5U<0or<$pWi+=bt;x;QX^Qe!{l(oXc!>@JAIEg#JBXtPnZd ztG?r#o3~PcWY69$^6GNG^=_m)2BvWQowqoi|Cg5X$;$W>_fH+Wspr=^Q`T@&p20hIo=g{^`5DF;^e7EGb7httG%>UK_DnTZo~OqO___2 z>YnS$O*n0RXW5gZAF}v7_%Cc)wS?u9v-K0v<$-roKHNQK;xSu5dTXoF?`?i>pSdS@ zolBgxl7HRxtD<|Qz8|stxU{!$y3e;$>-)am2<&-raIw~{nLLcySLS5xO}G`eX^HME z<5baW(HfuG8GJYPwb#AjX;zS*w!PN&oMW{_?1Nlp5zWABdw3ZHrzpS7R^t^iJ}D!q z&MWr%ps>2vDxFr(z(-xT7wdfglezOYqfl&fSA^DOv5mQ_WG2SFNZs3cy=23b=!R$k z-GJ+fNrpF!CrMcPJxtwPw&cQrfLZel%3^OszLg0$<$r4S_wQ`ew9Pe5PyT(O!P2N) zbU3wO&bqV8k6wHi;F@P6YIWpVV%?!RVHD%-Xt z7cg-?{1tfpuf<;@-m`C(ANuFjAEvg8S!eR~8C~6CLV6D?RoXiGo?bZMYWDTVD%ty) z(|Kz4I@ano^BdH#?dU#X-WF_Pt;2H7%SkVL$1mQn+@RxE-<)n?az9gD_w!+m&s5Jq zrCDER|6jC0*F5{~y2QitHTOPGa8v&H;C^Y*D#flxw`Mk9JG5f?Gxrw{H9BtW7WrXZ z{zjawRU&If_GZC}ma=!>oy=XF(y??x!tSz?xV%XM8%-Efs+JxtU}?!ayM4Fgov4@d z(i>hYpWu_drV^=f?~nQGreeRXx(7a=IH|2LcOvKFnaihqS{Xmncx72$*M9c37nJYD zmUalN+0=f?`;g>&D{;5dH-U|!okg-KC*C9}o0omhUi3Q4=jXEJzZdw%D!J~>GZUNe zeAZH>Vm|9qPOe95*IT{I=KrwM!_{Z*{F6UfS+`DQzo~oPWM0Xg2?<^};(oUMdas zGuFwgxfE_R{24NPuX9zC)N}`3_bAa7p|7Rtl^-7c#gj6rdCx3~z1t;Q=kL;g(6*>g zcKZGTNq@`U|A`?Fmhb*@M*hq65Pz@pJ7=8>opG-JjKuoH_Fa1x&pINi+ohVrG=*LG z@6>Xs4`%uqMcKuR?6)vgRG$C2#`9hOyNak2RwsU%N-EE@_`Qkc&HhE9|G0wg9dE0e z$nH1CC0*Qi>!Xj;mN*J!F1jwb<$(^H&}Fes8M77b?8)zbw*2B-%6#&?)466=xoPDS zo?X2v$1}xOIbB6W`;YjG!Y6wAyJeogJh)CI(q^l~$JoH~zNJl^Di?oDoy*N{w!rtv z-4pSv-k)Olz;^uA#ZP@x1^yPieZEZen(;=VmSi-lZ z-#j|2wtCm{1b#;u7aJL|xl?|xFyC`4iZA_^z8aItYQ8f`-{W&9F8XVv{x`R8XUfZ* zfC>(`lV4s2h8%wUn{(bfhJVZzZhJ1~KTqY37iRo&bNcDt^w0U-(yIz~IK@93q* ztaQ2HozPTORFk`E-LdrY`_Jb*D#+|x##hS66R7z5z}5L#1&5@%ZqABY?4Y&j>nyL; z3LlRboH7nla=k0BQ~D_2pWT~F{nNg$Og-8^bGlt$l@V*IduIB3$JC5BcUb1~g|Yte z`+TeP1n0}t4Gqr)=T3by@le&Qti4lN1Yhd!vtGS&#m_?<((|5Zn}&HY+XP0et*(y9 zUU1w!(l%k5VB(rdjaHj;rp^}k?Q!_o{nGW>_gPokcid0V3F?1cF0k#xgq*oqlm7k` zFg~iI^#71|*WSloZ)=ky{FyaVn;#wU5Lj?@pR~$7fs<+l4!fs|1@@HZJH4;-314j+ zm36FovFo$zTTjhgpVFTH^gyP>mFeq6Y_3=AN-o%PJm~H@-lH2zidHOnC}eb%qsc1sj27wG^8tiP<*2Cs5sHDQ8b)bVMJ$A3enb2Va|ekYgJ|BgNP<-F{L@6T_3U*g&=Ai;d@o6u+B zwx!{3#h2`UHo;M``p1o1C9{ieWY!-sSd)H{PqOdHnhZN>k!uaB_Fvu3#c8MeI%JXE zwZzXNpHKLiN(5DEzn_@)f2u)`a{S*1?fSbftUUJUU{v5r*Nqj`XS9DZ+~m~PY`w91 zipQ()N5#L@b*~9tw%zZ~Het#4P|HViXU_j5;Vs4#Zu#m}T6!S#9(57L5h-F5KN)r8(n2J8uks-`{y1 z>DJuB%?0=SO7B&Fdb6f-b&a2bo#YDF+kUNUCtN>fRpHPYY_sTPoonNa>U`1D^UbdE zCoFIhxcSU{FfuAqWoj&n%NzAS8#?J5SzfHXBS-5mU@)~xRr|l2Z&AETiGoHEN zlHIK%D|fy8kTfZd)i>l7H_Pp#hPT*%=wCm5neC$1+sfS=5HQDRwhh-t<0*DY;C?YVWFG#vsaIwY>R)p zz|?%l^8*FqAv3>a<~p|RjOp-uzCQlFj7eYEuh_C>XP12qSk1xyYUQcJksbS|a{k}m z;;>@H{Ir}M3m(4CURTF?&C2`covGaHu>qg+xR&+4W?uC1l(I#iyD*tiRhXu;38k4r|PjPzkth`XO?Dpa%xBaT`TshfZ{-fQ!CR@Oy zSAvh_(egD<8y4?4v8JRddBIW`_wS-9u9{Nj&!x>qjk^YS+F zP`GaFdrYoi$Nn3(a&z|N2rvjv-_&^J_^gX}?Oy8tKYM(xhQ|+&wfCp|T`KC)fA5Qt zN_{>1{2RKGQ(c)ZPyMl}J7{{9+vC#v9{bNu{{3I0;``!nIn2Mpv$lSaKloqS=JY<# zkh7+_H~}}Ia~KsIW1wepPv(A zX*}Ke{lCPd(0~;tCoU(Jxg45YwDDAziRs&WI>G;*7isZ3TOAFoRs4QN&o`uVLA2k4 zS(2;Uk}hnFDt}TGQ?O<7)%{x*H2>=KUiI&Jl*o~a)XNq-90K}f45~NncRVs_dYRa) zaQ5`5B7LoFI?qh zu~uQh=~UISP`Q`?W-!;C*>L<)$*tqLnH|=_Leeo$AI;19c6w$2*X}hzlbq!iB-+GW z<7Z?RsrI{*y5;oDbGPJFzR$DJJ^!dsKeN+J;jnYPyh8A0Rh@0?-3k`F{A_;1yWymd zo$=k3d_8SHQ~Td^UEO&y_Q2aM0h1U!e#vUTemwEpEa9nh_GX;0?=(BRdN!Bv6BTDI z-)4tbToe7?@hf@9N~-EAtefb4m}7SA!*kZFC)_Ayd!60B(I z%;4KNOVd+X#A-dy^^0m13`IQkc?DOpGr73t+&tdqwY_!8eoxWTu-sWK&x_KVw+ZxY z+VL*hVtv0eB-s7-^E;R9DW5QR%{j+8yuyEyJlj60G@37(|FvnIx+|^0Cs5|4V;#&4Kxf7*6ACLW*ut{OsPu{v4 z&#lCI|GV1GwmXt;zE9qM%5M`F=4(^7e382LZ(@CsdaQ+ut~HlvzxmbA&wDI{CD)ai zZG97a`{B;;O*eXdC|sg{IB0!f4;=}VezLg zW|3z?MIYO%=Wegrx$B;YG=Jvy%hLZ#vh?cnT*Q{BKl^<@HQjGx_~qx%&d%6%?XT^N zhp+6JuUww@<-ry$6RrL~&pJO&nZ?h&V(UA}j%^L=*L$ll?fD$b9p|$4cl6$bU)FDx zMIO1fROWK0I4iWXFKyhDymj6JZ% zxQ(Cj!=HKeCbc4e*WIu95Onj3l;r0_OTS&~b+KPjo~V7GAmZ-B6ib7a2j*cR3vMnzEp8S&K!_y5usal}CHo{(byXdBIwf$#_BYGpB@Z$BDcb zcYZCF(Pp{hXJ>kTCbPrRw}G5O&It{3Unc8#c~3o6@j>+JTlS6>DOc|w-W>GJ`s9bB zn;op`OZFc5;CD7Xi$zS5oBvlyH8E?CPMK)Rx9CgRLeE7eKWFaFUl3q% zVOdHb^9rtJxz}payBExf2z;ZTEupci^qRtJiwC+LhIxJ`tslSO$l2wo{Fh;@84zK?SV7=Zyf6?JG(46)>^r4>SF7uC%BZZEESoxLvNOfwD*r?yZ6~Yo4$4L!G<^c z{=IoV^UZ;6+Dq=HwQibK(RuxaP1m$nAC~Ct`RuRz=%Yv3^2t0qw(>ko&y)N4#Kcps zXL;9-f`XrZLh~PsT-|bghqixz_mZ5AfyWkZ__F1p$=35#IU>qobN_q`_1>d3ZIVEyu z!xfe}Nw#0vwVb&&eQ9Kkgzu8Kil5bbru^NraOz@H=U1@1_dzS(2FsMmN)$WsO&z0TN@t>u3hiH&Dn;r&*#``Y?Z|NeM?uyM4Au6^#c zzG}r%t5n(0drZ$d?&V!&J2hW=tL2>M-60>QarkbjD4Uw~zH#q~hx_cg^3PruWA6B3 ztiEH)M25o4$v?OBcFaF<)|dTyWV8DFR|g*^{MfZ3W@jbazIIYt^2Q#O zZ)O`fIye7QUFcFBz;HqRcBKh3-;?u?e@D!4v~IkhpimiimCuXq`l4T{ev>{NUZ#0O z?ED;-gMD|bI$vo-{(bx=R8Xb#bbp!SiGQ*0f5^9Ss%mOIyp|lganqA1o15Nw)4tE& zroXm?RU*h{`TS1}_b!W7KTH#UblqvvvOj;X>Oa3T;S0~H1y?8i+kSA#tCeSKPTyHo zb8maDHGjaS#*6>wci!tm4Uq zVB?R!&Md3gv0~re3E5SkM~RF zg%^!(K3I3tDIr%_Z$*q_t?GYO8L{~v!}sNt?rRas-Ymr7&-&T1Ez989^u~=B?8DVF z%4aSK{CIp%)&2Ox&2))_g&BQQ!%is*oXvXC*rCkXk}XnW)pX^rN08E|f=|uyzF&Jj zREsiBVAd(FpX4*?k>IY@^RF{C$0*?iBori=HnRC3Rm?KiBB$ z&kOgypR>ui8o$I!+jp6x+VeRns`1;dB~D-d<$-}!S%f>kNT^2U%UO%0UvBzYpWSPa z{;5(#;%UU>1?;N?`?(jEe0rZZ&3Wg}*y(!7`(|}~4dmgUGmo1)eQh~+{v~-&)8i64 zhrGU>GK*vRf67piP54;)r_3n%1{YD5lMR|I87a~4eA?Fnf8MZDOS{0i`W$cCPW>l) z*RRmMA32d#GWFu5@E(TWJe48gNih@VaM-S4>2dVxEr?M{3S9p-Grjk>ymNE&)gxb0 zKOC!`kf^YsUQ17s$(4EY<`>5*YvnF7-pfC7)kOEH+@*uHO}*V_1#;R-Ccgi_GgmZ~ z%8R``{!8_PmqNgmxoM{&4xjleb8PRCnOR3;z8uwjC${JOd{@^WlNfGJl&RgMV)imv z>nG>s#Ea||lf4si+m3}<9-HkQ-TiB_+%lHA`{HG$8+}b~>DKr0&hPW(`;c6OHOQ>Fi~Y<<_4q_EK{%&&T>jKO7bE5;9;XZ~^M{$9>@^ywLO z)=6)@!uM1a@Eb+RYrfrT)6sp8^-gAD+f?CwEHXc97#ysAltwAZcrPW6ggTV;rgy+BR05`nW3kh0t92gJLB!XYOQw z{-WrTeMF8&b9X|WFx!py9#duBuSiQ!I-j;|y})nf?!dd12df{oySz(27#<|cCm6*g zZxuP4aiP$I$+?}U{XcMUZu@^lc3tLd^MZ*%yZXWn-by@Ly07WZ^?lVP%d8Ta83Vdc z-gD@=sr7rAgW(~Lg?pwmCp|5%IAmI8qU3B7ZuZ(b?%qnrlJn2L{MMMG(sal9aZ&Hx zx06?{2}?{8|m9IP?(OkH|AthqMx zr@}L59r@EnZ`WKZ%aB%{cH)HE$;me(*&OODq+A&GX&JEm+LM%FF>mtW&?)b)ne1}C zR{8y}TV9U9PK%7*H@nt-u{EF2!#}@VI$)tqa>c|O%aRVA;7j=v9{$Hp*G_uE$sgOY z4o&;(^51^f-$1R-JEvW5rGJP!`&8ua)c5&G*t5)@Iu)w*2`YU?01g?ZTT*J^PQdRW`qp+GFdXa+0<3 z&Z=GawsE?s3FPdtQz(DBBi4S}1+8aq(z-f|o8oiI3oNaCO>O`D4BDU6JJWsF{v^ZcAiU`$yV9L)t_Rx^X2L9`+oVedcAViIlK0uhjXdzoi1R&nAjXlox?K2+;@>ZUR z*nZZyw5;`t!)$4`jDyFy*m+JIPIs_2`fpL(AGSU4os#wEH%EM?y0k@FNx$20Y7%3j zBx`K+)!iz76L(vxiCFI2^=QdYEk3bx&tI)NRB0v0_TYB@J8zel8edHGx3=z&@MpTV z@A#pdZNYzc8h&au+rjWQ%$138v3ROq{^=>F@24z$u+O~q zkJyLs0QOl1E3#^JA7_RvyZyn!&Hc$4mG%j#$961t+j#2ICFX=LWlu{ceEiP1fV+F{ z#z`-t7Cc&br>1Ql+ZV+JvwEjc>AY5F;+k0Rnx5!<^|Wuikb13U2MLe{=_ISJ6NN1@SiyDznhPWn-1FYK|L zIe3}ikrfhCDw260KX-b>vdW^r%qko_jBT7hf?mt zM!JhMp1+v(PUZWn|Nj}5+VVYOKWpxE#@%l*Pk11&`g`Z+f!kXioJ+d8d7g-P&6-_r zLLE4k91y5-T9|vNzBm8-eYCJtgZ`U7&+KNA{;%w|#zwujD))H(%WsS;uke35mF-#<%Ki zN>=8Ww9L-9{Gmwa{EH83gvxtLEyU!$Sw!}(nIG%EHCL2duylV4t5@d6Tz<2{HSLT^ z7jih?ChhzEai8l^)$}W(pSZb8a~vBO#IuiwKMwE?`X=2VH-~q#f&QwihL8Hs^lDt& zmaz29>RDIcIdk2yQ#Jf!boBj-^wkS4$hsce^z}=a-Mw95k!>p*uO>e}-%_^GJZ4_f z>WFPCV&hzPE?hs?JGEl^<-W$^eTQrI1@C^T{57p*P0UfXKa;L7F5>L=JvDdvDb-S= zv>CeZiZ1<K8D^`=E04!lY#tvwEa1&c>czqv>JVV*5ao5MJ{Ecx7X;soZIJ{*}0*$ z<>sCr{nP$Fcyu(u+3}fv_(2oX7o4rzE3P{F8Jqpt5@faRzj~);!_|mW$4faHwEy`? z+}2}p5npZBYiq!^JI&Wm^~Bqm6W3SqFek`txy-VWFD7%#wV0?m4VtGMrB5C!t#;y% zIF%5r9QjN1gl?(ofA3Y-<||EKyt|66NF|}SK4!kt`ufuaW9ix02lS!~vCX6Gd*V9q-C;n|*29 zF}=o_u@5pfZkeuq>ESmQgOIBwoblC-Z@wE%RR~+SB>BbU$NF;Kw|vxh2u-xy@k(KT z>ALV~QWjGt_ugOd_Ta|f)4M|M2Z%B)>a1Vc`D&$&;FlSFONBztn3m}G$Y^?h|GVX) z#?0N)SJw#K<38%yI3aJ7Nz2TdUtd}NF5$Ng*NV`1;4|UF>zUz>o9}fyhraXrAAj26 ztG|Fp&bvi#gKu5dN_+g~pL?#@k{n-NZj0+S=i9jdn+q;k*IaN`|9!iG@=C!3|Bydt zFPb~YH{H5l(D7V2oHa*H&~=)x)Xw=EUrc_ie4{+a? z*rBR=e5anQ;)D3-8#W((7*w*>;MA)J$#=BXpG^8+XMWgMzg6D5#JEVhMrwYP!HF8pb^BJ{TTwA(v&h^taZTLUwL}A56)e>aKX$b~ z*i$^?aVSSfPVU+LubKR=&pd3_d_mXu@cT{P3wB;@J21VkVX4dkF|~)MI!$9)-fudi zsb_c~e(L<&`vg>V)wp)7;bXd(81CkE+_isK=HXeV#oPAg=CCe(^<;g0)b7ar<_}Kz z6<_Oq@xjW9>51|0fVdXx_H{vrMJ$`URrp?7KlpbqI%!$|`!M^ki(g7kSU6<0IE&od z^Gdj|L4a?A*s2>-O3jOG)?Qwtbjd=_m33E8q)>5He^lxReNTmFjH?s%)DG=UzPW1y z|LTOv#(zKP#xn9&+59|lx8hvsKdt}w79IHXOyk^Mt^eCq3}x=Dk}lr(W0z&(#chh8 z{`Rz<@8>R(D`cMh|8Jf7#<_=%r5>CVUNmu&9pCJ?a(p?T|a!=r0=2~^# z*8R$f4YxwGC+*q5bn51|g}EDSr){$JTcXuO&5jw%>+>a2^hGBnOTB&Tvf=gC z-1iZ+*Y{0-_@&V<JK&bqx8^2kkE zpJrEn(dD_a;&8XjB&F(aipu656El40Cp=j6SCK`2bNBCycXxkHKj42{x16z@OYpq7 zPxrcDv8)A0a%VhQ{Qu|~=j)42ITzfQN^>zzVi2FMdAqY+`ztGl=GFOcpPZ@Xcr$-y z)2S#gKjUkQ?j*E)div67gY_o${&TjS4_z;n)lV~gqj&jgZ1HKPV#~Mtr1QO^w%bb_ zm~zD1B%kHi|Jj%H!_MVoKZ@GV{qy`Sr@6-^r?GnSEPWqe#JJBs`qjvtz}_4Ls45`f451_;p~8`@1t@a=v+JWLz{Qb2Ac&M+olwYd7Tk^tG;3DL$#}K z1ygySD6-f{7w`X&6UX4^`tfUXC!Upd`rf>{B@ zdUhH*^Y!??E^3(06MD+g^HmnZt#~Ex_Lo_*YLUG?zsn=K0&Epx5oLVw=3i58nM zr$YYG{S4MyCZ<0pT~$0N{Gfe~q<^cy9PKiFowsi6Z>>dJW}j=+wo5I(p`e$(Cbsd< ztGBJ8v!|ZE`0A`@`99%4*Dn0*eSOkIQ~%((A4zsCO)pQ`73K~wCOeG@*eUOBm( zFMWMw;`WxZ>nBbZo?NR|x#0b|g&S(_FV^4s$o_>#zn=kPPb%xvm*d~J2|l#n z`DjKUXJ>QesEIdxBtc4Cg_9@hDac};4MyO+h3cnB}J zCefnSk~7;;G^}eqW6IHv6}y)oT)9Jf`B_iNr3b>^y;Hd%=3aR4_Nf

/// /// This may or may not result in a Close() call on the captive stream. - /// See the constructors that have a leaveOpen parameter for more information. /// protected override void Dispose(bool disposing) { @@ -224,9 +222,9 @@ namespace SharpCompress.Compressors.Deflate { if (!_disposed) { - if (disposing && (_baseStream != null)) + if (disposing) { - _baseStream.Dispose(); + _baseStream?.Dispose(); } _disposed = true; } diff --git a/src/SharpCompress/Compressors/Deflate/GZipStream.cs b/src/SharpCompress/Compressors/Deflate/GZipStream.cs index 6784b6d4..decbc8ad 100644 --- a/src/SharpCompress/Compressors/Deflate/GZipStream.cs +++ b/src/SharpCompress/Compressors/Deflate/GZipStream.cs @@ -51,23 +51,18 @@ namespace SharpCompress.Compressors.Deflate private readonly Encoding _encoding; public GZipStream(Stream stream, CompressionMode mode) - : this(stream, mode, CompressionLevel.Default, false, Encoding.UTF8) + : this(stream, mode, CompressionLevel.Default, Encoding.UTF8) { } public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level) - : this(stream, mode, level, false, Encoding.UTF8) + : this(stream, mode, level, Encoding.UTF8) { } - public GZipStream(Stream stream, CompressionMode mode, bool leaveOpen) - : this(stream, mode, CompressionLevel.Default, leaveOpen, Encoding.UTF8) + public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level, Encoding encoding) { - } - - public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen, Encoding encoding) - { - BaseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, leaveOpen, encoding); + BaseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, encoding); _encoding = encoding; } @@ -200,7 +195,6 @@ namespace SharpCompress.Compressors.Deflate /// /// /// This may or may not result in a Close() call on the captive stream. - /// See the doc on constructors that take a leaveOpen parameter for more information. /// protected override void Dispose(bool disposing) { diff --git a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs index 875717ec..a7159122 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs @@ -50,7 +50,6 @@ namespace SharpCompress.Compressors.Deflate protected internal ZlibStreamFlavor _flavor; protected internal CompressionMode _compressionMode; protected internal CompressionLevel _level; - protected internal bool _leaveOpen; protected internal byte[] _workingBuffer; protected internal int _bufferSize = ZlibConstants.WorkingBufferSizeDefault; protected internal byte[] _buf1 = new byte[1]; @@ -83,14 +82,12 @@ namespace SharpCompress.Compressors.Deflate CompressionMode compressionMode, CompressionLevel level, ZlibStreamFlavor flavor, - bool leaveOpen, Encoding encoding) { _flushMode = FlushType.None; //this._workingBuffer = new byte[WORKING_BUFFER_SIZE_DEFAULT]; _stream = stream; - _leaveOpen = leaveOpen; _compressionMode = compressionMode; _flavor = flavor; _level = level; @@ -361,10 +358,7 @@ namespace SharpCompress.Compressors.Deflate finally { end(); - if (!_leaveOpen) - { - _stream.Dispose(); - } + _stream?.Dispose(); _stream = null; } } diff --git a/src/SharpCompress/Compressors/Deflate/ZlibStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibStream.cs index b94ceea8..6a644f39 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibStream.cs @@ -37,23 +37,18 @@ namespace SharpCompress.Compressors.Deflate private bool _disposed; public ZlibStream(Stream stream, CompressionMode mode) - : this(stream, mode, CompressionLevel.Default, false, Encoding.UTF8) + : this(stream, mode, CompressionLevel.Default, Encoding.UTF8) { } public ZlibStream(Stream stream, CompressionMode mode, CompressionLevel level) - : this(stream, mode, level, false, Encoding.UTF8) + : this(stream, mode, level, Encoding.UTF8) { } - public ZlibStream(Stream stream, CompressionMode mode, bool leaveOpen) - : this(stream, mode, CompressionLevel.Default, leaveOpen, Encoding.UTF8) + public ZlibStream(Stream stream, CompressionMode mode, CompressionLevel level, Encoding encoding) { - } - - public ZlibStream(Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen, Encoding encoding) - { - _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, leaveOpen, encoding); + _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, encoding); } #region Zlib properties @@ -208,7 +203,6 @@ namespace SharpCompress.Compressors.Deflate /// /// /// This may or may not result in a Close() call on the captive stream. - /// See the constructors that have a leaveOpen parameter for more information. /// protected override void Dispose(bool disposing) { @@ -216,9 +210,9 @@ namespace SharpCompress.Compressors.Deflate { if (!_disposed) { - if (disposing && (_baseStream != null)) + if (disposing) { - _baseStream.Dispose(); + _baseStream?.Dispose(); } _disposed = true; } diff --git a/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs b/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs index 1c8a5d9f..341dc6ff 100644 --- a/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs +++ b/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs @@ -11,19 +11,16 @@ using System.Runtime.CompilerServices; namespace SharpCompress.Compressors.Deflate64 { - public sealed partial class Deflate64Stream : Stream + public sealed class Deflate64Stream : Stream { - internal const int DefaultBufferSize = 8192; + private const int DefaultBufferSize = 8192; private Stream _stream; private CompressionMode _mode; - private bool _leaveOpen; private InflaterManaged _inflater; private byte[] _buffer; - public Deflate64Stream(Stream stream, CompressionMode mode, - CompressionLevel level = CompressionLevel.Default, - bool leaveOpen = false) + public Deflate64Stream(Stream stream, CompressionMode mode) { if (stream == null) throw new ArgumentNullException(nameof(stream)); @@ -32,13 +29,13 @@ namespace SharpCompress.Compressors.Deflate64 if (!stream.CanRead) throw new ArgumentException("Deflate64: input stream is not readable", nameof(stream)); - InitializeInflater(stream, leaveOpen, ZipCompressionMethod.Deflate64); + InitializeInflater(stream, ZipCompressionMethod.Deflate64); } /// /// Sets up this DeflateManagedStream to be used for Inflation/Decompression /// - internal void InitializeInflater(Stream stream, bool leaveOpen, ZipCompressionMethod method = ZipCompressionMethod.Deflate) + internal void InitializeInflater(Stream stream, ZipCompressionMethod method = ZipCompressionMethod.Deflate) { Debug.Assert(stream != null); Debug.Assert(method == ZipCompressionMethod.Deflate || method == ZipCompressionMethod.Deflate64); @@ -49,7 +46,6 @@ namespace SharpCompress.Compressors.Deflate64 _stream = stream; _mode = CompressionMode.Decompress; - _leaveOpen = leaveOpen; _buffer = new byte[DefaultBufferSize]; } @@ -234,8 +230,10 @@ namespace SharpCompress.Compressors.Deflate64 // In this case, we still need to clean up internal resources, hence the inner finally blocks. try { - if (disposing && !_leaveOpen && _stream != null) - _stream.Dispose(); + if (disposing) + { + _stream?.Dispose(); + } } finally { diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.cs index fbb78f5e..d3f95e11 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.cs @@ -19,15 +19,13 @@ namespace SharpCompress.Compressors.LZMA private readonly Stream stream; private readonly CountingWritableSubStream rawStream; private bool disposed; - private readonly bool leaveOpen; private bool finished; private long writeCount; - public LZipStream(Stream stream, CompressionMode mode, bool leaveOpen = false) + public LZipStream(Stream stream, CompressionMode mode) { Mode = mode; - this.leaveOpen = leaveOpen; if (mode == CompressionMode.Decompress) { @@ -87,10 +85,7 @@ namespace SharpCompress.Compressors.LZMA if (disposing) { Finish(); - if (!leaveOpen) - { - rawStream.Dispose(); - } + rawStream?.Dispose(); } } diff --git a/src/SharpCompress/IO/NonDisposingStream.cs b/src/SharpCompress/IO/NonDisposingStream.cs index 0c5363d1..b5c7b834 100644 --- a/src/SharpCompress/IO/NonDisposingStream.cs +++ b/src/SharpCompress/IO/NonDisposingStream.cs @@ -1,17 +1,24 @@ -using System.IO; +using System; +using System.IO; namespace SharpCompress.IO { internal class NonDisposingStream : Stream { - public NonDisposingStream(Stream stream) + public NonDisposingStream(Stream stream, bool throwOnDispose = false) { Stream = stream; + ThrowOnDispose = false; } + public bool ThrowOnDispose { get; set; } + protected override void Dispose(bool disposing) { - //don't dispose anything + if (ThrowOnDispose) + { + throw new InvalidOperationException(); + } } public Stream Stream { get; } diff --git a/src/SharpCompress/Readers/ReaderFactory.cs b/src/SharpCompress/Readers/ReaderFactory.cs index cf22fcc0..ec7d15c9 100644 --- a/src/SharpCompress/Readers/ReaderFactory.cs +++ b/src/SharpCompress/Readers/ReaderFactory.cs @@ -58,7 +58,7 @@ namespace SharpCompress.Readers if (BZip2Stream.IsBZip2(rewindableStream)) { rewindableStream.Rewind(false); - BZip2Stream testStream = new BZip2Stream(rewindableStream, CompressionMode.Decompress, true); + BZip2Stream testStream = new BZip2Stream(new NonDisposingStream(rewindableStream), CompressionMode.Decompress, false); if (TarArchive.IsTarFile(testStream)) { rewindableStream.Rewind(true); @@ -70,7 +70,7 @@ namespace SharpCompress.Readers if (LZipStream.IsLZipFile(rewindableStream)) { rewindableStream.Rewind(false); - LZipStream testStream = new LZipStream(rewindableStream, CompressionMode.Decompress, true); + LZipStream testStream = new LZipStream(new NonDisposingStream(rewindableStream), CompressionMode.Decompress); if (TarArchive.IsTarFile(testStream)) { rewindableStream.Rewind(true); diff --git a/src/SharpCompress/Readers/Tar/TarReader.cs b/src/SharpCompress/Readers/Tar/TarReader.cs index 809ea3b5..f83e444e 100644 --- a/src/SharpCompress/Readers/Tar/TarReader.cs +++ b/src/SharpCompress/Readers/Tar/TarReader.cs @@ -102,7 +102,7 @@ namespace SharpCompress.Readers.Tar if (LZipStream.IsLZipFile(rewindableStream)) { rewindableStream.Rewind(false); - LZipStream testStream = new LZipStream(rewindableStream, CompressionMode.Decompress, false); + LZipStream testStream = new LZipStream(rewindableStream, CompressionMode.Decompress); if (TarArchive.IsTarFile(testStream)) { rewindableStream.Rewind(true); diff --git a/src/SharpCompress/Writers/GZip/GZipWriter.cs b/src/SharpCompress/Writers/GZip/GZipWriter.cs index 978fcf60..735d89f5 100644 --- a/src/SharpCompress/Writers/GZip/GZipWriter.cs +++ b/src/SharpCompress/Writers/GZip/GZipWriter.cs @@ -3,6 +3,7 @@ using System.IO; using SharpCompress.Common; using SharpCompress.Compressors; using SharpCompress.Compressors.Deflate; +using SharpCompress.IO; namespace SharpCompress.Writers.GZip { @@ -13,9 +14,12 @@ namespace SharpCompress.Writers.GZip public GZipWriter(Stream destination, GZipWriterOptions options = null) : base(ArchiveType.GZip, options ?? new GZipWriterOptions()) { + if (WriterOptions.LeaveStreamOpen) + { + destination = new NonDisposingStream(destination); + } InitalizeStream(new GZipStream(destination, CompressionMode.Compress, - options?.CompressionLevel ?? CompressionLevel.Default, - WriterOptions.LeaveStreamOpen, + options?.CompressionLevel ?? CompressionLevel.Default, WriterOptions.ArchiveEncoding.GetEncoding())); } diff --git a/src/SharpCompress/Writers/Tar/TarWriter.cs b/src/SharpCompress/Writers/Tar/TarWriter.cs index 659c60e1..f6f06c56 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.cs @@ -6,6 +6,7 @@ using SharpCompress.Compressors; using SharpCompress.Compressors.BZip2; using SharpCompress.Compressors.Deflate; using SharpCompress.Compressors.LZMA; +using SharpCompress.IO; namespace SharpCompress.Writers.Tar { @@ -22,23 +23,27 @@ namespace SharpCompress.Writers.Tar { throw new ArgumentException("Tars require writable streams."); } + if (WriterOptions.LeaveStreamOpen) + { + destination = new NonDisposingStream(destination); + } switch (options.CompressionType) { case CompressionType.None: break; case CompressionType.BZip2: { - destination = new BZip2Stream(destination, CompressionMode.Compress, true); + destination = new BZip2Stream(destination, CompressionMode.Compress, false); } break; case CompressionType.GZip: { - destination = new GZipStream(destination, CompressionMode.Compress, true); + destination = new GZipStream(destination, CompressionMode.Compress); } break; case CompressionType.LZip: { - destination = new LZipStream(destination, CompressionMode.Compress, true); + destination = new LZipStream(destination, CompressionMode.Compress); } break; default: diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index 3a29ac63..85e0c8dd 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -37,6 +37,11 @@ namespace SharpCompress.Writers.Zip compressionType = zipWriterOptions.CompressionType; compressionLevel = zipWriterOptions.DeflateCompressionLevel; + + if (WriterOptions.LeaveStreamOpen) + { + destination = new NonDisposingStream(destination); + } InitalizeStream(destination); } @@ -312,12 +317,11 @@ namespace SharpCompress.Writers.Zip } case ZipCompressionMethod.Deflate: { - return new DeflateStream(counting, CompressionMode.Compress, compressionLevel, - true); + return new DeflateStream(counting, CompressionMode.Compress, compressionLevel); } case ZipCompressionMethod.BZip2: { - return new BZip2Stream(counting, CompressionMode.Compress, true); + return new BZip2Stream(counting, CompressionMode.Compress, false); } case ZipCompressionMethod.LZMA: { diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index 757b10a1..98546313 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using SharpCompress.Archives; using SharpCompress.Common; +using SharpCompress.IO; using SharpCompress.Readers; using Xunit; @@ -23,7 +24,7 @@ namespace SharpCompress.Test foreach (var path in testArchives) { ResetScratch(); - using (Stream stream = File.OpenRead(path)) + using (var stream = new NonDisposingStream(File.OpenRead(path), true)) using (var archive = ArchiveFactory.Open(stream)) { Assert.True(archive.IsSolid); @@ -46,6 +47,7 @@ namespace SharpCompress.Test Overwrite = true }); } + stream.ThrowOnDispose = false; } VerifyFiles(); } @@ -67,7 +69,7 @@ namespace SharpCompress.Test foreach (var path in testArchives) { ResetScratch(); - using (Stream stream = File.OpenRead(path)) + using (var stream = new NonDisposingStream(File.OpenRead(path))) using (var archive = ArchiveFactory.Open(stream, readerOptions)) { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) @@ -78,6 +80,7 @@ namespace SharpCompress.Test Overwrite = true }); } + stream.ThrowOnDispose = false; } VerifyFiles(); } From f18e5b75bbf5c9167b1808b36aa1191436605970 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 22 Apr 2018 10:06:30 +0100 Subject: [PATCH 088/124] Archives set up correctly --- src/SharpCompress/IO/NonDisposingStream.cs | 2 +- tests/SharpCompress.Test/ArchiveTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SharpCompress/IO/NonDisposingStream.cs b/src/SharpCompress/IO/NonDisposingStream.cs index b5c7b834..b1b87a06 100644 --- a/src/SharpCompress/IO/NonDisposingStream.cs +++ b/src/SharpCompress/IO/NonDisposingStream.cs @@ -8,7 +8,7 @@ namespace SharpCompress.IO public NonDisposingStream(Stream stream, bool throwOnDispose = false) { Stream = stream; - ThrowOnDispose = false; + ThrowOnDispose = throwOnDispose; } public bool ThrowOnDispose { get; set; } diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index 98546313..992c8030 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -69,7 +69,7 @@ namespace SharpCompress.Test foreach (var path in testArchives) { ResetScratch(); - using (var stream = new NonDisposingStream(File.OpenRead(path))) + using (var stream = new NonDisposingStream(File.OpenRead(path), true)) using (var archive = ArchiveFactory.Open(stream, readerOptions)) { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) From bf58742ddffb0252e15f4804f4c29a822027b536 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 22 Apr 2018 11:09:03 +0100 Subject: [PATCH 089/124] rework of leave stream open for readers --- .../Archives/SevenZip/SevenZipArchive.cs | 2 +- src/SharpCompress/Common/Volume.cs | 8 ++++-- .../Rar/MultiVolumeReadOnlyStream.cs | 10 +------ src/SharpCompress/Readers/AbstractReader.cs | 20 +++++--------- src/SharpCompress/Readers/GZip/GZipReader.cs | 2 +- .../Readers/Rar/MultiVolumeRarReader.cs | 2 +- src/SharpCompress/Readers/Rar/RarReader.cs | 2 +- .../Readers/Rar/SingleVolumeRarReader.cs | 2 +- src/SharpCompress/Readers/Tar/TarReader.cs | 4 +-- src/SharpCompress/Readers/Zip/ZipReader.cs | 2 +- tests/SharpCompress.Test/ArchiveTests.cs | 27 +++++++++++++++---- 11 files changed, 43 insertions(+), 38 deletions(-) diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index 48182b62..dbc3df8b 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -174,7 +174,7 @@ namespace SharpCompress.Archives.SevenZip public override SevenZipVolume Volume => archive.Volumes.Single(); - internal override IEnumerable GetEntries(Stream stream) + protected override IEnumerable GetEntries(Stream stream) { List entries = archive.Entries.ToList(); stream.Position = 0; diff --git a/src/SharpCompress/Common/Volume.cs b/src/SharpCompress/Common/Volume.cs index 93cbfefa..eab39d4d 100644 --- a/src/SharpCompress/Common/Volume.cs +++ b/src/SharpCompress/Common/Volume.cs @@ -10,11 +10,15 @@ namespace SharpCompress.Common internal Volume(Stream stream, ReaderOptions readerOptions) { - actualStream = stream; ReaderOptions = readerOptions; + if (readerOptions.LeaveStreamOpen) + { + stream = new NonDisposingStream(stream); + } + actualStream = stream; } - internal Stream Stream => new NonDisposingStream(actualStream); + internal Stream Stream => actualStream; protected ReaderOptions ReaderOptions { get; } diff --git a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs index d5618d5f..f89ea0f4 100644 --- a/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs +++ b/src/SharpCompress/Compressors/Rar/MultiVolumeReadOnlyStream.cs @@ -39,11 +39,7 @@ namespace SharpCompress.Compressors.Rar filePartEnumerator.Dispose(); filePartEnumerator = null; } - if (currentStream != null) - { - currentStream.Dispose(); - currentStream = null; - } + currentStream = null; } } @@ -51,10 +47,6 @@ namespace SharpCompress.Compressors.Rar { maxPosition = filePartEnumerator.Current.FileHeader.CompressedSize; currentPosition = 0; - if (currentStream != null) - { - currentStream.Dispose(); - } currentStream = filePartEnumerator.Current.GetCompressedStream(); currentPartTotalReadBytes = 0; diff --git a/src/SharpCompress/Readers/AbstractReader.cs b/src/SharpCompress/Readers/AbstractReader.cs index a5cc157e..0424f21a 100644 --- a/src/SharpCompress/Readers/AbstractReader.cs +++ b/src/SharpCompress/Readers/AbstractReader.cs @@ -97,12 +97,9 @@ namespace SharpCompress.Readers return false; } - internal bool LoadStreamForReading(Stream stream) + protected bool LoadStreamForReading(Stream stream) { - if (entriesForCurrentReadStream != null) - { - entriesForCurrentReadStream.Dispose(); - } + entriesForCurrentReadStream?.Dispose(); if ((stream == null) || (!stream.CanRead)) { throw new MultipartStreamRequiredException("File is split into multiple archives: '" @@ -110,14 +107,10 @@ namespace SharpCompress.Readers "'. A new readable stream is required. Use Cancel if it was intended."); } entriesForCurrentReadStream = GetEntries(stream).GetEnumerator(); - if (entriesForCurrentReadStream.MoveNext()) - { - return true; - } - return false; + return entriesForCurrentReadStream.MoveNext(); } - internal virtual Stream RequestInitialStream() + protected virtual Stream RequestInitialStream() { return Volume.Stream; } @@ -127,7 +120,7 @@ namespace SharpCompress.Readers return entriesForCurrentReadStream.MoveNext(); } - internal abstract IEnumerable GetEntries(Stream stream); + protected abstract IEnumerable GetEntries(Stream stream); #region Entry Skip/Write @@ -172,8 +165,7 @@ namespace SharpCompress.Readers } if ((writableStream == null) || (!writableStream.CanWrite)) { - throw new ArgumentNullException( - "A writable Stream was required. Use Cancel if that was intended."); + throw new ArgumentNullException("A writable Stream was required. Use Cancel if that was intended."); } Write(writableStream); diff --git a/src/SharpCompress/Readers/GZip/GZipReader.cs b/src/SharpCompress/Readers/GZip/GZipReader.cs index b8265409..ecee69ff 100644 --- a/src/SharpCompress/Readers/GZip/GZipReader.cs +++ b/src/SharpCompress/Readers/GZip/GZipReader.cs @@ -31,7 +31,7 @@ namespace SharpCompress.Readers.GZip #endregion Open - internal override IEnumerable GetEntries(Stream stream) + protected override IEnumerable GetEntries(Stream stream) { return GZipEntry.GetEntries(stream, Options); } diff --git a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs index 7bc61e2e..3de56052 100644 --- a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs +++ b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs @@ -22,7 +22,7 @@ namespace SharpCompress.Readers.Rar { } - internal override Stream RequestInitialStream() + protected override Stream RequestInitialStream() { if (streams.MoveNext()) { diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index 92e4d737..d82de9cf 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -52,7 +52,7 @@ namespace SharpCompress.Readers.Rar #endregion - internal override IEnumerable GetEntries(Stream stream) + protected override IEnumerable GetEntries(Stream stream) { volume = new RarReaderVolume(stream, Options); foreach (RarFilePart fp in volume.ReadFileParts()) diff --git a/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs b/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs index 3ffede69..f98ec9b4 100644 --- a/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs +++ b/src/SharpCompress/Readers/Rar/SingleVolumeRarReader.cs @@ -23,7 +23,7 @@ namespace SharpCompress.Readers.Rar } } - internal override Stream RequestInitialStream() + protected override Stream RequestInitialStream() { return stream; } diff --git a/src/SharpCompress/Readers/Tar/TarReader.cs b/src/SharpCompress/Readers/Tar/TarReader.cs index f83e444e..e6c67a92 100644 --- a/src/SharpCompress/Readers/Tar/TarReader.cs +++ b/src/SharpCompress/Readers/Tar/TarReader.cs @@ -27,7 +27,7 @@ namespace SharpCompress.Readers.Tar public override TarVolume Volume { get; } - internal override Stream RequestInitialStream() + protected override Stream RequestInitialStream() { var stream = base.RequestInitialStream(); switch (compressionType) @@ -116,7 +116,7 @@ namespace SharpCompress.Readers.Tar #endregion Open - internal override IEnumerable GetEntries(Stream stream) + protected override IEnumerable GetEntries(Stream stream) { return TarEntry.GetEntries(StreamingMode.Streaming, stream, compressionType, Options.ArchiveEncoding); } diff --git a/src/SharpCompress/Readers/Zip/ZipReader.cs b/src/SharpCompress/Readers/Zip/ZipReader.cs index 6dfbefb6..5b534610 100644 --- a/src/SharpCompress/Readers/Zip/ZipReader.cs +++ b/src/SharpCompress/Readers/Zip/ZipReader.cs @@ -35,7 +35,7 @@ namespace SharpCompress.Readers.Zip #endregion Open - internal override IEnumerable GetEntries(Stream stream) + protected override IEnumerable GetEntries(Stream stream) { foreach (ZipHeader h in _headerFactory.ReadStreamHeader(stream)) { diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index 992c8030..4ce9f872 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -36,6 +36,7 @@ namespace SharpCompress.Test if (archive.Entries.First().CompressionType == CompressionType.Rar) { + stream.ThrowOnDispose = false; return; } foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) @@ -72,13 +73,29 @@ namespace SharpCompress.Test using (var stream = new NonDisposingStream(File.OpenRead(path), true)) using (var archive = ArchiveFactory.Open(stream, readerOptions)) { - foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) + try { - entry.WriteToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - ExtractFullPath = true, - Overwrite = true - }); + entry.WriteToDirectory(SCRATCH_FILES_PATH, + new ExtractionOptions() + { + ExtractFullPath = true, + Overwrite = true + }); + } + } + catch (InvalidFormatException) + { + //rar SOLID test needs this + stream.ThrowOnDispose = false; + throw; + } + catch (IndexOutOfRangeException) + { + //SevenZipArchive_BZip2_Split test needs this + stream.ThrowOnDispose = false; + throw; } stream.ThrowOnDispose = false; } From 68a5e474a6a96a0f74e185a33e633223c7f7812c Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 22 Apr 2018 11:19:11 +0100 Subject: [PATCH 090/124] More testing of file handling --- tests/SharpCompress.Test/ReaderTests.cs | 6 +++-- tests/SharpCompress.Test/TestBase.cs | 34 +++++++++++++++++++++++++ tests/SharpCompress.Test/WriterTests.cs | 2 +- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/tests/SharpCompress.Test/ReaderTests.cs b/tests/SharpCompress.Test/ReaderTests.cs index 1de62c59..3652dabf 100644 --- a/tests/SharpCompress.Test/ReaderTests.cs +++ b/tests/SharpCompress.Test/ReaderTests.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.IO; using SharpCompress.Common; +using SharpCompress.IO; using SharpCompress.Readers; using Xunit; @@ -18,10 +19,11 @@ namespace SharpCompress.Test { foreach (var path in testArchives) { - using (Stream stream = new ForwardOnlyStream(File.OpenRead(path))) - using (IReader reader = ReaderFactory.Open(stream)) + using (var stream = new NonDisposingStream(new ForwardOnlyStream(File.OpenRead(path)), true)) + using (var reader = ReaderFactory.Open(stream)) { UseReader(this, reader, expectedCompression); + stream.ThrowOnDispose = false; } } } diff --git a/tests/SharpCompress.Test/TestBase.cs b/tests/SharpCompress.Test/TestBase.cs index 01ffc9cf..d8e5b587 100644 --- a/tests/SharpCompress.Test/TestBase.cs +++ b/tests/SharpCompress.Test/TestBase.cs @@ -185,6 +185,15 @@ namespace SharpCompress.Test { return; } + + if (IsFileLocked(new FileInfo(file1))) + { + throw new InvalidOperationException($"{file1} is not disposed"); + } + if (IsFileLocked(new FileInfo(file2))) + { + throw new InvalidOperationException($"{file2} is not disposed"); + } using (var file1Stream = File.OpenRead(file1)) using (var file2Stream = File.OpenRead(file2)) { @@ -235,6 +244,31 @@ namespace SharpCompress.Test Assert.Equal(archive1Entries[i], archive2Entries[i]); } } + + protected bool IsFileLocked(FileInfo file) + { + FileStream stream = null; + + try + { + stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None); + } + catch (IOException) + { + //the file is unavailable because it is: + //still being written to + //or being processed by another thread + //or does not exist (has already been processed) + return true; + } + finally + { + stream?.Close(); + } + + //file is not locked + return false; + } private static readonly object lockObject = new object(); diff --git a/tests/SharpCompress.Test/WriterTests.cs b/tests/SharpCompress.Test/WriterTests.cs index e3b614c8..bb4ff4bd 100644 --- a/tests/SharpCompress.Test/WriterTests.cs +++ b/tests/SharpCompress.Test/WriterTests.cs @@ -21,7 +21,7 @@ namespace SharpCompress.Test ResetScratch(); using (Stream stream = File.OpenWrite(Path.Combine(SCRATCH2_FILES_PATH, archive))) { - using (var writer = WriterFactory.Open(new NonDisposingStream(stream), type, new WriterOptions(compressionType) + using (var writer = WriterFactory.Open(stream, type, new WriterOptions(compressionType) { LeaveStreamOpen = true })) From 89ae8ca5260cea5efeead1deacfd0836e9ec14e3 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 22 Apr 2018 11:32:47 +0100 Subject: [PATCH 091/124] Rejigger read only substream --- src/SharpCompress/IO/NonDisposingStream.cs | 2 +- src/SharpCompress/IO/ReadOnlySubStream.cs | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/SharpCompress/IO/NonDisposingStream.cs b/src/SharpCompress/IO/NonDisposingStream.cs index b1b87a06..1b1b4939 100644 --- a/src/SharpCompress/IO/NonDisposingStream.cs +++ b/src/SharpCompress/IO/NonDisposingStream.cs @@ -21,7 +21,7 @@ namespace SharpCompress.IO } } - public Stream Stream { get; } + protected Stream Stream { get; } public override bool CanRead => Stream.CanRead; diff --git a/src/SharpCompress/IO/ReadOnlySubStream.cs b/src/SharpCompress/IO/ReadOnlySubStream.cs index 48609526..a8449c41 100644 --- a/src/SharpCompress/IO/ReadOnlySubStream.cs +++ b/src/SharpCompress/IO/ReadOnlySubStream.cs @@ -3,7 +3,7 @@ using System.IO; namespace SharpCompress.IO { - internal class ReadOnlySubStream : Stream + internal class ReadOnlySubStream : NonDisposingStream { public ReadOnlySubStream(Stream stream, long bytesToRead) : this(stream, null, bytesToRead) @@ -11,8 +11,8 @@ namespace SharpCompress.IO } public ReadOnlySubStream(Stream stream, long? origin, long bytesToRead) + : base(stream, false) { - Stream = stream; if (origin != null) { stream.Position = origin.Value; @@ -20,18 +20,8 @@ namespace SharpCompress.IO BytesLeftToRead = bytesToRead; } - protected override void Dispose(bool disposing) - { - if (disposing) - { - //Stream.Dispose(); - } - } - private long BytesLeftToRead { get; set; } - public Stream Stream { get; } - public override bool CanRead => true; public override bool CanSeek => false; From f3daaeb200de9289d4ea40dabd40d15834ecf7d3 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 23 Apr 2018 09:39:50 +0100 Subject: [PATCH 092/124] Try to use both for Rarv5 support --- src/SharpCompress/Archives/Rar/RarArchive.cs | 14 +++---------- .../Archives/Rar/RarArchiveEntry.cs | 10 ++++++++-- .../Common/Rar/Headers/MarkHeader.cs | 12 ----------- src/SharpCompress/Common/Rar/RarEntry.cs | 5 +++++ .../Compressors/Rar/UnpackV1/UnpackInline.cs | 7 +------ .../Rar/UnpackV2017/Unpack.unpack30_cpp.cs | 3 ++- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 4 ++-- .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 12 +++++------ src/SharpCompress/Readers/Rar/RarReader.cs | 20 ++++++++++--------- src/SharpCompress/SharpCompress.csproj | 2 +- src/SharpCompress/Utility.cs | 5 ----- 11 files changed, 39 insertions(+), 55 deletions(-) diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index 699073cb..fb052c37 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -12,7 +13,8 @@ namespace SharpCompress.Archives.Rar { public class RarArchive : AbstractArchive { - internal IRarUnpack Unpack { get; } + internal Lazy UnpackV2017 { get; } = new Lazy(() => new SharpCompress.Compressors.Rar.UnpackV2017.Unpack()); + internal Lazy UnpackV1 { get; } = new Lazy(() => new SharpCompress.Compressors.Rar.UnpackV1.Unpack()); #if !NO_FILE @@ -24,11 +26,6 @@ namespace SharpCompress.Archives.Rar internal RarArchive(FileInfo fileInfo, ReaderOptions options) : base(ArchiveType.Rar, fileInfo, options) { -#if !RarV2017_USELEGACY - Unpack = new SharpCompress.Compressors.Rar.UnpackV2017.Unpack(); -#else - Unpack = new SharpCompress.Compressors.Rar.UnpackV1.Unpack(); -#endif } protected override IEnumerable LoadVolumes(FileInfo file) @@ -45,11 +42,6 @@ namespace SharpCompress.Archives.Rar internal RarArchive(IEnumerable streams, ReaderOptions options) : base(ArchiveType.Rar, streams, options) { -#if !RarV2017_USELEGACY - Unpack = new SharpCompress.Compressors.Rar.UnpackV2017.Unpack(); -#else - Unpack = new SharpCompress.Compressors.Rar.UnpackV1.Unpack(); -#endif } protected override IEnumerable LoadEntries(IEnumerable volumes) diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs index 064dcf7a..60ece4ee 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs @@ -51,7 +51,7 @@ namespace SharpCompress.Archives.Rar get { CheckIncomplete(); - return parts.Aggregate(0L, (total, fp) => { return total + fp.FileHeader.CompressedSize; }); + return parts.Aggregate(0L, (total, fp) => total + fp.FileHeader.CompressedSize); } } @@ -61,7 +61,13 @@ namespace SharpCompress.Archives.Rar { throw new InvalidOperationException("Use ExtractAllEntries to extract SOLID archives."); } - return new RarStream(archive.Unpack, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); + + if (IsRarV3) + { + return new RarStream(archive.UnpackV1.Value, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); + } + + return new RarStream(archive.UnpackV2017.Value, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); } public bool IsComplete diff --git a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs index 4e972913..c7b94149 100644 --- a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs @@ -67,10 +67,6 @@ namespace SharpCompress.Common.Rar.Headers { return new MarkHeader(false); // Rar4 } - else - { - continue; - } } else if (b == 0x45) { @@ -80,19 +76,11 @@ namespace SharpCompress.Common.Rar.Headers if (b != 0x5e) continue; throw new InvalidFormatException("Rar format version pre-4 is unsupported."); } - else - { - continue; - } } else { b = GetByte(stream); start++; - continue; } - - // unreachable - throw new InvalidOperationException(); } } catch (Exception e) diff --git a/src/SharpCompress/Common/Rar/RarEntry.cs b/src/SharpCompress/Common/Rar/RarEntry.cs index cec1eb44..85be0bb3 100644 --- a/src/SharpCompress/Common/Rar/RarEntry.cs +++ b/src/SharpCompress/Common/Rar/RarEntry.cs @@ -7,6 +7,11 @@ namespace SharpCompress.Common.Rar { internal abstract FileHeader FileHeader { get; } + /// + /// As the V2017 port isn't complete, add this check to use the legacy Rar code. + /// + internal bool IsRarV3 => FileHeader.CompressionAlgorithm == 29 || FileHeader.CompressionAlgorithm == 36; + /// /// The File's 32 bit CRC Hash /// diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs index e5ca5b1e..35b65eb7 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace SharpCompress.Compressors.Rar.UnpackV1 { internal partial class Unpack diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs index 8ec81763..7e77c7d4 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack30_cpp.cs @@ -13,7 +13,7 @@ using System; using static SharpCompress.Compressors.Rar.UnpackV2017.PackDef; using static SharpCompress.Compressors.Rar.UnpackV2017.UnpackGlobal; //using static SharpCompress.Compressors.Rar.UnpackV2017.Unpack.Unpack30Local; - +/* namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack @@ -797,3 +797,4 @@ void InitFilters30(bool Solid) } } +*/ \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index 0fa14295..44abffa3 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -167,7 +167,7 @@ void DoUnpack(uint Method,bool Solid) #if !RarV2017_RAR5ONLY case 29: // rar 3.x compression if (!Fragmented) - Unpack29(Solid); + throw new NotImplementedException(); break; #endif case 50: // RAR 5.0 compression algorithm. @@ -225,7 +225,7 @@ void UnpInitData(bool Solid) #if !RarV2017_SFX_MODULE UnpInitData20(Solid); #endif - UnpInitData30(Solid); + //UnpInitData30(Solid); UnpInitData50(Solid); } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index afea642c..06520ab5 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -196,9 +196,9 @@ class UnpackFilter30 // in PrgStack array. Not defined for filters in Filters array. public uint ParentFilter; -#if !RarV2017_RAR5ONLY +/*#if !RarV2017_RAR5ONLY public VM_PreparedProgram Prg; -#endif +#endif*/ }; @@ -374,9 +374,9 @@ internal partial class Unpack int PrevLowDist,LowDistRepCount; -#if !RarV2017_RAR5ONLY +/*#if !RarV2017_RAR5ONLY ModelPPM PPM; -#endif +#endif*/ int PPMEscChar; readonly byte [] UnpOldTable = new byte[HUFF_TABLE_SIZE30]; @@ -390,9 +390,9 @@ internal partial class Unpack bool TablesRead2,TablesRead3,TablesRead5; // Virtual machine to execute filters code. -#if !RarV2017_RAR5ONLY +/*#if !RarV2017_RAR5ONLY RarVM VM; -#endif +#endif*/ // Buffer to read VM filters code. We moved it here from AddVMCode // function to reduce time spent in BitInput constructor. diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index 71fe260c..05fe0daa 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; using System.Linq; using SharpCompress.Common; @@ -13,16 +14,12 @@ namespace SharpCompress.Readers.Rar public abstract class RarReader : AbstractReader { private RarVolume volume; - private readonly IRarUnpack pack; + internal Lazy UnpackV2017 { get; } = new Lazy(() => new SharpCompress.Compressors.Rar.UnpackV2017.Unpack()); + internal Lazy UnpackV1 { get; } = new Lazy(() => new SharpCompress.Compressors.Rar.UnpackV1.Unpack()); internal RarReader(ReaderOptions options) : base(options, ArchiveType.Rar) { -#if !RarV2017_USELEGACY - this.pack = new SharpCompress.Compressors.Rar.UnpackV2017.Unpack(); -#else - this.pack = new SharpCompress.Compressors.Rar.UnpackV1.Unpack(); -#endif } internal abstract void ValidateArchive(RarVolume archive); @@ -68,9 +65,14 @@ namespace SharpCompress.Readers.Rar return Entry.Parts; } - protected override EntryStream GetEntryStream() { + protected override EntryStream GetEntryStream() + { var stream = new MultiVolumeReadOnlyStream(CreateFilePartEnumerableForCurrentEntry().Cast(), this); - return CreateEntryStream(new RarCrcStream(pack, Entry.FileHeader, stream)); + if (Entry.IsRarV3) + { + return CreateEntryStream(new RarCrcStream(UnpackV1.Value, Entry.FileHeader, stream)); + } + return CreateEntryStream(new RarCrcStream(UnpackV2017.Value, Entry.FileHeader, stream)); } } } diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 5c1d0815..a20a92a9 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -6,7 +6,7 @@ 0.20.0 0.20.0 Adam Hathcock - net45;net35;netstandard1.0;netstandard1.3;netstandard2.0 + netstandard2.0 true true SharpCompress diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index 320a59c6..bca28a9d 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -427,10 +427,5 @@ namespace SharpCompress } return true; } - - public static void CopyTo(this byte[] array, byte[] destination, int index) - { - Array.Copy(array, 0, destination, index, array.Length); - } } } \ No newline at end of file From d38276e8cfec7adc82fd980710a5f4a100407016 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 23 Apr 2018 10:29:46 +0100 Subject: [PATCH 093/124] Fix solid and some other tests --- .gitignore | 2 ++ .../Archives/Rar/RarArchiveEntry.cs | 4 ++-- .../Common/Rar/Headers/ArchiveHeader.cs | 3 ++- .../Common/Rar/Headers/FileHeader.cs | 8 -------- src/SharpCompress/Common/Rar/Headers/Flags.cs | 2 +- .../Common/Rar/Headers/RarHeaderFactory.cs | 5 +++-- src/SharpCompress/Compressors/Rar/IRarUnpack.cs | 4 ++-- .../Compressors/Rar/RarCrcStream.cs | 15 +++++++++------ src/SharpCompress/Compressors/Rar/RarStream.cs | 8 +++++--- .../Compressors/Rar/UnpackV1/Unpack.cs | 17 ++++++++--------- .../Compressors/Rar/UnpackV2017/Unpack.cs | 10 +++++----- src/SharpCompress/Readers/AbstractReader.cs | 7 ++----- src/SharpCompress/Readers/Rar/RarReader.cs | 4 ++-- tests/SharpCompress.Test/Rar/RarArchiveTests.cs | 8 ++++---- tests/SharpCompress.Test/Rar/RarReaderTests.cs | 10 +++++----- 15 files changed, 52 insertions(+), 55 deletions(-) diff --git a/.gitignore b/.gitignore index 2e0b14fa..cc539531 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ tests/TestArchives/Scratch tools .vscode .idea/ + +.DS_Store diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs index 60ece4ee..d4197b3f 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs @@ -64,10 +64,10 @@ namespace SharpCompress.Archives.Rar if (IsRarV3) { - return new RarStream(archive.UnpackV1.Value, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); + return new RarStream(archive.UnpackV1.Value, archive.IsSolid, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); } - return new RarStream(archive.UnpackV2017.Value, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); + return new RarStream(archive.UnpackV2017.Value, archive.IsSolid, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); } public bool IsComplete diff --git a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs index 83eda7f1..bc4955d0 100644 --- a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs @@ -73,7 +73,8 @@ namespace SharpCompress.Common.Rar.Headers public bool IsVolume => HasFlag(IsRar5 ? ArchiveFlagsV5.Volume : ArchiveFlagsV4.Volume); - public bool IsFirstVolume => IsRar5 ? VolumeNumber == 1 : HasFlag(ArchiveFlagsV4.FirstVolume); + // RAR5: Volume number field is present. True for all volumes except first. + public bool IsFirstVolume => IsRar5 ? VolumeNumber == null : HasFlag(ArchiveFlagsV4.FirstVolume); public bool IsSolid => HasFlag(IsRar5 ? ArchiveFlagsV5.Solid : ArchiveFlagsV4.Solid); } diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index 48d2f3e0..55a13322 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -64,11 +64,6 @@ namespace SharpCompress.Common.Rar.Headers // them. CompressionAlgorithm = (byte)((us & 0x3f) + 50); - // 7th bit (0x0040) defines the solid flag. If it is set, RAR continues to use the compression dictionary left after processing preceding files. - // It can be set only for file headers and is never set for service headers. - IsSolid = (us & 0x40) == 0x40; - if (IsSolid != HasHeaderFlag(HeaderFlagsV5.Solid_TESTME)) throw new InvalidFormatException("rar solid flag base header != file header"); - // Bits 8 - 10 (0x0380 mask) define the compression method. Currently only values 0 - 5 are used. 0 means no compression. CompressionMethod = (byte)((us >> 7) & 0x7); @@ -210,7 +205,6 @@ namespace SharpCompress.Common.Rar.Headers private void ReadFromReaderV4(MarkingBinaryReader reader) { Flags = HeaderFlags; - IsSolid = HasFlag(FileFlagsV4.Solid); WindowSize = IsDirectory ? 0U : ((size_t)0x10000) << ((Flags & FileFlagsV4.WindowMask) >> 5); uint lowUncompressedSize = reader.ReadUInt32(); @@ -415,8 +409,6 @@ namespace SharpCompress.Common.Rar.Headers //case 50: // RAR 5.0 compression algorithm. internal byte CompressionAlgorithm { get; private set; } - public bool IsSolid { get; private set; } - // unused for UnpackV1 implementation (limitation) internal size_t WindowSize { get; private set; } diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index 746b9018..9c5060e9 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -47,7 +47,7 @@ namespace SharpCompress.Common.Rar.Headers public const ushort Keep = 0x0004; // block must be kept during an update public const ushort SplitBefore = 0x0008; public const ushort SplitAfter = 0x0010; - public const ushort Solid_TESTME = 0x0020; // ??? Block depends on preceding file block. + public const ushort Child = 0x0020; // ??? Block depends on preceding file block. public const ushort PreserveChild = 0x0040; // ???? Preserve a child block if host block is modified } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index 05660583..cb670368 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -70,8 +70,9 @@ namespace SharpCompress.Common.Rar.Headers case HeaderCodeV.Rar4ArchiveHeader: { var ah = new ArchiveHeader(header, reader); - if (ah.IsEncrypted == true) { -//!!! rar5 we don't know yet + if (ah.IsEncrypted == true) + { + //!!! rar5 we don't know yet IsEncrypted = true; } return ah; diff --git a/src/SharpCompress/Compressors/Rar/IRarUnpack.cs b/src/SharpCompress/Compressors/Rar/IRarUnpack.cs index ecd70a47..887c590e 100644 --- a/src/SharpCompress/Compressors/Rar/IRarUnpack.cs +++ b/src/SharpCompress/Compressors/Rar/IRarUnpack.cs @@ -5,8 +5,8 @@ namespace SharpCompress.Compressors.Rar { interface IRarUnpack { - void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream); - void DoUnpack(); + void DoUnpack(bool isSolid, FileHeader fileHeader, Stream readStream, Stream writeStream); + void DoUnpack(bool isSolid); // eg u/i pause/resume button bool Suspended { get; set; } diff --git a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs index 85ed3e34..2bf98579 100644 --- a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs @@ -2,12 +2,15 @@ using System.IO; using SharpCompress.Common; using SharpCompress.Common.Rar.Headers; -namespace SharpCompress.Compressors.Rar { - internal class RarCrcStream : RarStream { +namespace SharpCompress.Compressors.Rar +{ + internal class RarCrcStream : RarStream + { private readonly MultiVolumeReadOnlyStream readStream; private uint currentCrc; - public RarCrcStream(IRarUnpack unpack, FileHeader fileHeader, MultiVolumeReadOnlyStream readStream) : base(unpack, fileHeader, readStream) + public RarCrcStream(IRarUnpack unpack, bool isSolid, FileHeader fileHeader, MultiVolumeReadOnlyStream readStream) + : base(unpack, isSolid, fileHeader, readStream) { this.readStream = readStream; ResetCrc(); @@ -23,19 +26,19 @@ namespace SharpCompress.Compressors.Rar { currentCrc = 0xffffffff; } - public override int Read(byte[] buffer, int offset, int count) { var result = base.Read(buffer, offset, count); - if (result != 0) + if (result != 0) { currentCrc = RarCRC.CheckCrc(currentCrc, buffer, offset, result); - } + } else if (GetCrc() != this.readStream.CurrentCrc && count != 0) { // NOTE: we use the last FileHeader in a multipart volume to check CRC throw new InvalidFormatException("file crc mismatch"); } + return result; } } diff --git a/src/SharpCompress/Compressors/Rar/RarStream.cs b/src/SharpCompress/Compressors/Rar/RarStream.cs index 08240859..6da56697 100644 --- a/src/SharpCompress/Compressors/Rar/RarStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarStream.cs @@ -9,6 +9,7 @@ namespace SharpCompress.Compressors.Rar private readonly IRarUnpack unpack; private readonly FileHeader fileHeader; private readonly Stream readStream; + private readonly bool isSolid; private bool fetch; @@ -22,13 +23,14 @@ namespace SharpCompress.Compressors.Rar private int outTotal; private bool isDisposed; - public RarStream(IRarUnpack unpack, FileHeader fileHeader, Stream readStream) + public RarStream(IRarUnpack unpack, bool isSolid, FileHeader fileHeader, Stream readStream) { + this.isSolid = isSolid; this.unpack = unpack; this.fileHeader = fileHeader; this.readStream = readStream; this.fetch = true; - unpack.DoUnpack(fileHeader, readStream, this); + unpack.DoUnpack(isSolid, fileHeader, readStream, this); this.fetch = false; } @@ -74,7 +76,7 @@ namespace SharpCompress.Compressors.Rar outOffset = offset; outCount = count; fetch = true; - unpack.DoUnpack(); + unpack.DoUnpack(isSolid); fetch = false; } return outTotal; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs index b576361a..dd3b52dc 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs @@ -103,46 +103,45 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 UnpInitData(false); } - public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) + public void DoUnpack(bool isSolid, FileHeader fileHeader, Stream readStream, Stream writeStream) { this.destUnpSize = fileHeader.UncompressedSize; this.fileHeader = fileHeader; this.readStream = readStream; this.writeStream = writeStream; - if (!fileHeader.IsSolid) + if (!isSolid) { Init(null); } this.suspended = false; - DoUnpack(); + DoUnpack(isSolid); } - public void DoUnpack() + public void DoUnpack(bool isSolid) { if (fileHeader.CompressionMethod == 0) { UnstoreFile(); return; } - var solid = fileHeader.IsSolid; switch (fileHeader.CompressionAlgorithm) { case 15: // rar 1.5 compression - unpack15(solid); + unpack15(isSolid); break; case 20: // rar 2.x compression case 26: // files larger than 2GB - unpack20(solid); + unpack20(isSolid); break; case 29: // rar 3.x compression case 36: // alternative hash - Unpack29(solid); + Unpack29(isSolid); break; case 50: // rar 5.x compression - Unpack5(solid); + Unpack5(isSolid); break; default: diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index e40ec2ec..2d52da5b 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -37,7 +37,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 this.writeStream.Write(buf, checked((int)offset), checked((int)count)); } - public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) + public void DoUnpack(bool isSolid, FileHeader fileHeader, Stream readStream, Stream writeStream) { // as of 12/2017 .NET limits array indexing to using a signed integer // MaxWinSize causes unpack to use a fragmented window when the file @@ -51,19 +51,19 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 this.readStream = readStream; this.writeStream = writeStream; if (!fileHeader.IsStored) { - Init(fileHeader.WindowSize, fileHeader.IsSolid); + Init(fileHeader.WindowSize, isSolid); } Suspended = false; - DoUnpack(); + DoUnpack(isSolid); } - public void DoUnpack() + public void DoUnpack(bool isSolid) { if (this.fileHeader.IsStored) { UnstoreFile(); } else { - DoUnpack(this.fileHeader.CompressionAlgorithm, this.fileHeader.IsSolid); + DoUnpack(this.fileHeader.CompressionAlgorithm, isSolid); } } diff --git a/src/SharpCompress/Readers/AbstractReader.cs b/src/SharpCompress/Readers/AbstractReader.cs index a5cc157e..a488e628 100644 --- a/src/SharpCompress/Readers/AbstractReader.cs +++ b/src/SharpCompress/Readers/AbstractReader.cs @@ -46,11 +46,8 @@ namespace SharpCompress.Readers public void Dispose() { - if (entriesForCurrentReadStream != null) - { - entriesForCurrentReadStream.Dispose(); - } - Volume.Dispose(); + entriesForCurrentReadStream?.Dispose(); + Volume?.Dispose(); } #endregion diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index 05fe0daa..81f99d9b 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -70,9 +70,9 @@ namespace SharpCompress.Readers.Rar var stream = new MultiVolumeReadOnlyStream(CreateFilePartEnumerableForCurrentEntry().Cast(), this); if (Entry.IsRarV3) { - return CreateEntryStream(new RarCrcStream(UnpackV1.Value, Entry.FileHeader, stream)); + return CreateEntryStream(new RarCrcStream(UnpackV1.Value, volume.IsSolidArchive, Entry.FileHeader, stream)); } - return CreateEntryStream(new RarCrcStream(UnpackV2017.Value, Entry.FileHeader, stream)); + return CreateEntryStream(new RarCrcStream(UnpackV2017.Value, volume.IsSolidArchive, Entry.FileHeader, stream)); } } } diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index d320f782..2fab450c 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -40,11 +40,11 @@ namespace SharpCompress.Test.Rar ReadRarPassword("Rar.Encrypted.rar", "test"); } - [Fact] + /*[Fact] public void Rar5_Encrypted_Archive() { ReadRarPassword("Rar5.Encrypted.rar", "test"); - } + }*/ private void ReadRarPassword(string testArchive, string password) { @@ -78,11 +78,11 @@ namespace SharpCompress.Test.Rar Assert.Throws(() => ArchiveFileReadPassword("Rar.EncryptedParts.part01.rar", "test")); } - [Fact] + /*[Fact] public void Rar5_Multi_Archive_Encrypted() { Assert.Throws(() => ArchiveFileReadPassword("Rar5.EncryptedParts.part01.rar", "test")); - } + }*/ protected void ArchiveFileReadPassword(string archiveName, string password) { diff --git a/tests/SharpCompress.Test/Rar/RarReaderTests.cs b/tests/SharpCompress.Test/Rar/RarReaderTests.cs index bc98dfab..79ee574b 100644 --- a/tests/SharpCompress.Test/Rar/RarReaderTests.cs +++ b/tests/SharpCompress.Test/Rar/RarReaderTests.cs @@ -60,7 +60,7 @@ namespace SharpCompress.Test.Rar "Rar.EncryptedParts.part06.rar"}); } - [Fact] + /*[Fact] public void Rar5_Multi_Reader_Encrypted() { DoRar_Multi_Reader_Encrypted(new string[] { "Rar5.EncryptedParts.part01.rar", @@ -69,7 +69,7 @@ namespace SharpCompress.Test.Rar "Rar5.EncryptedParts.part04.rar", "Rar5.EncryptedParts.part05.rar", "Rar5.EncryptedParts.part06.rar"}); - } + }*/ private void DoRar_Multi_Reader_Encrypted(string[] archives) { @@ -205,11 +205,11 @@ namespace SharpCompress.Test.Rar ReadRar("Rar.Encrypted.rar", "test"); } - [Fact] + /*[Fact] public void Rar5_Encrypted_Reader() { - ReadRar("Rar5.Encrypted.rar", "test"); - } + ReadRar("Rar5.encrypted_filesOnly.rar", "test"); + }*/ private void ReadRar(string testArchive, string password) { From bee7f43880f40b339291c0127aa2743e798dca8e Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Thu, 26 Apr 2018 09:46:01 +0100 Subject: [PATCH 094/124] Expose stream length. Clean up entry stream --- .gitignore | 1 + src/SharpCompress/Common/EntryStream.cs | 33 +++++++++++-------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index 2e0b14fa..ca133c68 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ tests/TestArchives/Scratch tools .vscode .idea/ +.DS_Store diff --git a/src/SharpCompress/Common/EntryStream.cs b/src/SharpCompress/Common/EntryStream.cs index 0120b782..4697ff8c 100644 --- a/src/SharpCompress/Common/EntryStream.cs +++ b/src/SharpCompress/Common/EntryStream.cs @@ -6,15 +6,15 @@ namespace SharpCompress.Common { public class EntryStream : Stream { - public IReader Reader { get; } - private readonly Stream stream; - private bool completed; - private bool isDisposed; + private readonly IReader _reader; + private readonly Stream _stream; + private bool _completed; + private bool _isDisposed; internal EntryStream(IReader reader, Stream stream) { - Reader = reader; - this.stream = stream; + _reader = reader; + _stream = stream; } /// @@ -22,26 +22,23 @@ namespace SharpCompress.Common /// public void SkipEntry() { - var buffer = new byte[4096]; - while (Read(buffer, 0, buffer.Length) > 0) - { - } - completed = true; + this.Skip(); + _completed = true; } protected override void Dispose(bool disposing) { - if (!(completed || Reader.Cancelled)) + if (!(_completed || _reader.Cancelled)) { SkipEntry(); } - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; + _isDisposed = true; base.Dispose(disposing); - stream.Dispose(); + _stream.Dispose(); } public override bool CanRead => true; @@ -55,16 +52,16 @@ namespace SharpCompress.Common throw new NotSupportedException(); } - public override long Length => throw new NotSupportedException(); + public override long Length => _stream.Length; public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { - int read = stream.Read(buffer, offset, count); + int read = _stream.Read(buffer, offset, count); if (read <= 0) { - completed = true; + _completed = true; } return read; } From 9d63dcb8d63aae399bd816846886edf7446dce26 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 28 Apr 2018 18:14:47 +0100 Subject: [PATCH 095/124] Uncommit some tests --- tests/SharpCompress.Test/Rar/RarArchiveTests.cs | 12 +++--------- tests/SharpCompress.Test/Rar/RarReaderTests.cs | 15 ++------------- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index 2fab450c..140ab367 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -40,11 +40,11 @@ namespace SharpCompress.Test.Rar ReadRarPassword("Rar.Encrypted.rar", "test"); } - /*[Fact] + [Fact] public void Rar5_Encrypted_Archive() { - ReadRarPassword("Rar5.Encrypted.rar", "test"); - }*/ + ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test"); + } private void ReadRarPassword(string testArchive, string password) { @@ -78,12 +78,6 @@ namespace SharpCompress.Test.Rar Assert.Throws(() => ArchiveFileReadPassword("Rar.EncryptedParts.part01.rar", "test")); } - /*[Fact] - public void Rar5_Multi_Archive_Encrypted() - { - Assert.Throws(() => ArchiveFileReadPassword("Rar5.EncryptedParts.part01.rar", "test")); - }*/ - protected void ArchiveFileReadPassword(string archiveName, string password) { ResetScratch(); diff --git a/tests/SharpCompress.Test/Rar/RarReaderTests.cs b/tests/SharpCompress.Test/Rar/RarReaderTests.cs index 79ee574b..68af67a9 100644 --- a/tests/SharpCompress.Test/Rar/RarReaderTests.cs +++ b/tests/SharpCompress.Test/Rar/RarReaderTests.cs @@ -60,17 +60,6 @@ namespace SharpCompress.Test.Rar "Rar.EncryptedParts.part06.rar"}); } - /*[Fact] - public void Rar5_Multi_Reader_Encrypted() { - DoRar_Multi_Reader_Encrypted(new string[] { - "Rar5.EncryptedParts.part01.rar", - "Rar5.EncryptedParts.part02.rar", - "Rar5.EncryptedParts.part03.rar", - "Rar5.EncryptedParts.part04.rar", - "Rar5.EncryptedParts.part05.rar", - "Rar5.EncryptedParts.part06.rar"}); - }*/ - private void DoRar_Multi_Reader_Encrypted(string[] archives) { Assert.Throws(() => @@ -205,11 +194,11 @@ namespace SharpCompress.Test.Rar ReadRar("Rar.Encrypted.rar", "test"); } - /*[Fact] + [Fact] public void Rar5_Encrypted_Reader() { ReadRar("Rar5.encrypted_filesOnly.rar", "test"); - }*/ + } private void ReadRar(string testArchive, string password) { From 15534f466af2389844b8ae545f4d2116197bbf4f Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 28 Apr 2018 18:20:40 +0100 Subject: [PATCH 096/124] Add basic rar5 crypt header --- .../Common/Rar/Headers/CryptHeader.cs | 50 +++++++++++++++++++ src/SharpCompress/Common/Rar/Headers/Flags.cs | 9 +++- .../Common/Rar/Headers/RarHeaderFactory.cs | 6 +++ .../Rar/RarHeaderFactoryTest.cs | 2 +- 4 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 src/SharpCompress/Common/Rar/Headers/CryptHeader.cs diff --git a/src/SharpCompress/Common/Rar/Headers/CryptHeader.cs b/src/SharpCompress/Common/Rar/Headers/CryptHeader.cs new file mode 100644 index 00000000..6cb347c6 --- /dev/null +++ b/src/SharpCompress/Common/Rar/Headers/CryptHeader.cs @@ -0,0 +1,50 @@ +using SharpCompress.IO; + +namespace SharpCompress.Common.Rar.Headers +{ + internal class CryptHeader : RarHeader + { + + private const int CRYPT_VERSION = 0; // Supported encryption version. + private const int SIZE_SALT50 = 16; + private const int SIZE_PSWCHECK = 8; + private const int SIZE_PSWCHECK_CSUM = 4; + + + private bool UsePswCheck; + private uint Lg2Count; // Log2 of PBKDF2 repetition count. + private byte[] Salt; + private byte[] PswCheck; + + public CryptHeader(RarHeader header, RarCrcBinaryReader reader) + : base(header, reader, HeaderType.Crypt) + { + } + + protected override void ReadFinish(MarkingBinaryReader reader) + { + var cryptVersion = reader.ReadRarVIntUInt32(); + if (cryptVersion > CRYPT_VERSION) + { + //error? + return; + } + + UsePswCheck = HasHeaderFlag(EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); + Lg2Count = reader.ReadRarVIntByte(1); + if (Lg2Count > 0) + { + //error? + return; + } + + Salt = reader.ReadBytes(SIZE_SALT50); + if (UsePswCheck) + { + PswCheck = reader.ReadBytes(SIZE_PSWCHECK); + + var csum = reader.ReadBytes(SIZE_PSWCHECK_CSUM); + } + } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index 9c5060e9..7fe7d9b2 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -12,7 +12,8 @@ namespace SharpCompress.Common.Rar.Headers Protect, Sign, NewSub, - EndArchive + EndArchive, + Crypt } internal static class HeaderCodeV @@ -40,6 +41,12 @@ namespace SharpCompress.Common.Rar.Headers public const ushort HasData = 0x8000; } + internal static class EncryptionFlagsV5 + { + // RAR 5.0 archive encryption header specific flags. + public const ushort CHFL_CRYPT_PSWCHECK = 0x0001; // Password check data is present. + } + internal static class HeaderFlagsV5 { public const ushort HasExtra = 0x0001; diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index cb670368..0a637241 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -158,6 +158,12 @@ namespace SharpCompress.Common.Rar.Headers { return new EndArchiveHeader(header, reader); } + case HeaderCodeV.Rar5EncryptionHeader: + { + var ch = new CryptHeader(header, reader); + IsEncrypted = true; + return ch; + } default: { throw new InvalidFormatException("Unknown Rar Header: " + header.HeaderCode); diff --git a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs index f334903b..c2732522 100644 --- a/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs +++ b/tests/SharpCompress.Test/Rar/RarHeaderFactoryTest.cs @@ -51,7 +51,7 @@ namespace SharpCompress.Test.Rar { foreach (var header in rarHeaderFactory.ReadHeaders(stream)) { - if (header.HeaderType == HeaderType.Archive) + if (header.HeaderType == HeaderType.Archive || header.HeaderType == HeaderType.Crypt) { Assert.Equal(isEncrypted, rarHeaderFactory.IsEncrypted); break; From d865120480a81b6f3454238536989d5060098e89 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 29 Apr 2018 10:13:46 +0100 Subject: [PATCH 097/124] ArchiveCryptHeader renamed --- .../{CryptHeader.cs => ArchiveCryptHeader.cs} | 23 ++++++++++++------- src/SharpCompress/Common/Rar/Headers/Flags.cs | 7 ++++-- .../Common/Rar/Headers/RarHeaderFactory.cs | 4 ++-- 3 files changed, 22 insertions(+), 12 deletions(-) rename src/SharpCompress/Common/Rar/Headers/{CryptHeader.cs => ArchiveCryptHeader.cs} (54%) diff --git a/src/SharpCompress/Common/Rar/Headers/CryptHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs similarity index 54% rename from src/SharpCompress/Common/Rar/Headers/CryptHeader.cs rename to src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs index 6cb347c6..0ffaed01 100644 --- a/src/SharpCompress/Common/Rar/Headers/CryptHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs @@ -2,21 +2,26 @@ namespace SharpCompress.Common.Rar.Headers { - internal class CryptHeader : RarHeader + internal class ArchiveCryptHeader : RarHeader { private const int CRYPT_VERSION = 0; // Supported encryption version. private const int SIZE_SALT50 = 16; + private const int SIZE_SALT30 = 8; + private const int SIZE_INITV = 16; private const int SIZE_PSWCHECK = 8; private const int SIZE_PSWCHECK_CSUM = 4; + private const int CRYPT5_KDF_LG2_COUNT = 15; // LOG2 of PDKDF2 iteration count. + private const int CRYPT5_KDF_LG2_COUNT_MAX = 24; // LOG2 of maximum accepted iteration count. private bool UsePswCheck; private uint Lg2Count; // Log2 of PBKDF2 repetition count. private byte[] Salt; private byte[] PswCheck; + private byte[] PswCheckCsm; - public CryptHeader(RarHeader header, RarCrcBinaryReader reader) + public ArchiveCryptHeader(RarHeader header, RarCrcBinaryReader reader) : base(header, reader, HeaderType.Crypt) { } @@ -28,11 +33,14 @@ namespace SharpCompress.Common.Rar.Headers { //error? return; - } + } + var encryptionFlags = reader.ReadRarVIntUInt32(); + UsePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); + Lg2Count = reader.ReadRarVIntByte(1); - UsePswCheck = HasHeaderFlag(EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); - Lg2Count = reader.ReadRarVIntByte(1); - if (Lg2Count > 0) + + //UsePswCheck = HasHeaderFlag(EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); + if (Lg2Count > CRYPT5_KDF_LG2_COUNT_MAX) { //error? return; @@ -42,8 +50,7 @@ namespace SharpCompress.Common.Rar.Headers if (UsePswCheck) { PswCheck = reader.ReadBytes(SIZE_PSWCHECK); - - var csum = reader.ReadBytes(SIZE_PSWCHECK_CSUM); + PswCheckCsm = reader.ReadBytes(SIZE_PSWCHECK_CSUM); } } } diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index 7fe7d9b2..488b3c5b 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -32,7 +32,7 @@ namespace SharpCompress.Common.Rar.Headers public const byte Rar5ArchiveHeader = 0x01; public const byte Rar5FileHeader = 0x02; public const byte Rar5ServiceHeader = 0x03; - public const byte Rar5EncryptionHeader = 0x04; + public const byte Rar5ArchiveEncryptionHeader = 0x04; public const byte Rar5EndArchiveHeader = 0x05; } @@ -44,7 +44,10 @@ namespace SharpCompress.Common.Rar.Headers internal static class EncryptionFlagsV5 { // RAR 5.0 archive encryption header specific flags. - public const ushort CHFL_CRYPT_PSWCHECK = 0x0001; // Password check data is present. + public const uint CHFL_CRYPT_PSWCHECK = 0x01; // Password check data is present. + + public const uint FHEXTRA_CRYPT_PSWCHECK = 0x01; // Password check data is present. + public const uint FHEXTRA_CRYPT_HASHMAC = 0x02; } internal static class HeaderFlagsV5 diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index 0a637241..0f562314 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -158,9 +158,9 @@ namespace SharpCompress.Common.Rar.Headers { return new EndArchiveHeader(header, reader); } - case HeaderCodeV.Rar5EncryptionHeader: + case HeaderCodeV.Rar5ArchiveEncryptionHeader: { - var ch = new CryptHeader(header, reader); + var ch = new ArchiveCryptHeader(header, reader); IsEncrypted = true; return ch; } From b43d2c3d955cd2385012bee0aeb8273acd697783 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 29 Apr 2018 10:55:51 +0100 Subject: [PATCH 098/124] Disabled decryption tests --- tests/SharpCompress.Test/Rar/RarArchiveTests.cs | 12 ++++++------ tests/SharpCompress.Test/Rar/RarReaderTests.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index 140ab367..72bd222e 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -16,11 +16,11 @@ namespace SharpCompress.Test.Rar ReadRarPassword("Rar.encrypted_filesAndHeader.rar", "test"); } - [Fact] + /*[Fact] public void Rar5_EncryptedFileAndHeader_Archive() { ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test"); - } + }*/ [Fact] public void Rar_EncryptedFileOnly_Archive() @@ -28,11 +28,11 @@ namespace SharpCompress.Test.Rar ReadRarPassword("Rar.encrypted_filesOnly.rar", "test"); } - [Fact] + /*[Fact] public void Rar5_EncryptedFileOnly_Archive() { ReadRarPassword("Rar5.encrypted_filesOnly.rar", "test"); - } + }*/ [Fact] public void Rar_Encrypted_Archive() @@ -40,11 +40,11 @@ namespace SharpCompress.Test.Rar ReadRarPassword("Rar.Encrypted.rar", "test"); } - [Fact] + /*[Fact] public void Rar5_Encrypted_Archive() { ReadRarPassword("Rar5.encrypted_filesAndHeader.rar", "test"); - } + }*/ private void ReadRarPassword(string testArchive, string password) { diff --git a/tests/SharpCompress.Test/Rar/RarReaderTests.cs b/tests/SharpCompress.Test/Rar/RarReaderTests.cs index 68af67a9..a4101c64 100644 --- a/tests/SharpCompress.Test/Rar/RarReaderTests.cs +++ b/tests/SharpCompress.Test/Rar/RarReaderTests.cs @@ -170,11 +170,11 @@ namespace SharpCompress.Test.Rar ReadRar("Rar.encrypted_filesAndHeader.rar", "test"); } - [Fact] + /*[Fact] public void Rar5_EncryptedFileAndHeader_Reader() { ReadRar("Rar5.encrypted_filesAndHeader.rar", "test"); - } + }*/ [Fact] public void Rar_EncryptedFileOnly_Reader() @@ -182,11 +182,11 @@ namespace SharpCompress.Test.Rar ReadRar("Rar.encrypted_filesOnly.rar", "test"); } - [Fact] + /*[Fact] public void Rar5_EncryptedFileOnly_Reader() { ReadRar("Rar5.encrypted_filesOnly.rar", "test"); - } + }*/ [Fact] public void Rar_Encrypted_Reader() @@ -194,11 +194,11 @@ namespace SharpCompress.Test.Rar ReadRar("Rar.Encrypted.rar", "test"); } - [Fact] + /*[Fact] public void Rar5_Encrypted_Reader() { ReadRar("Rar5.encrypted_filesOnly.rar", "test"); - } + }*/ private void ReadRar(string testArchive, string password) { From 031b3c55f64bbb3e3f5365ce638daedc214aba03 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 29 Apr 2018 11:12:28 +0100 Subject: [PATCH 099/124] FIx solid support. I did it wrong --- .../Archives/Rar/RarArchiveEntry.cs | 4 ++-- .../Common/Rar/Headers/FileHeader.cs | 15 +++++++++++---- src/SharpCompress/Compressors/Rar/IRarUnpack.cs | 4 ++-- .../Compressors/Rar/RarCrcStream.cs | 4 ++-- src/SharpCompress/Compressors/Rar/RarStream.cs | 8 +++----- .../Compressors/Rar/UnpackV1/Unpack.cs | 16 ++++++++-------- .../Compressors/Rar/UnpackV2017/Unpack.cs | 10 +++++----- src/SharpCompress/Readers/Rar/RarReader.cs | 4 ++-- 8 files changed, 35 insertions(+), 30 deletions(-) diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs index d4197b3f..60ece4ee 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs @@ -64,10 +64,10 @@ namespace SharpCompress.Archives.Rar if (IsRarV3) { - return new RarStream(archive.UnpackV1.Value, archive.IsSolid, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); + return new RarStream(archive.UnpackV1.Value, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); } - return new RarStream(archive.UnpackV2017.Value, archive.IsSolid, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); + return new RarStream(archive.UnpackV2017.Value, FileHeader, new MultiVolumeReadOnlyStream(Parts.Cast(), archive)); } public bool IsComplete diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index 55a13322..39b238ab 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -55,20 +55,24 @@ namespace SharpCompress.Common.Rar.Headers FileCrc = reader.ReadUInt32(); } - var us = reader.ReadRarVIntUInt16(); + var compressionInfo = reader.ReadRarVIntUInt16(); // Lower 6 bits (0x003f mask) contain the version of compression algorithm, resulting in possible 0 - 63 values. Current version is 0. // "+ 50" to not mix with old RAR format algorithms. For example, // we may need to use the compression algorithm 15 in the future, // but it was already used in RAR 1.5 and Unpack needs to distinguish // them. - CompressionAlgorithm = (byte)((us & 0x3f) + 50); + CompressionAlgorithm = (byte)((compressionInfo & 0x3f) + 50); + + // 7th bit (0x0040) defines the solid flag. If it is set, RAR continues to use the compression dictionary left after processing preceding files. + // It can be set only for file headers and is never set for service headers. + IsSolid = (compressionInfo & 0x40) == 0x40; // Bits 8 - 10 (0x0380 mask) define the compression method. Currently only values 0 - 5 are used. 0 means no compression. - CompressionMethod = (byte)((us >> 7) & 0x7); + CompressionMethod = (byte)((compressionInfo >> 7) & 0x7); // Bits 11 - 14 (0x3c00) define the minimum size of dictionary size required to extract data. Value 0 means 128 KB, 1 - 256 KB, ..., 14 - 2048 MB, 15 - 4096 MB. - WindowSize = IsDirectory ? 0 : ((size_t)0x20000) << ((us>>10) & 0xf); + WindowSize = IsDirectory ? 0 : ((size_t)0x20000) << ((compressionInfo>>10) & 0xf); HostOs = reader.ReadRarVIntByte(); @@ -205,6 +209,7 @@ namespace SharpCompress.Common.Rar.Headers private void ReadFromReaderV4(MarkingBinaryReader reader) { Flags = HeaderFlags; + IsSolid = HasFlag(FileFlagsV4.Solid); WindowSize = IsDirectory ? 0U : ((size_t)0x10000) << ((Flags & FileFlagsV4.WindowMask) >> 5); uint lowUncompressedSize = reader.ReadUInt32(); @@ -408,6 +413,8 @@ namespace SharpCompress.Common.Rar.Headers //case 29: // rar 3.x compression //case 50: // RAR 5.0 compression algorithm. internal byte CompressionAlgorithm { get; private set; } + + public bool IsSolid { get; private set; } // unused for UnpackV1 implementation (limitation) internal size_t WindowSize { get; private set; } diff --git a/src/SharpCompress/Compressors/Rar/IRarUnpack.cs b/src/SharpCompress/Compressors/Rar/IRarUnpack.cs index 887c590e..ecd70a47 100644 --- a/src/SharpCompress/Compressors/Rar/IRarUnpack.cs +++ b/src/SharpCompress/Compressors/Rar/IRarUnpack.cs @@ -5,8 +5,8 @@ namespace SharpCompress.Compressors.Rar { interface IRarUnpack { - void DoUnpack(bool isSolid, FileHeader fileHeader, Stream readStream, Stream writeStream); - void DoUnpack(bool isSolid); + void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream); + void DoUnpack(); // eg u/i pause/resume button bool Suspended { get; set; } diff --git a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs index 2bf98579..df7d85ff 100644 --- a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs @@ -9,8 +9,8 @@ namespace SharpCompress.Compressors.Rar private readonly MultiVolumeReadOnlyStream readStream; private uint currentCrc; - public RarCrcStream(IRarUnpack unpack, bool isSolid, FileHeader fileHeader, MultiVolumeReadOnlyStream readStream) - : base(unpack, isSolid, fileHeader, readStream) + public RarCrcStream(IRarUnpack unpack, FileHeader fileHeader, MultiVolumeReadOnlyStream readStream) + : base(unpack, fileHeader, readStream) { this.readStream = readStream; ResetCrc(); diff --git a/src/SharpCompress/Compressors/Rar/RarStream.cs b/src/SharpCompress/Compressors/Rar/RarStream.cs index 6da56697..08240859 100644 --- a/src/SharpCompress/Compressors/Rar/RarStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarStream.cs @@ -9,7 +9,6 @@ namespace SharpCompress.Compressors.Rar private readonly IRarUnpack unpack; private readonly FileHeader fileHeader; private readonly Stream readStream; - private readonly bool isSolid; private bool fetch; @@ -23,14 +22,13 @@ namespace SharpCompress.Compressors.Rar private int outTotal; private bool isDisposed; - public RarStream(IRarUnpack unpack, bool isSolid, FileHeader fileHeader, Stream readStream) + public RarStream(IRarUnpack unpack, FileHeader fileHeader, Stream readStream) { - this.isSolid = isSolid; this.unpack = unpack; this.fileHeader = fileHeader; this.readStream = readStream; this.fetch = true; - unpack.DoUnpack(isSolid, fileHeader, readStream, this); + unpack.DoUnpack(fileHeader, readStream, this); this.fetch = false; } @@ -76,7 +74,7 @@ namespace SharpCompress.Compressors.Rar outOffset = offset; outCount = count; fetch = true; - unpack.DoUnpack(isSolid); + unpack.DoUnpack(); fetch = false; } return outTotal; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs index dd3b52dc..90c09c6d 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs @@ -103,21 +103,21 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 UnpInitData(false); } - public void DoUnpack(bool isSolid, FileHeader fileHeader, Stream readStream, Stream writeStream) + public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) { this.destUnpSize = fileHeader.UncompressedSize; this.fileHeader = fileHeader; this.readStream = readStream; this.writeStream = writeStream; - if (!isSolid) + if (!fileHeader.IsSolid) { Init(null); } this.suspended = false; - DoUnpack(isSolid); + DoUnpack(); } - public void DoUnpack(bool isSolid) + public void DoUnpack() { if (fileHeader.CompressionMethod == 0) { @@ -127,21 +127,21 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 switch (fileHeader.CompressionAlgorithm) { case 15: // rar 1.5 compression - unpack15(isSolid); + unpack15(fileHeader.IsSolid); break; case 20: // rar 2.x compression case 26: // files larger than 2GB - unpack20(isSolid); + unpack20(fileHeader.IsSolid); break; case 29: // rar 3.x compression case 36: // alternative hash - Unpack29(isSolid); + Unpack29(fileHeader.IsSolid); break; case 50: // rar 5.x compression - Unpack5(isSolid); + Unpack5(fileHeader.IsSolid); break; default: diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index 2d52da5b..e8bc8c36 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -37,7 +37,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 this.writeStream.Write(buf, checked((int)offset), checked((int)count)); } - public void DoUnpack(bool isSolid, FileHeader fileHeader, Stream readStream, Stream writeStream) + public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) { // as of 12/2017 .NET limits array indexing to using a signed integer // MaxWinSize causes unpack to use a fragmented window when the file @@ -51,19 +51,19 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 this.readStream = readStream; this.writeStream = writeStream; if (!fileHeader.IsStored) { - Init(fileHeader.WindowSize, isSolid); + Init(fileHeader.WindowSize, fileHeader.IsSolid); } Suspended = false; - DoUnpack(isSolid); + DoUnpack(); } - public void DoUnpack(bool isSolid) + public void DoUnpack() { if (this.fileHeader.IsStored) { UnstoreFile(); } else { - DoUnpack(this.fileHeader.CompressionAlgorithm, isSolid); + DoUnpack(this.fileHeader.CompressionAlgorithm, fileHeader.IsSolid); } } diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index 81f99d9b..05fe0daa 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -70,9 +70,9 @@ namespace SharpCompress.Readers.Rar var stream = new MultiVolumeReadOnlyStream(CreateFilePartEnumerableForCurrentEntry().Cast(), this); if (Entry.IsRarV3) { - return CreateEntryStream(new RarCrcStream(UnpackV1.Value, volume.IsSolidArchive, Entry.FileHeader, stream)); + return CreateEntryStream(new RarCrcStream(UnpackV1.Value, Entry.FileHeader, stream)); } - return CreateEntryStream(new RarCrcStream(UnpackV2017.Value, volume.IsSolidArchive, Entry.FileHeader, stream)); + return CreateEntryStream(new RarCrcStream(UnpackV2017.Value, Entry.FileHeader, stream)); } } } From d9c178cbee83e52886fadc9bddd7792ee94776e0 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 29 Apr 2018 11:33:49 +0100 Subject: [PATCH 100/124] Fix all platform support --- src/SharpCompress/Archives/Rar/RarArchive.cs | 1 - .../Archives/Rar/RarArchiveEntry.cs | 2 +- .../FragmentedWindow.unpack50frag_cpp.cs | 4 --- .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 6 ++-- src/SharpCompress/Lazy.cs | 29 +++++++++++++++++++ src/SharpCompress/Readers/Rar/RarReader.cs | 3 +- src/SharpCompress/SharpCompress.csproj | 2 +- src/SharpCompress/Utility.cs | 12 ++++++++ 8 files changed, 47 insertions(+), 12 deletions(-) create mode 100644 src/SharpCompress/Lazy.cs diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index fb052c37..52ffaa1c 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.IO; using System.Linq; diff --git a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs index 60ece4ee..3f2360b2 100644 --- a/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs +++ b/src/SharpCompress/Archives/Rar/RarArchiveEntry.cs @@ -24,7 +24,7 @@ namespace SharpCompress.Archives.Rar public IArchive Archive => archive; - internal override IEnumerable Parts => this.parts; + internal override IEnumerable Parts => parts.Cast(); internal override FileHeader FileHeader => parts.First().FileHeader; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs index b0bc6da7..cd0b5eb0 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs @@ -10,10 +10,6 @@ using size_t = System.UInt64; using int64 = System.Int64; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace SharpCompress.Compressors.Rar.UnpackV2017 { diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index 3a0d63c5..53f0e713 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -334,7 +334,7 @@ void UnpWriteBuf() FragWindow.CopyData(Mem,0,BlockStart,BlockLength); else //x memcpy(Mem,Window+BlockStart,BlockLength); - Array.Copy(Window, BlockStart, Mem, 0, BlockLength); + Utility.Copy(Window, BlockStart, Mem, 0, BlockLength); } else { @@ -347,9 +347,9 @@ void UnpWriteBuf() else { //x memcpy(Mem,Window+BlockStart,FirstPartLength); - Array.Copy(Window, BlockStart, Mem, 0, FirstPartLength); + Utility.Copy(Window, BlockStart, Mem, 0, FirstPartLength); //x memcpy(Mem+FirstPartLength,Window,BlockEnd); - Array.Copy(Window, 0, Mem, FirstPartLength,BlockEnd); + Utility.Copy(Window, 0, Mem, FirstPartLength,BlockEnd); } } diff --git a/src/SharpCompress/Lazy.cs b/src/SharpCompress/Lazy.cs new file mode 100644 index 00000000..893e3bb1 --- /dev/null +++ b/src/SharpCompress/Lazy.cs @@ -0,0 +1,29 @@ +using System; + +namespace SharpCompress +{ + public class Lazy + { + private readonly Func _lazyFunc; + private bool _evaluated; + private T _value; + + public Lazy(Func lazyFunc) + { + _lazyFunc = lazyFunc; + } + + public T Value + { + get + { + if (!_evaluated) + { + _value = _lazyFunc(); + _evaluated = true; + } + return _value; + } + } + } +} \ No newline at end of file diff --git a/src/SharpCompress/Readers/Rar/RarReader.cs b/src/SharpCompress/Readers/Rar/RarReader.cs index 05fe0daa..23a4005e 100644 --- a/src/SharpCompress/Readers/Rar/RarReader.cs +++ b/src/SharpCompress/Readers/Rar/RarReader.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using SharpCompress.Common; diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index a20a92a9..5c1d0815 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -6,7 +6,7 @@ 0.20.0 0.20.0 Adam Hathcock - netstandard2.0 + net45;net35;netstandard1.0;netstandard1.3;netstandard2.0 true true SharpCompress diff --git a/src/SharpCompress/Utility.cs b/src/SharpCompress/Utility.cs index bca28a9d..e97d093d 100644 --- a/src/SharpCompress/Utility.cs +++ b/src/SharpCompress/Utility.cs @@ -184,6 +184,18 @@ namespace SharpCompress action(item); } } + + public static void Copy(Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length) + { + if (sourceIndex > Int32.MaxValue || sourceIndex < Int32.MinValue) + throw new ArgumentOutOfRangeException(); + if (destinationIndex > Int32.MaxValue || destinationIndex < Int32.MinValue) + throw new ArgumentOutOfRangeException(); + if (length > Int32.MaxValue || length < Int32.MinValue) + throw new ArgumentOutOfRangeException(); + + Array.Copy(sourceArray, (int)sourceIndex, destinationArray, (int)destinationIndex, (int)length); + } public static IEnumerable AsEnumerable(this T item) { From 259acd0694f48b69b3eb932d79821a4496c3556a Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 29 Apr 2018 15:09:26 +0100 Subject: [PATCH 101/124] misc additions --- README.md | 2 +- build.sh | 2 +- reference/RAR 5.0 archive format.htm | 1197 ++++++++++++++++++++++++++ 3 files changed, 1199 insertions(+), 2 deletions(-) create mode 100644 reference/RAR 5.0 archive format.htm diff --git a/README.md b/README.md index b3f04c30..47fce793 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ I'm always looking for help or ideas. Please submit code or email with ideas. Un ## TODOs (always lots) -* RAR 5 support +* RAR 5 decryption support * 7Zip writing * Zip64 (Need writing and extend Reading) * Multi-volume Zip support. diff --git a/build.sh b/build.sh index 71dd07dc..d478e0a7 100755 --- a/build.sh +++ b/build.sh @@ -8,7 +8,7 @@ # Define directories. SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) TOOLS_DIR=$SCRIPT_DIR/tools -CAKE_VERSION=0.26.0 +CAKE_VERSION=0.27.1 CAKE_DLL=$TOOLS_DIR/Cake.CoreCLR.$CAKE_VERSION/Cake.dll # Make sure the tools folder exist. diff --git a/reference/RAR 5.0 archive format.htm b/reference/RAR 5.0 archive format.htm new file mode 100644 index 00000000..ecaae5e2 --- /dev/null +++ b/reference/RAR 5.0 archive format.htm @@ -0,0 +1,1197 @@ + + + +RAR 5.0 archive format + + + + +

RAR 5.0 archive format

+ +

Here we describe basic data structures of archive format introduced +in RAR 5.0. If you need information about algorithms or more detailed +information on data structures, please use UnRAR source code.

+ +

Contents

+
+ +

Data types

+ +

vint

+ +

Variable length integer. Can include one or more bytes, where lower +7 bits of every byte contain integer data and highest bit in every byte +is the continuation flag. If highest bit is 0, this is the last byte +in sequence. So first byte contains 7 least significant bits of integer +and continuation flag. Second byte, if present, contains next 7 bits +and so on.

+

Currently RAR format uses vint to store up to 64 bit integers, +resulting in 10 bytes maximum. This value may be increased in the future +if necessary for some reason.

+

Sometimes RAR needs to pre-allocate space for vint before knowing +its exact value. In such situation it can allocate more space than really +necessary and then fill several leading bytes with 0x80 hexadecimal, +which means 0 with continuation flag set.

+ +

byte, uint16, uint32, uint64

+ +

Byte, 16-, 32-, 64- bit unsigned integer in little endian format.

+ +

Variable length data

+ +

We use ellipsis ... to denote variable length data areas. + +

Hexadecimal values

+

We use 0x prefix to define hexadecimal values, such as 0xf000

+ + + +

General archive structure

+ +

General archive block format

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldSizeDescription
Header CRC32uint32CRC32 of header data starting from Header size field +and up to and including the optional extra area.
Header sizevintSize of header data starting from Header type field +and up to and including the optional extra area. +This field must not be longer than 3 bytes in current implementation, +resulting in 2 MB maximum header size. +
Header typevintType of archive header. Possible values are: +
  1   Main archive header. +
  2   File header. +
  3   Service header. +
  4   Archive encryption header. +
  5   End of archive header. +
Header flagsvintFlags common for all headers: +
  0x0001   Extra area is present in the end of header. +
  0x0002   Data area is present in the end of header. +
  0x0004   Blocks with unknown type and this flag must be skipped when updating an archive. +
  0x0008   Data area is continuing from previous volume. +
  0x0010   Data area is continuing in next volume. +
  0x0020   Block depends on preceding file block. +
  0x0040   Preserve a child block if host block is modified. +
Extra area sizevintSize of extra area. Optional field, present only if 0x0001 header flag +is set.
Data sizevintSize of data area. Optional field, present only if 0x0002 header flag +is set.
......Fields specific for current block type. See concrete block types +description for details.
Extra area...Optional area containing additional header fields, present only +if 0x0001 header flag is set.
Data areavintOptional data area, present only if 0x0002 header flag is set. +Used to store large data amounts, such as compressed file data. +Not counted in Header CRC and Header size fields.
+ +

General extra area format

+ +

Extra area can include one or more records having the following format:

+ + + + + + + + + + + + + + + + + + + + + +
SizevintSize of record data starting from Type. +
TypevintRecord type. Different archive blocks have different associated extra area +record types. Read the concrete archive block description for details. +New record types can be added in the future, so unknown record types +need to be skipped without interrupting an operation. +
Data...Record dependent data. May be missing if record consists only from +size and type. +
+ + +

General archive layout

+ +
    Self-extracting module (optional)
+    RAR 5.0 signature
+    Archive encryption header (optional)
+    Main archive header
+    Archive comment service header (optional)
+
+    File header 1
+    Service headers (NTFS ACL, streams, etc.) for preceding file (optional).
+    ...
+    File header N
+    Service headers (NTFS ACL, streams, etc.) for preceding file (optional).
+
+    Recovery record (optional).
+    End of archive header.
+
+ +

Archive blocks

+ +

Self-extracting module (SFX)

+ +

Any data preceding the archive signature. Self-extracting module size +and contents is not defined. At the moment of writing this documentation +RAR assumes the maximum SFX module size to not exceed 1 MB, but this value +can be increased in the future.

+ +

RAR 5.0 signature

+ +

+RAR 5.0 signature consists of 8 bytes: +0x52 0x61 0x72 0x21 0x1A 0x07 0x01 0x00. +You need to search for this signature in supposed archive from beginning +and up to maximum SFX module size. +Just for comparison this is RAR 4.x 7 byte length signature: +0x52 0x61 0x72 0x21 0x1A 0x07 0x00. +

+ + +

Archive encryption header

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Header CRC32uint32
Header sizevint
Header typevint4
Header flagsvintFlags common for all headers
Encryption versionvint +Version of encryption algorithm. Now only 0 version (AES-256) +is supported.
Encryption flagsvint + 0x0001   Password check data is present. +
KDF count1 byte +Binary logarithm of iteration number for PBKDF2 function. RAR can refuse +to process KDF count exceeding some threshold. Concrete value of threshold +is a version dependent. +
Salt16 bytes +Salt value used globally for all encrypted archive headers. +
Check value12 bytes +Value used to verify the password validity. Present only if 0x0001 +encryption flag is set. First 8 bytes are calculated using additional +PBKDF2 rounds, 4 last bytes is the additional checksum. Together with +the standard header CRC32 we have 64 bit checksum to reliably verify +this field integrity and distinguish invalid password and damaged data. +Further details can be found in UnRAR source code. +
+ +

This header is present only in archives with encrypted headers. +Every next header after this one is started from 16 byte AES-256 +initialization vector followed by encrypted header data. Size of encrypted +header data block is aligned to 16 byte boundary.

+ +

Main archive header

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Header CRC32uint32
Header sizevint
Header typevint1
Header flagsvintFlags common for all headers
Extra area sizevintSize of extra area. Optional field, present only if 0x0001 header flag +is set.
Archive flagsvint +   0x0001   Volume. Archive is a part of multivolume set.
+   0x0002   Volume number field is present. This flag is present + in all volumes except first.
+   0x0004   Solid archive.
+   0x0008   Recovery record is present.
+   0x0010   Locked archive. +
Volume numbervintOptional field, present only if 0x0002 archive flag is set. +Not present for first volume, 1 for second volume, 2 for third and so on.
Extra area...Optional area containing additional header fields, present only +if 0x0001 header flag is set.
+ +

Extra area of main archive header can contain following record +types

+ + + + + + + +
TypeNameDescription
0x01LocatorContains positions of different service blocks, so they can be accessed +quickly, without scanning the entire archive. This record is optional. +If it is missing, it is still necessary to scan the entire archive to +verify presence of service blocks.
+ +

Locator record

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sizevint
Typevint1
Flagsvint +   0x0001   Quick open record offset is present.
+   0x0002   Recovery record offset is present.
+
Quick open offsetvintDistance from beginning of quick open service block to beginning +of main archive header. Present only if 0x0001 flag is set. +If equal to 0, should be ignored. It can be set to zero if preallocated +space was not enough to store resulting offset. +
Recovery record offsetvintDistance from beginning of recovery record service block to beginning +of main archive header. Present only if 0x0002 flag is set. +If equal to 0, should be ignored. It can be set to zero if preallocated +space was not enough to store resulting offset. +
+ + +

File header and service header

+ +

These two header types use the similar data structure, so we describe them +both here.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Header CRC32uint32
Header sizevint
Header typevint2 for file header, 3 for service header
Header flagsvintFlags common for all headers
Extra area sizevintSize of extra area. Optional field, present only if 0x0001 header flag +is set.
Data sizevintSize of data area. Optional field, present only if 0x0002 header flag +is set. For file header this field contains the packed file size.
File flagsvintFlags specific for these header types:
+   0x0001   Directory file system object (file header only).
+   0x0002   Time field in Unix format is present.
+   0x0004   CRC32 field is present.
+   0x0008   Unpacked size is unknown.
+ +

If flag 0x0008 is set, unpacked size field is still present, + but must be ignored and extraction must be performed until reaching + the end of compression stream. This flag can be set if actual file size + is larger than reported by OS or if file size is unknown such as + for all volumes except last when archiving from stdin to multivolume + archive.

+
Unpacked sizevintUnpacked file or service data size.
AttributesvintOperating system specific file attributes in case of file header. +Might be either used for data specific needs or just reserved and set to 0 +for service header.
mtimeuint32File modification time in Unix time format. +Optional, present if 0x0002 file flag is set.
Data CRC32uint32CRC32 of unpacked file or service data. For files split between volumes +it contains CRC32 of file packed data contained in current volume +for all file parts except the last. +Optional, present if 0x0004 file flag is set.
Compression informationvint +

Lower 6 bits (0x003f mask) contain the version of compression algorithm, +resulting in possible 0 - 63 values. Current version is 0.

+

7th bit (0x0040) defines the solid flag. If it is set, RAR continues +to use the compression dictionary left after processing preceding files. +It can be set only for file headers and is never set for service headers.

+

Bits 8 - 10 (0x0380 mask) define the compression method. Currently only +values 0 - 5 are used. 0 means no compression.

+

Bits 11 - 14 (0x3c00) define the minimum size of dictionary size required +to extract data. Value 0 means 128 KB, 1 - 256 KB, ..., 14 - 2048 MB, +15 - 4096 MB.

+
Host OSvintType of operating system used to create the archive.
+   0x0000   Windows.
+   0x0001   Unix.
+
Name lengthvintFile or service header name length.
Name? bytes +

Variable length field containing Name length bytes in UTF-8 +format without trailing zero.

+

For file header this is a name of archived file. Forward slash character +is used as the path separator both for Unix and Windows names. Backslashes +are treated as a part of name for Unix names and as invalid character +for Windows file names. Type of name is defined by Host OS field.

+

If Unix file name contains any high ASCII characters which cannot be +correctly converted to Unicode and UTF-8, we map such characters to +to 0xE080 - 0xE0FF private use Unicode area and insert 0xFFFE Unicode +non-character to resulting string to indicate that it contains mapped +characters, which need to be converted back when extracting. Concrete +position of 0xFFFE is not defined, we need to search the entire string +for it. Such mapped names are not portable and can be correctly unpacked +only on the same system where they were created.

+

For service header this field contains a name of service header. +Now the following names are used: + + + + + + +
CMTArchive comment
QOArchive quick open data
ACLNTFS file permissions
STMNTFS alternate data stream
RRRecovery record
+ +

+
Extra area...Optional area containing additional header fields, present only +if 0x0001 header flag is set.
Data areavintOptional data area, present only if 0x0002 header flag is set. +Store file data in case of file header or service data for service header. +Depending on the compression method value in Compression information +can be either uncompressed (compression method 0) or compressed. +
+ + +

File and service headers use the same types of extra area +records:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeNameDescription
0x01File encryptionFile encryption information.
0x02File hashFile data hash.
0x03File timeHigh precision file time.
0x04File versionFile version number.
0x05RedirectionFile system redirection.
0x06Unix ownerUnix owner and group information.
0x07Service dataService header data array.
+ + +

File encryption record

+ +

This record is present if file data is encrypted.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sizevint
Typevint0x01
Versionvint +Version of encryption algorithm. Now only 0 version (AES-256) +is supported.
Flagsvint +   0x0001   Password check data is present.
+   0x0002   Use tweaked checksums instead of plain checksums.
+ +

If flag 0x0002 is present, RAR transforms the checksum preserving + file or service data integrity, so it becomes dependent on encryption key. + It makes guessing file contents based on checksum impossible. + It affects both data CRC32 in file header and checksums + in file hash record in extra area.

+
KDF count1 byte +Binary logarithm of iteration number for PBKDF2 function. RAR can refuse +to process KDF count exceeding some threshold. Concrete value of threshold +is version dependent. +
Salt16 bytes +Salt value to set the decryption key for encrypted file. +
IV16 bytes +AES-256 initialization vector. +
Check value12 bytes +Value used to verify the password validity. Present only if 0x0001 +encryption flag is set. First 8 bytes are calculated using additional +PBKDF2 rounds, 4 last bytes is the additional checksum. Together with +the standard header CRC32 we have 64 bit checksum to reliably verify +this field integrity and distinguish invalid password and damaged data. +Further details can be found in UnRAR source code. +
+ + +

File hash record

+ +

Only the standard CRC32 checksum can be stored directly in file header. +If other hash is used, it is stored in this extra area record:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sizevint
Typevint0x02
Hash typevint + 0x00   BLAKE2sp hash function.
+
Hash data? bytes + 32 bytes of BLAKE2sp for 0x00 hash type. +
+ +

For files split between volumes it contains a hash of file packed +data contained in current volume for all file parts except the last. +For files not split between volumes and for last parts of split files +it contains an unpacked data hash. +

+ + +

File time record

+ +

This record is used if it is necessary to store creation and last access +time or if 1 second precision of Unix mtime stored in file header is not +enough:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sizevint
Typevint0x03
Flagsvint + 0x0001   Time is stored in Unix time format if this flags is set + and in Windows FILETIME format otherwise
+ 0x0002   Modification time is present
+ 0x0004   Creation time is present
+ 0x0008   Last access time is present
+
mtimeuint32 or uint64Modification time. Present if 0x0002 flag is set. Depending on 0x0001 +flag can be in Unix time or Windows FILETIME format.
ctimeuint32 or uint64Creation time. Present if 0x0004 flag is set. Depending on 0x0001 +flag can be in Unix time or Windows FILETIME format.
ctimeuint32 or uint64Last access time. Present if 0x0008 flag is set. Depending on 0x0001 +flag can be in Unix time or Windows FILETIME format.
+ + +

File version record

+ +

This record is used in archives created with -ver switch.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sizevint
Typevint0x04
Flagsvint + No file version flags are defined yet, so it is set to 0. +
Version numbervintFile version number.
+ + +

File system redirection record

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sizevint
Typevint0x05
Redirection typevint + 0x0001   Unix symlink
+ 0x0002   Windows symlink
+ 0x0003   Windows junction
+ 0x0004   Hard link
+ 0x0005   File copy
+
Flagsvint + 0x0001   Link target is directory
+
Name lengthvintLength of link target name
NamevintLink target name in UTF-8 format without trailing zero
+ + +

Unix owner record

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Sizevint
Typevint0x06
Flagsvint + 0x0001   User name string is present
+ 0x0002   Group name string is present
+ 0x0004   Numeric user ID is present
+ 0x0008   Numeric group ID is present
+
User name lengthvintLength of owner user name. Present if 0x0001 flag is set.
User name? bytesOwner user name in native encoding. Not zero terminated. +Present if 0x0001 flag is set.
Group name lengthvintLength of owner group name. Present if 0x0002 flag is set.
Group name? bytesOwner group name in native encoding. Not zero terminated. +Present if 0x0002 flag is set.
User IDvintNumeric owner user ID. Present if 0x0004 flag is set.
Group IDvintNumeric owner group ID. Present if 0x0008 flag is set.
+ + +

Service data record

+ +

This record is used only by service headers to store additional +parameters.

+ + + + + + + + + + + + + + + + + + + + + +
Sizevint
Typevint0x07
Data? bytesConcrete contents of service data depends on service header type.
+ +

End of archive header

+ +

End of archive marker. RAR does not read anything after this header +letting to use third party tools to add extra information such as +a digital signature to archive.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Header CRC32uint32
Header sizevint
Header typevint5
Header flagsvintFlags common for all headers
End of archive flagsvint +0x0001   Archive is volume and it is not last volume in the set
+
+ +

Service headers

+ +

RAR uses service headers based on the file header +data structure to store different supplementary information.

+ +

Archive comment header

+ +

Optional header storing the main archive comment. Contains CMT identifier +in file name field. Placed before any file headers and after the main +archive header. Comment data is stored in UTF-8 immediately after +the archive comment header. Now RAR does not use compression for archive +comments, so packed and unpacked data sizes in header are equal and they +both define the comment data size. Compression method in header is set +to 0.

+ +

Quick open header

+ +

Optional header storing the quick open record. Contains QO identifier +in file name field. Placed after all file headers, but before the recovery +record and end of archive header. It is possible to locate the quick open +header with locator record in main archive header. + +

Quick open record data is stored immediately after the quick open header. +RAR does not use compression for quick open data, so packed and unpacked +data sizes in header are equal and they both define the quick open data size. +Compression method in header is set to 0.

+ +

Quick open data is the array consisting of data cache structures. +Every data cache structure stores a portion of archived data +and has the following format:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldSizeDescription
Structure CRC32uint32CRC32 of structure data starting from Structure size field.
Structure sizevintSize of structure data starting from Flags field. +This field must not be longer than 3 bytes in current implementation, +resulting in 2 MB maximum size. +
FlagsvintCurrently set to 0.
OffsetvintOffset from beginning of quick open header to beginning of archive data +cached in current structure. We can use this value to calculate +the absolute position of archived data stored the current structure. +It is guaranteed that absolute archive positions referred by data cache +structures are always growing when going from beginning of structure array +to end.
Data sizevintSize of archive data stored in the current structure.
Data? bytesArchive data stored in the current structure.
+ +

Normally RAR uses the quick open data to store copies of file and service +headers. It can store either all headers or only a part of them. If required +header is missing in quick open data or if structure CRC32 is invalid, +data are read from its original archive position.

+ +

Using the quick open data is optional. You can skip it completely +and read only standard archive headers. But it is important to use the same +access pattern when reading file names to display them to user and +to extract files. Otherwise it would be possible to see one file name +and extract another in case the quick open data and real archive data +are intentionally created different. It could introduce a security threat. +So if you use the quick open data when displaying the archive contents, +use it when extracting. If you do not use it when displaying +the archive contents, do not use it when extracting.

+ + + + \ No newline at end of file From abddabf18e5402ac37ebaebc3d6a880a14aacfe8 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 29 Apr 2018 16:27:26 +0100 Subject: [PATCH 102/124] Proper fixes for all platforms --- src/SharpCompress/Common/ArchiveEncoding.cs | 17 +++++++++++++++++ .../Common/Zip/Headers/DirectoryEntryHeader.cs | 12 ++++++------ .../Common/Zip/Headers/LocalEntryHeader.cs | 3 +-- src/SharpCompress/SharpCompress.csproj | 8 +++++++- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/SharpCompress/Common/ArchiveEncoding.cs b/src/SharpCompress/Common/ArchiveEncoding.cs index e37da084..a8712115 100644 --- a/src/SharpCompress/Common/ArchiveEncoding.cs +++ b/src/SharpCompress/Common/ArchiveEncoding.cs @@ -32,11 +32,28 @@ namespace SharpCompress.Common Password = Encoding.UTF8; } +#if NETSTANDARD1_3 || NETSTANDARD2_0 + static ArchiveEncoding() + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } +#endif + public string Decode(byte[] bytes) { return Decode(bytes, 0, bytes.Length); } + public string Decode437(byte[] bytes) + { +#if NETSTANDARD1_0 + return Decode(bytes, 0, bytes.Length); +#else + var extendedASCIIEncoding = Encoding.GetEncoding(437); + return extendedASCIIEncoding.GetString(bytes, 0, bytes.Length); +#endif + } + public string Decode(byte[] bytes, int start, int length) { return GetDecoder().Invoke(bytes, start, length); diff --git a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs index 6c855a28..5e32020d 100644 --- a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs @@ -1,5 +1,4 @@ -using System; -using System.IO; +using System.IO; using System.Linq; using System.Text; @@ -34,12 +33,11 @@ namespace SharpCompress.Common.Zip.Headers byte[] name = reader.ReadBytes(nameLength); byte[] extra = reader.ReadBytes(extraLength); byte[] comment = reader.ReadBytes(commentLength); + if ((Flags & HeaderFlags.UTF8) == 0) { - // Use IBM Code Page 437 (IBM PC character encoding set) - var extendedASCIIEncoding = Encoding.GetEncoding("IBM437"); - Name = extendedASCIIEncoding.GetString(name, 0, name.Length); - Comment = extendedASCIIEncoding.GetString(comment, 0, comment.Length); + Name = ArchiveEncoding.Decode437(name); + Comment = ArchiveEncoding.Decode437(comment); } else { @@ -62,10 +60,12 @@ namespace SharpCompress.Common.Zip.Headers { CompressedSize = zip64ExtraData.CompressedSize; } + if (UncompressedSize == uint.MaxValue) { UncompressedSize = zip64ExtraData.UncompressedSize; } + if (RelativeOffsetOfEntryHeader == uint.MaxValue) { RelativeOffsetOfEntryHeader = zip64ExtraData.RelativeOffsetOfEntryHeader; diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs index 01f3a63e..582d04f7 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs @@ -28,8 +28,7 @@ namespace SharpCompress.Common.Zip.Headers if ((Flags & HeaderFlags.UTF8) == 0) { // Use IBM Code Page 437 (IBM PC character encoding set) - var extendedASCIIEncoding = Encoding.GetEncoding("IBM437"); - Name = extendedASCIIEncoding.GetString(name, 0, name.Length); + Name = ArchiveEncoding.Decode437(name); } else { diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 5c1d0815..58cf4dd3 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -19,7 +19,7 @@ https://github.com/adamhathcock/sharpcompress/blob/master/LICENSE.txt false false - SharpCompress is a compression library for NET Standard 1.0 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented. + SharpCompress is a compression library for NET Standard 1.0 that can unrar, decompress 7zip, decompress xz, zip/unzip, tar/untar lzip/unlzip, bzip2/unbzip2 and gzip/ungzip with forward-only reading and file random access APIs. Write support for zip/tar/bzip2/gzip is implemented.
$(DefineConstants);NO_FILE;NO_CRYPTO;SILVERLIGHT @@ -27,7 +27,13 @@ $(DefineConstants);NETCORE + + + $(DefineConstants);NETCORE + + + \ No newline at end of file From 501407c3fe2ec7faa27788a9419ffec9423ddd8e Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 29 Apr 2018 16:33:15 +0100 Subject: [PATCH 103/124] Change flag name to be closer to spec --- reference/APPNOTE.TXT | 3497 +++++++++++++++++ .../Zip/Headers/DirectoryEntryHeader.cs | 12 +- .../Common/Zip/Headers/HeaderFlags.cs | 3 +- .../Common/Zip/Headers/LocalEntryHeader.cs | 9 +- .../Writers/Zip/ZipCentralDirectoryEntry.cs | 2 +- src/SharpCompress/Writers/Zip/ZipWriter.cs | 2 +- 6 files changed, 3512 insertions(+), 13 deletions(-) create mode 100644 reference/APPNOTE.TXT diff --git a/reference/APPNOTE.TXT b/reference/APPNOTE.TXT new file mode 100644 index 00000000..c89d8a8e --- /dev/null +++ b/reference/APPNOTE.TXT @@ -0,0 +1,3497 @@ +File: APPNOTE.TXT - .ZIP File Format Specification +Version: 6.3.4 +Status: Final - replaces version 6.3.3 +Revised: October 1, 2014 +Copyright (c) 1989 - 2014 PKWARE Inc., All Rights Reserved. + +1.0 Introduction +--------------- + +1.1 Purpose +----------- + + 1.1.1 This specification is intended to define a cross-platform, + interoperable file storage and transfer format. Since its + first publication in 1989, PKWARE, Inc. ("PKWARE") has remained + committed to ensuring the interoperability of the .ZIP file + format through periodic publication and maintenance of this + specification. We trust that all .ZIP compatible vendors and + application developers that use and benefit from this format + will share and support this commitment to interoperability. + +1.2 Scope +--------- + + 1.2.1 ZIP is one of the most widely used compressed file formats. It is + universally used to aggregate, compress, and encrypt files into a single + interoperable container. No specific use or application need is + defined by this format and no specific implementation guidance is + provided. This document provides details on the storage format for + creating ZIP files. Information is provided on the records and + fields that describe what a ZIP file is. + +1.3 Trademarks +-------------- + + 1.3.1 PKWARE, PKZIP, SecureZIP, and PKSFX are registered trademarks of + PKWARE, Inc. in the United States and elsewhere. PKPatchMaker, + Deflate64, and ZIP64 are trademarks of PKWARE, Inc. Other marks + referenced within this document appear for identification + purposes only and are the property of their respective owners. + + +1.4 Permitted Use +----------------- + + 1.4.1 This document, "APPNOTE.TXT - .ZIP File Format Specification" is the + exclusive property of PKWARE. Use of the information contained in this + document is permitted solely for the purpose of creating products, + programs and processes that read and write files in the ZIP format + subject to the terms and conditions herein. + + 1.4.2 Use of the content of this document within other publications is + permitted only through reference to this document. Any reproduction + or distribution of this document in whole or in part without prior + written permission from PKWARE is strictly prohibited. + + 1.4.3 Certain technological components provided in this document are the + patented proprietary technology of PKWARE and as such require a + separate, executed license agreement from PKWARE. Applicable + components are marked with the following, or similar, statement: + 'Refer to the section in this document entitled "Incorporating + PKWARE Proprietary Technology into Your Product" for more information'. + +1.5 Contacting PKWARE +--------------------- + + 1.5.1 If you have questions on this format, its use, or licensing, or if you + wish to report defects, request changes or additions, please contact: + + PKWARE, Inc. + 201 E. Pittsburgh Avenue, Suite 400 + Milwaukee, WI 53204 + +1-414-289-9788 + +1-414-289-9789 FAX + zipformat@pkware.com + + 1.5.2 Information about this format and copies of this document are publicly + available at: + + http://www.pkware.com/appnote + +1.6 Disclaimer +-------------- + + 1.6.1 Although PKWARE will attempt to supply current and accurate + information relating to its file formats, algorithms, and the + subject programs, the possibility of error or omission cannot + be eliminated. PKWARE therefore expressly disclaims any warranty + that the information contained in the associated materials relating + to the subject programs and/or the format of the files created or + accessed by the subject programs and/or the algorithms used by + the subject programs, or any other matter, is current, correct or + accurate as delivered. Any risk of damage due to any possible + inaccurate information is assumed by the user of the information. + Furthermore, the information relating to the subject programs + and/or the file formats created or accessed by the subject + programs and/or the algorithms used by the subject programs is + subject to change without notice. + +2.0 Revisions +-------------- + +2.1 Document Status +-------------------- + + 2.1.1 If the STATUS of this file is marked as DRAFT, the content + defines proposed revisions to this specification which may consist + of changes to the ZIP format itself, or that may consist of other + content changes to this document. Versions of this document and + the format in DRAFT form may be subject to modification prior to + publication STATUS of FINAL. DRAFT versions are published periodically + to provide notification to the ZIP community of pending changes and to + provide opportunity for review and comment. + + 2.1.2 Versions of this document having a STATUS of FINAL are + considered to be in the final form for that version of the document + and are not subject to further change until a new, higher version + numbered document is published. Newer versions of this format + specification are intended to remain interoperable with with all prior + versions whenever technically possible. + +2.2 Change Log +-------------- + + Version Change Description Date + ------- ------------------ ---------- + 5.2 -Single Password Symmetric Encryption 07/16/2003 + storage + + 6.1.0 -Smartcard compatibility 01/20/2004 + -Documentation on certificate storage + + 6.2.0 -Introduction of Central Directory 04/26/2004 + Encryption for encrypting metadata + -Added OS X to Version Made By values + + 6.2.1 -Added Extra Field placeholder for 04/01/2005 + POSZIP using ID 0x4690 + + -Clarified size field on + "zip64 end of central directory record" + + 6.2.2 -Documented Final Feature Specification 01/06/2006 + for Strong Encryption + + -Clarifications and typographical + corrections + + 6.3.0 -Added tape positioning storage 09/29/2006 + parameters + + -Expanded list of supported hash algorithms + + -Expanded list of supported compression + algorithms + + -Expanded list of supported encryption + algorithms + + -Added option for Unicode filename + storage + + -Clarifications for consistent use + of Data Descriptor records + + -Added additional "Extra Field" + definitions + + 6.3.1 -Corrected standard hash values for 04/11/2007 + SHA-256/384/512 + + 6.3.2 -Added compression method 97 09/28/2007 + + -Documented InfoZIP "Extra Field" + values for UTF-8 file name and + file comment storage + + 6.3.3 -Formatting changes to support 09/01/2012 + easier referencing of this APPNOTE + from other documents and standards + + 6.3.4 -Address change 10/01/2014 + + +3.0 Notations +------------- + + 3.1 Use of the term MUST or SHALL indicates a required element. + + 3.2 MAY NOT or SHALL NOT indicates an element is prohibited from use. + + 3.3 SHOULD indicates a RECOMMENDED element. + + 3.4 SHOULD NOT indicates an element NOT RECOMMENDED for use. + + 3.5 MAY indicates an OPTIONAL element. + + +4.0 ZIP Files +------------- + +4.1 What is a ZIP file +---------------------- + + 4.1.1 ZIP files MAY be identified by the standard .ZIP file extension + although use of a file extension is not required. Use of the + extension .ZIPX is also recognized and MAY be used for ZIP files. + Other common file extensions using the ZIP format include .JAR, .WAR, + .DOCX, .XLXS, .PPTX, .ODT, .ODS, .ODP and others. Programs reading or + writing ZIP files SHOULD rely on internal record signatures described + in this document to identify files in this format. + + 4.1.2 ZIP files SHOULD contain at least one file and MAY contain + multiple files. + + 4.1.3 Data compression MAY be used to reduce the size of files + placed into a ZIP file, but is not required. This format supports the + use of multiple data compression algorithms. When compression is used, + one of the documented compression algorithms MUST be used. Implementors + are advised to experiment with their data to determine which of the + available algorithms provides the best compression for their needs. + Compression method 8 (Deflate) is the method used by default by most + ZIP compatible application programs. + + + 4.1.4 Data encryption MAY be used to protect files within a ZIP file. + Keying methods supported for encryption within this format include + passwords and public/private keys. Either MAY be used individually + or in combination. Encryption MAY be applied to individual files. + Additional security MAY be used through the encryption of ZIP file + metadata stored within the Central Directory. See the section on the + Strong Encryption Specification for information. Refer to the section + in this document entitled "Incorporating PKWARE Proprietary Technology + into Your Product" for more information. + + 4.1.5 Data integrity MUST be provided for each file using CRC32. + + 4.1.6 Additional data integrity MAY be included through the use of + digital signatures. Individual files MAY be signed with one or more + digital signatures. The Central Directory, if signed, MUST use a + single signature. + + 4.1.7 Files MAY be placed within a ZIP file uncompressed or stored. + The term "stored" as used in the context of this document means the file + is copied into the ZIP file uncompressed. + + 4.1.8 Each data file placed into a ZIP file MAY be compressed, stored, + encrypted or digitally signed independent of how other data files in the + same ZIP file are archived. + + 4.1.9 ZIP files MAY be streamed, split into segments (on fixed or on + removable media) or "self-extracting". Self-extracting ZIP + files MUST include extraction code for a target platform within + the ZIP file. + + 4.1.10 Extensibility is provided for platform or application specific + needs through extra data fields that MAY be defined for custom + purposes. Extra data definitions MUST NOT conflict with existing + documented record definitions. + + 4.1.11 Common uses for ZIP MAY also include the use of manifest files. + Manifest files store application specific information within a file stored + within the ZIP file. This manifest file SHOULD be the first file in the + ZIP file. This specification does not provide any information or guidance on + the use of manifest files within ZIP files. Refer to the application developer + for information on using manifest files and for any additional profile + information on using ZIP within an application. + + 4.1.12 ZIP files MAY be placed within other ZIP files. + +4.2 ZIP Metadata +---------------- + + 4.2.1 ZIP files are identified by metadata consisting of defined record types + containing the storage information necessary for maintaining the files + placed into a ZIP file. Each record type MUST be identified using a header + signature that identifies the record type. Signature values begin with the + two byte constant marker of 0x4b50, representing the characters "PK". + + +4.3 General Format of a .ZIP file +--------------------------------- + + 4.3.1 A ZIP file MUST contain an "end of central directory record". A ZIP + file containing only an "end of central directory record" is considered an + empty ZIP file. Files may be added or replaced within a ZIP file, or deleted. + A ZIP file MUST have only one "end of central directory record". Other + records defined in this specification MAY be used as needed to support + storage requirements for individual ZIP files. + + 4.3.2 Each file placed into a ZIP file MUST be preceeded by a "local + file header" record for that file. Each "local file header" MUST be + accompanied by a corresponding "central directory header" record within + the central directory section of the ZIP file. + + 4.3.3 Files MAY be stored in arbitrary order within a ZIP file. A ZIP + file MAY span multiple volumes or it MAY be split into user-defined + segment sizes. All values MUST be stored in little-endian byte order unless + otherwise specified in this document for a specific data element. + + 4.3.4 Compression MUST NOT be applied to a "local file header", an "encryption + header", or an "end of central directory record". Individual "central + directory records" must not be compressed, but the aggregate of all central + directory records MAY be compressed. + + 4.3.5 File data MAY be followed by a "data descriptor" for the file. Data + descriptors are used to facilitate ZIP file streaming. + + + 4.3.6 Overall .ZIP file format: + + [local file header 1] + [encryption header 1] + [file data 1] + [data descriptor 1] + . + . + . + [local file header n] + [encryption header n] + [file data n] + [data descriptor n] + [archive decryption header] + [archive extra data record] + [central directory header 1] + . + . + . + [central directory header n] + [zip64 end of central directory record] + [zip64 end of central directory locator] + [end of central directory record] + + + 4.3.7 Local file header: + + local file header signature 4 bytes (0x04034b50) + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + file name length 2 bytes + extra field length 2 bytes + + file name (variable size) + extra field (variable size) + + 4.3.8 File data + + Immediately following the local header for a file + SHOULD be placed the compressed or stored data for the file. + If the file is encrypted, the encryption header for the file + SHOULD be placed after the local header and before the file + data. The series of [local file header][encryption header] + [file data][data descriptor] repeats for each file in the + .ZIP archive. + + Zero-byte files, directories, and other file types that + contain no content MUST not include file data. + + 4.3.9 Data descriptor: + + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + + 4.3.9.1 This descriptor MUST exist if bit 3 of the general + purpose bit flag is set (see below). It is byte aligned + and immediately follows the last byte of compressed data. + This descriptor SHOULD be used only when it was not possible to + seek in the output .ZIP file, e.g., when the output .ZIP file + was standard output or a non-seekable device. For ZIP64(tm) format + archives, the compressed and uncompressed sizes are 8 bytes each. + + 4.3.9.2 When compressing files, compressed and uncompressed sizes + should be stored in ZIP64 format (as 8 byte values) when a + file's size exceeds 0xFFFFFFFF. However ZIP64 format may be + used regardless of the size of a file. When extracting, if + the zip64 extended information extra field is present for + the file the compressed and uncompressed sizes will be 8 + byte values. + + 4.3.9.3 Although not originally assigned a signature, the value + 0x08074b50 has commonly been adopted as a signature value + for the data descriptor record. Implementers should be + aware that ZIP files may be encountered with or without this + signature marking data descriptors and SHOULD account for + either case when reading ZIP files to ensure compatibility. + + 4.3.9.4 When writing ZIP files, implementors SHOULD include the + signature value marking the data descriptor record. When + the signature is used, the fields currently defined for + the data descriptor record will immediately follow the + signature. + + 4.3.9.5 An extensible data descriptor will be released in a + future version of this APPNOTE. This new record is intended to + resolve conflicts with the use of this record going forward, + and to provide better support for streamed file processing. + + 4.3.9.6 When the Central Directory Encryption method is used, + the data descriptor record is not required, but MAY be used. + If present, and bit 3 of the general purpose bit field is set to + indicate its presence, the values in fields of the data descriptor + record MUST be set to binary zeros. See the section on the Strong + Encryption Specification for information. Refer to the section in + this document entitled "Incorporating PKWARE Proprietary Technology + into Your Product" for more information. + + + 4.3.10 Archive decryption header: + + 4.3.10.1 The Archive Decryption Header is introduced in version 6.2 + of the ZIP format specification. This record exists in support + of the Central Directory Encryption Feature implemented as part of + the Strong Encryption Specification as described in this document. + When the Central Directory Structure is encrypted, this decryption + header MUST precede the encrypted data segment. + + 4.3.10.2 The encrypted data segment SHALL consist of the Archive + extra data record (if present) and the encrypted Central Directory + Structure data. The format of this data record is identical to the + Decryption header record preceding compressed file data. If the + central directory structure is encrypted, the location of the start of + this data record is determined using the Start of Central Directory + field in the Zip64 End of Central Directory record. See the + section on the Strong Encryption Specification for information + on the fields used in the Archive Decryption Header record. + Refer to the section in this document entitled "Incorporating + PKWARE Proprietary Technology into Your Product" for more information. + + + 4.3.11 Archive extra data record: + + archive extra data signature 4 bytes (0x08064b50) + extra field length 4 bytes + extra field data (variable size) + + 4.3.11.1 The Archive Extra Data Record is introduced in version 6.2 + of the ZIP format specification. This record MAY be used in support + of the Central Directory Encryption Feature implemented as part of + the Strong Encryption Specification as described in this document. + When present, this record MUST immediately precede the central + directory data structure. + + 4.3.11.2 The size of this data record SHALL be included in the + Size of the Central Directory field in the End of Central + Directory record. If the central directory structure is compressed, + but not encrypted, the location of the start of this data record is + determined using the Start of Central Directory field in the Zip64 + End of Central Directory record. Refer to the section in this document + entitled "Incorporating PKWARE Proprietary Technology into Your + Product" for more information. + + 4.3.12 Central directory structure: + + [central directory header 1] + . + . + . + [central directory header n] + [digital signature] + + File header: + + central file header signature 4 bytes (0x02014b50) + version made by 2 bytes + version needed to extract 2 bytes + general purpose bit flag 2 bytes + compression method 2 bytes + last mod file time 2 bytes + last mod file date 2 bytes + crc-32 4 bytes + compressed size 4 bytes + uncompressed size 4 bytes + file name length 2 bytes + extra field length 2 bytes + file comment length 2 bytes + disk number start 2 bytes + internal file attributes 2 bytes + external file attributes 4 bytes + relative offset of local header 4 bytes + + file name (variable size) + extra field (variable size) + file comment (variable size) + + 4.3.13 Digital signature: + + header signature 4 bytes (0x05054b50) + size of data 2 bytes + signature data (variable size) + + With the introduction of the Central Directory Encryption + feature in version 6.2 of this specification, the Central + Directory Structure MAY be stored both compressed and encrypted. + Although not required, it is assumed when encrypting the + Central Directory Structure, that it will be compressed + for greater storage efficiency. Information on the + Central Directory Encryption feature can be found in the section + describing the Strong Encryption Specification. The Digital + Signature record will be neither compressed nor encrypted. + + 4.3.14 Zip64 end of central directory record + + zip64 end of central dir + signature 4 bytes (0x06064b50) + size of zip64 end of central + directory record 8 bytes + version made by 2 bytes + version needed to extract 2 bytes + number of this disk 4 bytes + number of the disk with the + start of the central directory 4 bytes + total number of entries in the + central directory on this disk 8 bytes + total number of entries in the + central directory 8 bytes + size of the central directory 8 bytes + offset of start of central + directory with respect to + the starting disk number 8 bytes + zip64 extensible data sector (variable size) + + 4.3.14.1 The value stored into the "size of zip64 end of central + directory record" should be the size of the remaining + record and should not include the leading 12 bytes. + + Size = SizeOfFixedFields + SizeOfVariableData - 12. + + 4.3.14.2 The above record structure defines Version 1 of the + zip64 end of central directory record. Version 1 was + implemented in versions of this specification preceding + 6.2 in support of the ZIP64 large file feature. The + introduction of the Central Directory Encryption feature + implemented in version 6.2 as part of the Strong Encryption + Specification defines Version 2 of this record structure. + Refer to the section describing the Strong Encryption + Specification for details on the version 2 format for + this record. Refer to the section in this document entitled + "Incorporating PKWARE Proprietary Technology into Your Product" + for more information applicable to use of Version 2 of this + record. + + 4.3.14.3 Special purpose data MAY reside in the zip64 extensible + data sector field following either a V1 or V2 version of this + record. To ensure identification of this special purpose data + it must include an identifying header block consisting of the + following: + + Header ID - 2 bytes + Data Size - 4 bytes + + The Header ID field indicates the type of data that is in the + data block that follows. + + Data Size identifies the number of bytes that follow for this + data block type. + + 4.3.14.4 Multiple special purpose data blocks MAY be present. + Each MUST be preceded by a Header ID and Data Size field. Current + mappings of Header ID values supported in this field are as + defined in APPENDIX C. + + 4.3.15 Zip64 end of central directory locator + + zip64 end of central dir locator + signature 4 bytes (0x07064b50) + number of the disk with the + start of the zip64 end of + central directory 4 bytes + relative offset of the zip64 + end of central directory record 8 bytes + total number of disks 4 bytes + + 4.3.16 End of central directory record: + + end of central dir signature 4 bytes (0x06054b50) + number of this disk 2 bytes + number of the disk with the + start of the central directory 2 bytes + total number of entries in the + central directory on this disk 2 bytes + total number of entries in + the central directory 2 bytes + size of the central directory 4 bytes + offset of start of central + directory with respect to + the starting disk number 4 bytes + .ZIP file comment length 2 bytes + .ZIP file comment (variable size) + +4.4 Explanation of fields +-------------------------- + + 4.4.1 General notes on fields + + 4.4.1.1 All fields unless otherwise noted are unsigned and stored + in Intel low-byte:high-byte, low-word:high-word order. + + 4.4.1.2 String fields are not null terminated, since the length + is given explicitly. + + 4.4.1.3 The entries in the central directory may not necessarily + be in the same order that files appear in the .ZIP file. + + 4.4.1.4 If one of the fields in the end of central directory + record is too small to hold required data, the field should be + set to -1 (0xFFFF or 0xFFFFFFFF) and the ZIP64 format record + should be created. + + 4.4.1.5 The end of central directory record and the Zip64 end + of central directory locator record MUST reside on the same + disk when splitting or spanning an archive. + + 4.4.2 version made by (2 bytes) + + 4.4.2.1 The upper byte indicates the compatibility of the file + attribute information. If the external file attributes + are compatible with MS-DOS and can be read by PKZIP for + DOS version 2.04g then this value will be zero. If these + attributes are not compatible, then this value will + identify the host system on which the attributes are + compatible. Software can use this information to determine + the line record format for text files etc. + + 4.4.2.2 The current mappings are: + + 0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems) + 1 - Amiga 2 - OpenVMS + 3 - UNIX 4 - VM/CMS + 5 - Atari ST 6 - OS/2 H.P.F.S. + 7 - Macintosh 8 - Z-System + 9 - CP/M 10 - Windows NTFS + 11 - MVS (OS/390 - Z/OS) 12 - VSE + 13 - Acorn Risc 14 - VFAT + 15 - alternate MVS 16 - BeOS + 17 - Tandem 18 - OS/400 + 19 - OS X (Darwin) 20 thru 255 - unused + + 4.4.2.3 The lower byte indicates the ZIP specification version + (the version of this document) supported by the software + used to encode the file. The value/10 indicates the major + version number, and the value mod 10 is the minor version + number. + + 4.4.3 version needed to extract (2 bytes) + + 4.4.3.1 The minimum supported ZIP specification version needed + to extract the file, mapped as above. This value is based on + the specific format features a ZIP program MUST support to + be able to extract the file. If multiple features are + applied to a file, the minimum version MUST be set to the + feature having the highest value. New features or feature + changes affecting the published format specification will be + implemented using higher version numbers than the last + published value to avoid conflict. + + 4.4.3.2 Current minimum feature versions are as defined below: + + 1.0 - Default value + 1.1 - File is a volume label + 2.0 - File is a folder (directory) + 2.0 - File is compressed using Deflate compression + 2.0 - File is encrypted using traditional PKWARE encryption + 2.1 - File is compressed using Deflate64(tm) + 2.5 - File is compressed using PKWARE DCL Implode + 2.7 - File is a patch data set + 4.5 - File uses ZIP64 format extensions + 4.6 - File is compressed using BZIP2 compression* + 5.0 - File is encrypted using DES + 5.0 - File is encrypted using 3DES + 5.0 - File is encrypted using original RC2 encryption + 5.0 - File is encrypted using RC4 encryption + 5.1 - File is encrypted using AES encryption + 5.1 - File is encrypted using corrected RC2 encryption** + 5.2 - File is encrypted using corrected RC2-64 encryption** + 6.1 - File is encrypted using non-OAEP key wrapping*** + 6.2 - Central directory encryption + 6.3 - File is compressed using LZMA + 6.3 - File is compressed using PPMd+ + 6.3 - File is encrypted using Blowfish + 6.3 - File is encrypted using Twofish + + 4.4.3.3 Notes on version needed to extract + + * Early 7.x (pre-7.2) versions of PKZIP incorrectly set the + version needed to extract for BZIP2 compression to be 50 + when it should have been 46. + + ** Refer to the section on Strong Encryption Specification + for additional information regarding RC2 corrections. + + *** Certificate encryption using non-OAEP key wrapping is the + intended mode of operation for all versions beginning with 6.1. + Support for OAEP key wrapping MUST only be used for + backward compatibility when sending ZIP files to be opened by + versions of PKZIP older than 6.1 (5.0 or 6.0). + + + Files compressed using PPMd MUST set the version + needed to extract field to 6.3, however, not all ZIP + programs enforce this and may be unable to decompress + data files compressed using PPMd if this value is set. + + When using ZIP64 extensions, the corresponding value in the + zip64 end of central directory record MUST also be set. + This field should be set appropriately to indicate whether + Version 1 or Version 2 format is in use. + + + 4.4.4 general purpose bit flag: (2 bytes) + + Bit 0: If set, indicates that the file is encrypted. + + (For Method 6 - Imploding) + Bit 1: If the compression method used was type 6, + Imploding, then this bit, if set, indicates + an 8K sliding dictionary was used. If clear, + then a 4K sliding dictionary was used. + + Bit 2: If the compression method used was type 6, + Imploding, then this bit, if set, indicates + 3 Shannon-Fano trees were used to encode the + sliding dictionary output. If clear, then 2 + Shannon-Fano trees were used. + + (For Methods 8 and 9 - Deflating) + Bit 2 Bit 1 + 0 0 Normal (-en) compression option was used. + 0 1 Maximum (-exx/-ex) compression option was used. + 1 0 Fast (-ef) compression option was used. + 1 1 Super Fast (-es) compression option was used. + + (For Method 14 - LZMA) + Bit 1: If the compression method used was type 14, + LZMA, then this bit, if set, indicates + an end-of-stream (EOS) marker is used to + mark the end of the compressed data stream. + If clear, then an EOS marker is not present + and the compressed data size must be known + to extract. + + Note: Bits 1 and 2 are undefined if the compression + method is any other. + + Bit 3: If this bit is set, the fields crc-32, compressed + size and uncompressed size are set to zero in the + local header. The correct values are put in the + data descriptor immediately following the compressed + data. (Note: PKZIP version 2.04g for DOS only + recognizes this bit for method 8 compression, newer + versions of PKZIP recognize this bit for any + compression method.) + + Bit 4: Reserved for use with method 8, for enhanced + deflating. + + Bit 5: If this bit is set, this indicates that the file is + compressed patched data. (Note: Requires PKZIP + version 2.70 or greater) + + Bit 6: Strong encryption. If this bit is set, you MUST + set the version needed to extract value to at least + 50 and you MUST also set bit 0. If AES encryption + is used, the version needed to extract value MUST + be at least 51. See the section describing the Strong + Encryption Specification for details. Refer to the + section in this document entitled "Incorporating PKWARE + Proprietary Technology into Your Product" for more + information. + + Bit 7: Currently unused. + + Bit 8: Currently unused. + + Bit 9: Currently unused. + + Bit 10: Currently unused. + + Bit 11: Language encoding flag (EFS). If this bit is set, + the filename and comment fields for this file + MUST be encoded using UTF-8. (see APPENDIX D) + + Bit 12: Reserved by PKWARE for enhanced compression. + + Bit 13: Set when encrypting the Central Directory to indicate + selected data values in the Local Header are masked to + hide their actual values. See the section describing + the Strong Encryption Specification for details. Refer + to the section in this document entitled "Incorporating + PKWARE Proprietary Technology into Your Product" for + more information. + + Bit 14: Reserved by PKWARE. + + Bit 15: Reserved by PKWARE. + + 4.4.5 compression method: (2 bytes) + + 0 - The file is stored (no compression) + 1 - The file is Shrunk + 2 - The file is Reduced with compression factor 1 + 3 - The file is Reduced with compression factor 2 + 4 - The file is Reduced with compression factor 3 + 5 - The file is Reduced with compression factor 4 + 6 - The file is Imploded + 7 - Reserved for Tokenizing compression algorithm + 8 - The file is Deflated + 9 - Enhanced Deflating using Deflate64(tm) + 10 - PKWARE Data Compression Library Imploding (old IBM TERSE) + 11 - Reserved by PKWARE + 12 - File is compressed using BZIP2 algorithm + 13 - Reserved by PKWARE + 14 - LZMA (EFS) + 15 - Reserved by PKWARE + 16 - Reserved by PKWARE + 17 - Reserved by PKWARE + 18 - File is compressed using IBM TERSE (new) + 19 - IBM LZ77 z Architecture (PFS) + 97 - WavPack compressed data + 98 - PPMd version I, Rev 1 + + + 4.4.6 date and time fields: (2 bytes each) + + The date and time are encoded in standard MS-DOS format. + If input came from standard input, the date and time are + those at which compression was started for this data. + If encrypting the central directory and general purpose bit + flag 13 is set indicating masking, the value stored in the + Local Header will be zero. + + 4.4.7 CRC-32: (4 bytes) + + The CRC-32 algorithm was generously contributed by + David Schwaderer and can be found in his excellent + book "C Programmers Guide to NetBIOS" published by + Howard W. Sams & Co. Inc. The 'magic number' for + the CRC is 0xdebb20e3. The proper CRC pre and post + conditioning is used, meaning that the CRC register + is pre-conditioned with all ones (a starting value + of 0xffffffff) and the value is post-conditioned by + taking the one's complement of the CRC residual. + If bit 3 of the general purpose flag is set, this + field is set to zero in the local header and the correct + value is put in the data descriptor and in the central + directory. When encrypting the central directory, if the + local header is not in ZIP64 format and general purpose + bit flag 13 is set indicating masking, the value stored + in the Local Header will be zero. + + 4.4.8 compressed size: (4 bytes) + 4.4.9 uncompressed size: (4 bytes) + + The size of the file compressed (4.4.8) and uncompressed, + (4.4.9) respectively. When a decryption header is present it + will be placed in front of the file data and the value of the + compressed file size will include the bytes of the decryption + header. If bit 3 of the general purpose bit flag is set, + these fields are set to zero in the local header and the + correct values are put in the data descriptor and + in the central directory. If an archive is in ZIP64 format + and the value in this field is 0xFFFFFFFF, the size will be + in the corresponding 8 byte ZIP64 extended information + extra field. When encrypting the central directory, if the + local header is not in ZIP64 format and general purpose bit + flag 13 is set indicating masking, the value stored for the + uncompressed size in the Local Header will be zero. + + 4.4.10 file name length: (2 bytes) + 4.4.11 extra field length: (2 bytes) + 4.4.12 file comment length: (2 bytes) + + The length of the file name, extra field, and comment + fields respectively. The combined length of any + directory record and these three fields should not + generally exceed 65,535 bytes. If input came from standard + input, the file name length is set to zero. + + + 4.4.13 disk number start: (2 bytes) + + The number of the disk on which this file begins. If an + archive is in ZIP64 format and the value in this field is + 0xFFFF, the size will be in the corresponding 4 byte zip64 + extended information extra field. + + 4.4.14 internal file attributes: (2 bytes) + + Bits 1 and 2 are reserved for use by PKWARE. + + 4.4.14.1 The lowest bit of this field indicates, if set, + that the file is apparently an ASCII or text file. If not + set, that the file apparently contains binary data. + The remaining bits are unused in version 1.0. + + 4.4.14.2 The 0x0002 bit of this field indicates, if set, that + a 4 byte variable record length control field precedes each + logical record indicating the length of the record. The + record length control field is stored in little-endian byte + order. This flag is independent of text control characters, + and if used in conjunction with text data, includes any + control characters in the total length of the record. This + value is provided for mainframe data transfer support. + + 4.4.15 external file attributes: (4 bytes) + + The mapping of the external attributes is + host-system dependent (see 'version made by'). For + MS-DOS, the low order byte is the MS-DOS directory + attribute byte. If input came from standard input, this + field is set to zero. + + 4.4.16 relative offset of local header: (4 bytes) + + This is the offset from the start of the first disk on + which this file appears, to where the local header should + be found. If an archive is in ZIP64 format and the value + in this field is 0xFFFFFFFF, the size will be in the + corresponding 8 byte zip64 extended information extra field. + + 4.4.17 file name: (Variable) + + 4.4.17.1 The name of the file, with optional relative path. + The path stored MUST not contain a drive or + device letter, or a leading slash. All slashes + MUST be forward slashes '/' as opposed to + backwards slashes '\' for compatibility with Amiga + and UNIX file systems etc. If input came from standard + input, there is no file name field. + + 4.4.17.2 If using the Central Directory Encryption Feature and + general purpose bit flag 13 is set indicating masking, the file + name stored in the Local Header will not be the actual file name. + A masking value consisting of a unique hexadecimal value will + be stored. This value will be sequentially incremented for each + file in the archive. See the section on the Strong Encryption + Specification for details on retrieving the encrypted file name. + Refer to the section in this document entitled "Incorporating PKWARE + Proprietary Technology into Your Product" for more information. + + + 4.4.18 file comment: (Variable) + + The comment for this file. + + 4.4.19 number of this disk: (2 bytes) + + The number of this disk, which contains central + directory end record. If an archive is in ZIP64 format + and the value in this field is 0xFFFF, the size will + be in the corresponding 4 byte zip64 end of central + directory field. + + + 4.4.20 number of the disk with the start of the central + directory: (2 bytes) + + The number of the disk on which the central + directory starts. If an archive is in ZIP64 format + and the value in this field is 0xFFFF, the size will + be in the corresponding 4 byte zip64 end of central + directory field. + + 4.4.21 total number of entries in the central dir on + this disk: (2 bytes) + + The number of central directory entries on this disk. + If an archive is in ZIP64 format and the value in + this field is 0xFFFF, the size will be in the + corresponding 8 byte zip64 end of central + directory field. + + 4.4.22 total number of entries in the central dir: (2 bytes) + + The total number of files in the .ZIP file. If an + archive is in ZIP64 format and the value in this field + is 0xFFFF, the size will be in the corresponding 8 byte + zip64 end of central directory field. + + 4.4.23 size of the central directory: (4 bytes) + + The size (in bytes) of the entire central directory. + If an archive is in ZIP64 format and the value in + this field is 0xFFFFFFFF, the size will be in the + corresponding 8 byte zip64 end of central + directory field. + + 4.4.24 offset of start of central directory with respect to + the starting disk number: (4 bytes) + + Offset of the start of the central directory on the + disk on which the central directory starts. If an + archive is in ZIP64 format and the value in this + field is 0xFFFFFFFF, the size will be in the + corresponding 8 byte zip64 end of central + directory field. + + 4.4.25 .ZIP file comment length: (2 bytes) + + The length of the comment for this .ZIP file. + + 4.4.26 .ZIP file comment: (Variable) + + The comment for this .ZIP file. ZIP file comment data + is stored unsecured. No encryption or data authentication + is applied to this area at this time. Confidential information + should not be stored in this section. + + 4.4.27 zip64 extensible data sector (variable size) + + (currently reserved for use by PKWARE) + + + 4.4.28 extra field: (Variable) + + This SHOULD be used for storage expansion. If additional + information needs to be stored within a ZIP file for special + application or platform needs, it SHOULD be stored here. + Programs supporting earlier versions of this specification can + then safely skip the file, and find the next file or header. + This field will be 0 length in version 1.0. + + Existing extra fields are defined in the section + Extensible data fields that follows. + +4.5 Extensible data fields +-------------------------- + + 4.5.1 In order to allow different programs and different types + of information to be stored in the 'extra' field in .ZIP + files, the following structure MUST be used for all + programs storing data in this field: + + header1+data1 + header2+data2 . . . + + Each header should consist of: + + Header ID - 2 bytes + Data Size - 2 bytes + + Note: all fields stored in Intel low-byte/high-byte order. + + The Header ID field indicates the type of data that is in + the following data block. + + Header IDs of 0 thru 31 are reserved for use by PKWARE. + The remaining IDs can be used by third party vendors for + proprietary usage. + + 4.5.2 The current Header ID mappings defined by PKWARE are: + + 0x0001 Zip64 extended information extra field + 0x0007 AV Info + 0x0008 Reserved for extended language encoding data (PFS) + (see APPENDIX D) + 0x0009 OS/2 + 0x000a NTFS + 0x000c OpenVMS + 0x000d UNIX + 0x000e Reserved for file stream and fork descriptors + 0x000f Patch Descriptor + 0x0014 PKCS#7 Store for X.509 Certificates + 0x0015 X.509 Certificate ID and Signature for + individual file + 0x0016 X.509 Certificate ID for Central Directory + 0x0017 Strong Encryption Header + 0x0018 Record Management Controls + 0x0019 PKCS#7 Encryption Recipient Certificate List + 0x0065 IBM S/390 (Z390), AS/400 (I400) attributes + - uncompressed + 0x0066 Reserved for IBM S/390 (Z390), AS/400 (I400) + attributes - compressed + 0x4690 POSZIP 4690 (reserved) + + + 4.5.3 -Zip64 Extended Information Extra Field (0x0001): + + The following is the layout of the zip64 extended + information "extra" block. If one of the size or + offset fields in the Local or Central directory + record is too small to hold the required data, + a Zip64 extended information record is created. + The order of the fields in the zip64 extended + information record is fixed, but the fields MUST + only appear if the corresponding Local or Central + directory record field is set to 0xFFFF or 0xFFFFFFFF. + + Note: all fields stored in Intel low-byte/high-byte order. + + Value Size Description + ----- ---- ----------- +(ZIP64) 0x0001 2 bytes Tag for this "extra" block type + Size 2 bytes Size of this "extra" block + Original + Size 8 bytes Original uncompressed file size + Compressed + Size 8 bytes Size of compressed data + Relative Header + Offset 8 bytes Offset of local header record + Disk Start + Number 4 bytes Number of the disk on which + this file starts + + This entry in the Local header MUST include BOTH original + and compressed file size fields. If encrypting the + central directory and bit 13 of the general purpose bit + flag is set indicating masking, the value stored in the + Local Header for the original file size will be zero. + + + 4.5.4 -OS/2 Extra Field (0x0009): + + The following is the layout of the OS/2 attributes "extra" + block. (Last Revision 09/05/95) + + Note: all fields stored in Intel low-byte/high-byte order. + + Value Size Description + ----- ---- ----------- +(OS/2) 0x0009 2 bytes Tag for this "extra" block type + TSize 2 bytes Size for the following data block + BSize 4 bytes Uncompressed Block Size + CType 2 bytes Compression type + EACRC 4 bytes CRC value for uncompress block + (var) variable Compressed block + + The OS/2 extended attribute structure (FEA2LIST) is + compressed and then stored in its entirety within this + structure. There will only ever be one "block" of data in + VarFields[]. + + 4.5.5 -NTFS Extra Field (0x000a): + + The following is the layout of the NTFS attributes + "extra" block. (Note: At this time the Mtime, Atime + and Ctime values MAY be used on any WIN32 system.) + + Note: all fields stored in Intel low-byte/high-byte order. + + Value Size Description + ----- ---- ----------- +(NTFS) 0x000a 2 bytes Tag for this "extra" block type + TSize 2 bytes Size of the total "extra" block + Reserved 4 bytes Reserved for future use + Tag1 2 bytes NTFS attribute tag value #1 + Size1 2 bytes Size of attribute #1, in bytes + (var) Size1 Attribute #1 data + . + . + . + TagN 2 bytes NTFS attribute tag value #N + SizeN 2 bytes Size of attribute #N, in bytes + (var) SizeN Attribute #N data + + For NTFS, values for Tag1 through TagN are as follows: + (currently only one set of attributes is defined for NTFS) + + Tag Size Description + ----- ---- ----------- + 0x0001 2 bytes Tag for attribute #1 + Size1 2 bytes Size of attribute #1, in bytes + Mtime 8 bytes File last modification time + Atime 8 bytes File last access time + Ctime 8 bytes File creation time + + 4.5.6 -OpenVMS Extra Field (0x000c): + + The following is the layout of the OpenVMS attributes + "extra" block. + + Note: all fields stored in Intel low-byte/high-byte order. + + Value Size Description + ----- ---- ----------- + (VMS) 0x000c 2 bytes Tag for this "extra" block type + TSize 2 bytes Size of the total "extra" block + CRC 4 bytes 32-bit CRC for remainder of the block + Tag1 2 bytes OpenVMS attribute tag value #1 + Size1 2 bytes Size of attribute #1, in bytes + (var) Size1 Attribute #1 data + . + . + . + TagN 2 bytes OpenVMS attribute tag value #N + SizeN 2 bytes Size of attribute #N, in bytes + (var) SizeN Attribute #N data + + OpenVMS Extra Field Rules: + + 4.5.6.1. There will be one or more attributes present, which + will each be preceded by the above TagX & SizeX values. + These values are identical to the ATR$C_XXXX and ATR$S_XXXX + constants which are defined in ATR.H under OpenVMS C. Neither + of these values will ever be zero. + + 4.5.6.2. No word alignment or padding is performed. + + 4.5.6.3. A well-behaved PKZIP/OpenVMS program should never produce + more than one sub-block with the same TagX value. Also, there will + never be more than one "extra" block of type 0x000c in a particular + directory record. + + 4.5.7 -UNIX Extra Field (0x000d): + + The following is the layout of the UNIX "extra" block. + Note: all fields are stored in Intel low-byte/high-byte + order. + + Value Size Description + ----- ---- ----------- +(UNIX) 0x000d 2 bytes Tag for this "extra" block type + TSize 2 bytes Size for the following data block + Atime 4 bytes File last access time + Mtime 4 bytes File last modification time + Uid 2 bytes File user ID + Gid 2 bytes File group ID + (var) variable Variable length data field + + The variable length data field will contain file type + specific data. Currently the only values allowed are + the original "linked to" file names for hard or symbolic + links, and the major and minor device node numbers for + character and block device nodes. Since device nodes + cannot be either symbolic or hard links, only one set of + variable length data is stored. Link files will have the + name of the original file stored. This name is NOT NULL + terminated. Its size can be determined by checking TSize - + 12. Device entries will have eight bytes stored as two 4 + byte entries (in little endian format). The first entry + will be the major device number, and the second the minor + device number. + + 4.5.8 -PATCH Descriptor Extra Field (0x000f): + + 4.5.8.1 The following is the layout of the Patch Descriptor + "extra" block. + + Note: all fields stored in Intel low-byte/high-byte order. + + Value Size Description + ----- ---- ----------- +(Patch) 0x000f 2 bytes Tag for this "extra" block type + TSize 2 bytes Size of the total "extra" block + Version 2 bytes Version of the descriptor + Flags 4 bytes Actions and reactions (see below) + OldSize 4 bytes Size of the file about to be patched + OldCRC 4 bytes 32-bit CRC of the file to be patched + NewSize 4 bytes Size of the resulting file + NewCRC 4 bytes 32-bit CRC of the resulting file + + 4.5.8.2 Actions and reactions + + Bits Description + ---- ---------------- + 0 Use for auto detection + 1 Treat as a self-patch + 2-3 RESERVED + 4-5 Action (see below) + 6-7 RESERVED + 8-9 Reaction (see below) to absent file + 10-11 Reaction (see below) to newer file + 12-13 Reaction (see below) to unknown file + 14-15 RESERVED + 16-31 RESERVED + + 4.5.8.2.1 Actions + + Action Value + ------ ----- + none 0 + add 1 + delete 2 + patch 3 + + 4.5.8.2.2 Reactions + + Reaction Value + -------- ----- + ask 0 + skip 1 + ignore 2 + fail 3 + + 4.5.8.3 Patch support is provided by PKPatchMaker(tm) technology + and is covered under U.S. Patents and Patents Pending. The use or + implementation in a product of certain technological aspects set + forth in the current APPNOTE, including those with regard to + strong encryption or patching requires a license from PKWARE. + Refer to the section in this document entitled "Incorporating + PKWARE Proprietary Technology into Your Product" for more + information. + + 4.5.9 -PKCS#7 Store for X.509 Certificates (0x0014): + + This field MUST contain information about each of the certificates + files may be signed with. When the Central Directory Encryption + feature is enabled for a ZIP file, this record will appear in + the Archive Extra Data Record, otherwise it will appear in the + first central directory record and will be ignored in any + other record. + + + Note: all fields stored in Intel low-byte/high-byte order. + + Value Size Description + ----- ---- ----------- +(Store) 0x0014 2 bytes Tag for this "extra" block type + TSize 2 bytes Size of the store data + TData TSize Data about the store + + + 4.5.10 -X.509 Certificate ID and Signature for individual file (0x0015): + + This field contains the information about which certificate in + the PKCS#7 store was used to sign a particular file. It also + contains the signature data. This field can appear multiple + times, but can only appear once per certificate. + + Note: all fields stored in Intel low-byte/high-byte order. + + Value Size Description + ----- ---- ----------- +(CID) 0x0015 2 bytes Tag for this "extra" block type + TSize 2 bytes Size of data that follows + TData TSize Signature Data + + 4.5.11 -X.509 Certificate ID and Signature for central directory (0x0016): + + This field contains the information about which certificate in + the PKCS#7 store was used to sign the central directory structure. + When the Central Directory Encryption feature is enabled for a + ZIP file, this record will appear in the Archive Extra Data Record, + otherwise it will appear in the first central directory record. + + Note: all fields stored in Intel low-byte/high-byte order. + + Value Size Description + ----- ---- ----------- +(CDID) 0x0016 2 bytes Tag for this "extra" block type + TSize 2 bytes Size of data that follows + TData TSize Data + + 4.5.12 -Strong Encryption Header (0x0017): + + Value Size Description + ----- ---- ----------- + 0x0017 2 bytes Tag for this "extra" block type + TSize 2 bytes Size of data that follows + Format 2 bytes Format definition for this record + AlgID 2 bytes Encryption algorithm identifier + Bitlen 2 bytes Bit length of encryption key + Flags 2 bytes Processing flags + CertData TSize-8 Certificate decryption extra field data + (refer to the explanation for CertData + in the section describing the + Certificate Processing Method under + the Strong Encryption Specification) + + See the section describing the Strong Encryption Specification + for details. Refer to the section in this document entitled + "Incorporating PKWARE Proprietary Technology into Your Product" + for more information. + + 4.5.13 -Record Management Controls (0x0018): + + Value Size Description + ----- ---- ----------- +(Rec-CTL) 0x0018 2 bytes Tag for this "extra" block type + CSize 2 bytes Size of total extra block data + Tag1 2 bytes Record control attribute 1 + Size1 2 bytes Size of attribute 1, in bytes + Data1 Size1 Attribute 1 data + . + . + . + TagN 2 bytes Record control attribute N + SizeN 2 bytes Size of attribute N, in bytes + DataN SizeN Attribute N data + + + 4.5.14 -PKCS#7 Encryption Recipient Certificate List (0x0019): + + This field MAY contain information about each of the certificates + used in encryption processing and it can be used to identify who is + allowed to decrypt encrypted files. This field should only appear + in the archive extra data record. This field is not required and + serves only to aid archive modifications by preserving public + encryption key data. Individual security requirements may dictate + that this data be omitted to deter information exposure. + + Note: all fields stored in Intel low-byte/high-byte order. + + Value Size Description + ----- ---- ----------- +(CStore) 0x0019 2 bytes Tag for this "extra" block type + TSize 2 bytes Size of the store data + TData TSize Data about the store + + TData: + + Value Size Description + ----- ---- ----------- + Version 2 bytes Format version number - must 0x0001 at this time + CStore (var) PKCS#7 data blob + + See the section describing the Strong Encryption Specification + for details. Refer to the section in this document entitled + "Incorporating PKWARE Proprietary Technology into Your Product" + for more information. + + 4.5.15 -MVS Extra Field (0x0065): + + The following is the layout of the MVS "extra" block. + Note: Some fields are stored in Big Endian format. + All text is in EBCDIC format unless otherwise specified. + + Value Size Description + ----- ---- ----------- +(MVS) 0x0065 2 bytes Tag for this "extra" block type + TSize 2 bytes Size for the following data block + ID 4 bytes EBCDIC "Z390" 0xE9F3F9F0 or + "T4MV" for TargetFour + (var) TSize-4 Attribute data (see APPENDIX B) + + + 4.5.16 -OS/400 Extra Field (0x0065): + + The following is the layout of the OS/400 "extra" block. + Note: Some fields are stored in Big Endian format. + All text is in EBCDIC format unless otherwise specified. + + Value Size Description + ----- ---- ----------- +(OS400) 0x0065 2 bytes Tag for this "extra" block type + TSize 2 bytes Size for the following data block + ID 4 bytes EBCDIC "I400" 0xC9F4F0F0 or + "T4MV" for TargetFour + (var) TSize-4 Attribute data (see APPENDIX A) + +4.6 Third Party Mappings +------------------------ + + 4.6.1 Third party mappings commonly used are: + + 0x07c8 Macintosh + 0x2605 ZipIt Macintosh + 0x2705 ZipIt Macintosh 1.3.5+ + 0x2805 ZipIt Macintosh 1.3.5+ + 0x334d Info-ZIP Macintosh + 0x4341 Acorn/SparkFS + 0x4453 Windows NT security descriptor (binary ACL) + 0x4704 VM/CMS + 0x470f MVS + 0x4b46 FWKCS MD5 (see below) + 0x4c41 OS/2 access control list (text ACL) + 0x4d49 Info-ZIP OpenVMS + 0x4f4c Xceed original location extra field + 0x5356 AOS/VS (ACL) + 0x5455 extended timestamp + 0x554e Xceed unicode extra field + 0x5855 Info-ZIP UNIX (original, also OS/2, NT, etc) + 0x6375 Info-ZIP Unicode Comment Extra Field + 0x6542 BeOS/BeBox + 0x7075 Info-ZIP Unicode Path Extra Field + 0x756e ASi UNIX + 0x7855 Info-ZIP UNIX (new) + 0xa220 Microsoft Open Packaging Growth Hint + 0xfd4a SMS/QDOS + + Detailed descriptions of Extra Fields defined by third + party mappings will be documented as information on + these data structures is made available to PKWARE. + PKWARE does not guarantee the accuracy of any published + third party data. + + 4.6.2 Third-party Extra Fields must include a Header ID using + the format defined in the section of this document + titled Extensible Data Fields (section 4.5). + + The Data Size field indicates the size of the following + data block. Programs can use this value to skip to the + next header block, passing over any data blocks that are + not of interest. + + Note: As stated above, the size of the entire .ZIP file + header, including the file name, comment, and extra + field should not exceed 64K in size. + + 4.6.3 In case two different programs should appropriate the same + Header ID value, it is strongly recommended that each + program SHOULD place a unique signature of at least two bytes in + size (and preferably 4 bytes or bigger) at the start of + each data area. Every program SHOULD verify that its + unique signature is present, in addition to the Header ID + value being correct, before assuming that it is a block of + known type. + + Third-party Mappings: + + 4.6.4 -ZipIt Macintosh Extra Field (long) (0x2605): + + The following is the layout of the ZipIt extra block + for Macintosh. The local-header and central-header versions + are identical. This block must be present if the file is + stored MacBinary-encoded and it should not be used if the file + is not stored MacBinary-encoded. + + Value Size Description + ----- ---- ----------- + (Mac2) 0x2605 Short tag for this extra block type + TSize Short total data size for this block + "ZPIT" beLong extra-field signature + FnLen Byte length of FileName + FileName variable full Macintosh filename + FileType Byte[4] four-byte Mac file type string + Creator Byte[4] four-byte Mac creator string + + + 4.6.5 -ZipIt Macintosh Extra Field (short, for files) (0x2705): + + The following is the layout of a shortened variant of the + ZipIt extra block for Macintosh (without "full name" entry). + This variant is used by ZipIt 1.3.5 and newer for entries of + files (not directories) that do not have a MacBinary encoded + file. The local-header and central-header versions are identical. + + Value Size Description + ----- ---- ----------- + (Mac2b) 0x2705 Short tag for this extra block type + TSize Short total data size for this block (12) + "ZPIT" beLong extra-field signature + FileType Byte[4] four-byte Mac file type string + Creator Byte[4] four-byte Mac creator string + fdFlags beShort attributes from FInfo.frFlags, + may be omitted + 0x0000 beShort reserved, may be omitted + + + 4.6.6 -ZipIt Macintosh Extra Field (short, for directories) (0x2805): + + The following is the layout of a shortened variant of the + ZipIt extra block for Macintosh used only for directory + entries. This variant is used by ZipIt 1.3.5 and newer to + save some optional Mac-specific information about directories. + The local-header and central-header versions are identical. + + Value Size Description + ----- ---- ----------- + (Mac2c) 0x2805 Short tag for this extra block type + TSize Short total data size for this block (12) + "ZPIT" beLong extra-field signature + frFlags beShort attributes from DInfo.frFlags, may + be omitted + View beShort ZipIt view flag, may be omitted + + + The View field specifies ZipIt-internal settings as follows: + + Bits of the Flags: + bit 0 if set, the folder is shown expanded (open) + when the archive contents are viewed in ZipIt. + bits 1-15 reserved, zero; + + + 4.6.7 -FWKCS MD5 Extra Field (0x4b46): + + The FWKCS Contents_Signature System, used in + automatically identifying files independent of file name, + optionally adds and uses an extra field to support the + rapid creation of an enhanced contents_signature: + + Header ID = 0x4b46 + Data Size = 0x0013 + Preface = 'M','D','5' + followed by 16 bytes containing the uncompressed file's + 128_bit MD5 hash(1), low byte first. + + When FWKCS revises a .ZIP file central directory to add + this extra field for a file, it also replaces the + central directory entry for that file's uncompressed + file length with a measured value. + + FWKCS provides an option to strip this extra field, if + present, from a .ZIP file central directory. In adding + this extra field, FWKCS preserves .ZIP file Authenticity + Verification; if stripping this extra field, FWKCS + preserves all versions of AV through PKZIP version 2.04g. + + FWKCS, and FWKCS Contents_Signature System, are + trademarks of Frederick W. Kantor. + + (1) R. Rivest, RFC1321.TXT, MIT Laboratory for Computer + Science and RSA Data Security, Inc., April 1992. + ll.76-77: "The MD5 algorithm is being placed in the + public domain for review and possible adoption as a + standard." + + + 4.6.8 -Info-ZIP Unicode Comment Extra Field (0x6375): + + Stores the UTF-8 version of the file comment as stored in the + central directory header. (Last Revision 20070912) + + Value Size Description + ----- ---- ----------- + (UCom) 0x6375 Short tag for this extra block type ("uc") + TSize Short total data size for this block + Version 1 byte version of this extra field, currently 1 + ComCRC32 4 bytes Comment Field CRC32 Checksum + UnicodeCom Variable UTF-8 version of the entry comment + + Currently Version is set to the number 1. If there is a need + to change this field, the version will be incremented. Changes + may not be backward compatible so this extra field should not be + used if the version is not recognized. + + The ComCRC32 is the standard zip CRC32 checksum of the File Comment + field in the central directory header. This is used to verify that + the comment field has not changed since the Unicode Comment extra field + was created. This can happen if a utility changes the File Comment + field but does not update the UTF-8 Comment extra field. If the CRC + check fails, this Unicode Comment extra field should be ignored and + the File Comment field in the header should be used instead. + + The UnicodeCom field is the UTF-8 version of the File Comment field + in the header. As UnicodeCom is defined to be UTF-8, no UTF-8 byte + order mark (BOM) is used. The length of this field is determined by + subtracting the size of the previous fields from TSize. If both the + File Name and Comment fields are UTF-8, the new General Purpose Bit + Flag, bit 11 (Language encoding flag (EFS)), can be used to indicate + both the header File Name and Comment fields are UTF-8 and, in this + case, the Unicode Path and Unicode Comment extra fields are not + needed and should not be created. Note that, for backward + compatibility, bit 11 should only be used if the native character set + of the paths and comments being zipped up are already in UTF-8. It is + expected that the same file comment storage method, either general + purpose bit 11 or extra fields, be used in both the Local and Central + Directory Header for a file. + + + 4.6.9 -Info-ZIP Unicode Path Extra Field (0x7075): + + Stores the UTF-8 version of the file name field as stored in the + local header and central directory header. (Last Revision 20070912) + + Value Size Description + ----- ---- ----------- + (UPath) 0x7075 Short tag for this extra block type ("up") + TSize Short total data size for this block + Version 1 byte version of this extra field, currently 1 + NameCRC32 4 bytes File Name Field CRC32 Checksum + UnicodeName Variable UTF-8 version of the entry File Name + + Currently Version is set to the number 1. If there is a need + to change this field, the version will be incremented. Changes + may not be backward compatible so this extra field should not be + used if the version is not recognized. + + The NameCRC32 is the standard zip CRC32 checksum of the File Name + field in the header. This is used to verify that the header + File Name field has not changed since the Unicode Path extra field + was created. This can happen if a utility renames the File Name but + does not update the UTF-8 path extra field. If the CRC check fails, + this UTF-8 Path Extra Field should be ignored and the File Name field + in the header should be used instead. + + The UnicodeName is the UTF-8 version of the contents of the File Name + field in the header. As UnicodeName is defined to be UTF-8, no UTF-8 + byte order mark (BOM) is used. The length of this field is determined + by subtracting the size of the previous fields from TSize. If both + the File Name and Comment fields are UTF-8, the new General Purpose + Bit Flag, bit 11 (Language encoding flag (EFS)), can be used to + indicate that both the header File Name and Comment fields are UTF-8 + and, in this case, the Unicode Path and Unicode Comment extra fields + are not needed and should not be created. Note that, for backward + compatibility, bit 11 should only be used if the native character set + of the paths and comments being zipped up are already in UTF-8. It is + expected that the same file name storage method, either general + purpose bit 11 or extra fields, be used in both the Local and Central + Directory Header for a file. + + + 4.6.10 -Microsoft Open Packaging Growth Hint (0xa220): + + Value Size Description + ----- ---- ----------- + 0xa220 Short tag for this extra block type + TSize Short size of Sig + PadVal + Padding + Sig Short verification signature (A028) + PadVal Short Initial padding value + Padding variable filled with NULL characters + +4.7 Manifest Files +------------------ + + 4.7.1 Applications using ZIP files may have a need for additional + information that must be included with the files placed into + a ZIP file. Application specific information that cannot be + stored using the defined ZIP storage records SHOULD be stored + using the extensible Extra Field convention defined in this + document. However, some applications may use a manifest + file as a means for storing additional information. One + example is the META-INF/MANIFEST.MF file used in ZIP formatted + files having the .JAR extension (JAR files). + + 4.7.2 A manifest file is a file created for the application process + that requires this information. A manifest file MAY be of any + file type required by the defining application process. It is + placed within the same ZIP file as files to which this information + applies. By convention, this file is typically the first file placed + into the ZIP file and it may include a defined directory path. + + 4.7.3 Manifest files may be compressed or encrypted as needed for + application processing of the files inside the ZIP files. + + Manifest files are outside of the scope of this specification. + + +5.0 Explanation of compression methods +-------------------------------------- + + +5.1 UnShrinking - Method 1 +-------------------------- + + 5.1.1 Shrinking is a Dynamic Ziv-Lempel-Welch compression algorithm + with partial clearing. The initial code size is 9 bits, and the + maximum code size is 13 bits. Shrinking differs from conventional + Dynamic Ziv-Lempel-Welch implementations in several respects: + + 5.1.2 The code size is controlled by the compressor, and is + not automatically increased when codes larger than the current + code size are created (but not necessarily used). When + the decompressor encounters the code sequence 256 + (decimal) followed by 1, it should increase the code size + read from the input stream to the next bit size. No + blocking of the codes is performed, so the next code at + the increased size should be read from the input stream + immediately after where the previous code at the smaller + bit size was read. Again, the decompressor should not + increase the code size used until the sequence 256,1 is + encountered. + + 5.1.3 When the table becomes full, total clearing is not + performed. Rather, when the compressor emits the code + sequence 256,2 (decimal), the decompressor should clear + all leaf nodes from the Ziv-Lempel tree, and continue to + use the current code size. The nodes that are cleared + from the Ziv-Lempel tree are then re-used, with the lowest + code value re-used first, and the highest code value + re-used last. The compressor can emit the sequence 256,2 + at any time. + +5.2 Expanding - Methods 2-5 +--------------------------- + + 5.2.1 The Reducing algorithm is actually a combination of two + distinct algorithms. The first algorithm compresses repeated + byte sequences, and the second algorithm takes the compressed + stream from the first algorithm and applies a probabilistic + compression method. + + 5.2.2 The probabilistic compression stores an array of 'follower + sets' S(j), for j=0 to 255, corresponding to each possible + ASCII character. Each set contains between 0 and 32 + characters, to be denoted as S(j)[0],...,S(j)[m], where m<32. + The sets are stored at the beginning of the data area for a + Reduced file, in reverse order, with S(255) first, and S(0) + last. + + 5.2.3 The sets are encoded as { N(j), S(j)[0],...,S(j)[N(j)-1] }, + where N(j) is the size of set S(j). N(j) can be 0, in which + case the follower set for S(j) is empty. Each N(j) value is + encoded in 6 bits, followed by N(j) eight bit character values + corresponding to S(j)[0] to S(j)[N(j)-1] respectively. If + N(j) is 0, then no values for S(j) are stored, and the value + for N(j-1) immediately follows. + + 5.2.4 Immediately after the follower sets, is the compressed data + stream. The compressed data stream can be interpreted for the + probabilistic decompression as follows: + + let Last-Character <- 0. + loop until done + if the follower set S(Last-Character) is empty then + read 8 bits from the input stream, and copy this + value to the output stream. + otherwise if the follower set S(Last-Character) is non-empty then + read 1 bit from the input stream. + if this bit is not zero then + read 8 bits from the input stream, and copy this + value to the output stream. + otherwise if this bit is zero then + read B(N(Last-Character)) bits from the input + stream, and assign this value to I. + Copy the value of S(Last-Character)[I] to the + output stream. + + assign the last value placed on the output stream to + Last-Character. + end loop + + B(N(j)) is defined as the minimal number of bits required to + encode the value N(j)-1. + + 5.2.5 The decompressed stream from above can then be expanded to + re-create the original file as follows: + + let State <- 0. + + loop until done + read 8 bits from the input stream into C. + case State of + 0: if C is not equal to DLE (144 decimal) then + copy C to the output stream. + otherwise if C is equal to DLE then + let State <- 1. + + 1: if C is non-zero then + let V <- C. + let Len <- L(V) + let State <- F(Len). + otherwise if C is zero then + copy the value 144 (decimal) to the output stream. + let State <- 0 + + 2: let Len <- Len + C + let State <- 3. + + 3: move backwards D(V,C) bytes in the output stream + (if this position is before the start of the output + stream, then assume that all the data before the + start of the output stream is filled with zeros). + copy Len+3 bytes from this position to the output stream. + let State <- 0. + end case + end loop + + The functions F,L, and D are dependent on the 'compression + factor', 1 through 4, and are defined as follows: + + For compression factor 1: + L(X) equals the lower 7 bits of X. + F(X) equals 2 if X equals 127 otherwise F(X) equals 3. + D(X,Y) equals the (upper 1 bit of X) * 256 + Y + 1. + For compression factor 2: + L(X) equals the lower 6 bits of X. + F(X) equals 2 if X equals 63 otherwise F(X) equals 3. + D(X,Y) equals the (upper 2 bits of X) * 256 + Y + 1. + For compression factor 3: + L(X) equals the lower 5 bits of X. + F(X) equals 2 if X equals 31 otherwise F(X) equals 3. + D(X,Y) equals the (upper 3 bits of X) * 256 + Y + 1. + For compression factor 4: + L(X) equals the lower 4 bits of X. + F(X) equals 2 if X equals 15 otherwise F(X) equals 3. + D(X,Y) equals the (upper 4 bits of X) * 256 + Y + 1. + +5.3 Imploding - Method 6 +------------------------ + + 5.3.1 The Imploding algorithm is actually a combination of two + distinct algorithms. The first algorithm compresses repeated byte + sequences using a sliding dictionary. The second algorithm is + used to compress the encoding of the sliding dictionary output, + using multiple Shannon-Fano trees. + + 5.3.2 The Imploding algorithm can use a 4K or 8K sliding dictionary + size. The dictionary size used can be determined by bit 1 in the + general purpose flag word; a 0 bit indicates a 4K dictionary + while a 1 bit indicates an 8K dictionary. + + 5.3.3 The Shannon-Fano trees are stored at the start of the + compressed file. The number of trees stored is defined by bit 2 in + the general purpose flag word; a 0 bit indicates two trees stored, + a 1 bit indicates three trees are stored. If 3 trees are stored, + the first Shannon-Fano tree represents the encoding of the + Literal characters, the second tree represents the encoding of + the Length information, the third represents the encoding of the + Distance information. When 2 Shannon-Fano trees are stored, the + Length tree is stored first, followed by the Distance tree. + + 5.3.4 The Literal Shannon-Fano tree, if present is used to represent + the entire ASCII character set, and contains 256 values. This + tree is used to compress any data not compressed by the sliding + dictionary algorithm. When this tree is present, the Minimum + Match Length for the sliding dictionary is 3. If this tree is + not present, the Minimum Match Length is 2. + + 5.3.5 The Length Shannon-Fano tree is used to compress the Length + part of the (length,distance) pairs from the sliding dictionary + output. The Length tree contains 64 values, ranging from the + Minimum Match Length, to 63 plus the Minimum Match Length. + + 5.3.6 The Distance Shannon-Fano tree is used to compress the Distance + part of the (length,distance) pairs from the sliding dictionary + output. The Distance tree contains 64 values, ranging from 0 to + 63, representing the upper 6 bits of the distance value. The + distance values themselves will be between 0 and the sliding + dictionary size, either 4K or 8K. + + 5.3.7 The Shannon-Fano trees themselves are stored in a compressed + format. The first byte of the tree data represents the number of + bytes of data representing the (compressed) Shannon-Fano tree + minus 1. The remaining bytes represent the Shannon-Fano tree + data encoded as: + + High 4 bits: Number of values at this bit length + 1. (1 - 16) + Low 4 bits: Bit Length needed to represent value + 1. (1 - 16) + + 5.3.8 The Shannon-Fano codes can be constructed from the bit lengths + using the following algorithm: + + 1) Sort the Bit Lengths in ascending order, while retaining the + order of the original lengths stored in the file. + + 2) Generate the Shannon-Fano trees: + + Code <- 0 + CodeIncrement <- 0 + LastBitLength <- 0 + i <- number of Shannon-Fano codes - 1 (either 255 or 63) + + loop while i >= 0 + Code = Code + CodeIncrement + if BitLength(i) <> LastBitLength then + LastBitLength=BitLength(i) + CodeIncrement = 1 shifted left (16 - LastBitLength) + ShannonCode(i) = Code + i <- i - 1 + end loop + + 3) Reverse the order of all the bits in the above ShannonCode() + vector, so that the most significant bit becomes the least + significant bit. For example, the value 0x1234 (hex) would + become 0x2C48 (hex). + + 4) Restore the order of Shannon-Fano codes as originally stored + within the file. + + Example: + + This example will show the encoding of a Shannon-Fano tree + of size 8. Notice that the actual Shannon-Fano trees used + for Imploding are either 64 or 256 entries in size. + + Example: 0x02, 0x42, 0x01, 0x13 + + The first byte indicates 3 values in this table. Decoding the + bytes: + 0x42 = 5 codes of 3 bits long + 0x01 = 1 code of 2 bits long + 0x13 = 2 codes of 4 bits long + + This would generate the original bit length array of: + (3, 3, 3, 3, 3, 2, 4, 4) + + There are 8 codes in this table for the values 0 thru 7. Using + the algorithm to obtain the Shannon-Fano codes produces: + + Reversed Order Original + Val Sorted Constructed Code Value Restored Length + --- ------ ----------------- -------- -------- ------ + 0: 2 1100000000000000 11 101 3 + 1: 3 1010000000000000 101 001 3 + 2: 3 1000000000000000 001 110 3 + 3: 3 0110000000000000 110 010 3 + 4: 3 0100000000000000 010 100 3 + 5: 3 0010000000000000 100 11 2 + 6: 4 0001000000000000 1000 1000 4 + 7: 4 0000000000000000 0000 0000 4 + + The values in the Val, Order Restored and Original Length columns + now represent the Shannon-Fano encoding tree that can be used for + decoding the Shannon-Fano encoded data. How to parse the + variable length Shannon-Fano values from the data stream is beyond + the scope of this document. (See the references listed at the end of + this document for more information.) However, traditional decoding + schemes used for Huffman variable length decoding, such as the + Greenlaw algorithm, can be successfully applied. + + 5.3.9 The compressed data stream begins immediately after the + compressed Shannon-Fano data. The compressed data stream can be + interpreted as follows: + + loop until done + read 1 bit from input stream. + + if this bit is non-zero then (encoded data is literal data) + if Literal Shannon-Fano tree is present + read and decode character using Literal Shannon-Fano tree. + otherwise + read 8 bits from input stream. + copy character to the output stream. + otherwise (encoded data is sliding dictionary match) + if 8K dictionary size + read 7 bits for offset Distance (lower 7 bits of offset). + otherwise + read 6 bits for offset Distance (lower 6 bits of offset). + + using the Distance Shannon-Fano tree, read and decode the + upper 6 bits of the Distance value. + + using the Length Shannon-Fano tree, read and decode + the Length value. + + Length <- Length + Minimum Match Length + + if Length = 63 + Minimum Match Length + read 8 bits from the input stream, + add this value to Length. + + move backwards Distance+1 bytes in the output stream, and + copy Length characters from this position to the output + stream. (if this position is before the start of the output + stream, then assume that all the data before the start of + the output stream is filled with zeros). + end loop + +5.4 Tokenizing - Method 7 +------------------------- + + 5.4.1 This method is not used by PKZIP. + +5.5 Deflating - Method 8 +------------------------ + + 5.5.1 The Deflate algorithm is similar to the Implode algorithm using + a sliding dictionary of up to 32K with secondary compression + from Huffman/Shannon-Fano codes. + + 5.5.2 The compressed data is stored in blocks with a header describing + the block and the Huffman codes used in the data block. The header + format is as follows: + + Bit 0: Last Block bit This bit is set to 1 if this is the last + compressed block in the data. + Bits 1-2: Block type + 00 (0) - Block is stored - All stored data is byte aligned. + Skip bits until next byte, then next word = block + length, followed by the ones compliment of the block + length word. Remaining data in block is the stored + data. + + 01 (1) - Use fixed Huffman codes for literal and distance codes. + Lit Code Bits Dist Code Bits + --------- ---- --------- ---- + 0 - 143 8 0 - 31 5 + 144 - 255 9 + 256 - 279 7 + 280 - 287 8 + + Literal codes 286-287 and distance codes 30-31 are + never used but participate in the huffman construction. + + 10 (2) - Dynamic Huffman codes. (See expanding Huffman codes) + + 11 (3) - Reserved - Flag a "Error in compressed data" if seen. + + 5.5.3 Expanding Huffman Codes + + If the data block is stored with dynamic Huffman codes, the Huffman + codes are sent in the following compressed format: + + 5 Bits: # of Literal codes sent - 256 (256 - 286) + All other codes are never sent. + 5 Bits: # of Dist codes - 1 (1 - 32) + 4 Bits: # of Bit Length codes - 3 (3 - 19) + + The Huffman codes are sent as bit lengths and the codes are built as + described in the implode algorithm. The bit lengths themselves are + compressed with Huffman codes. There are 19 bit length codes: + + 0 - 15: Represent bit lengths of 0 - 15 + 16: Copy the previous bit length 3 - 6 times. + The next 2 bits indicate repeat length (0 = 3, ... ,3 = 6) + Example: Codes 8, 16 (+2 bits 11), 16 (+2 bits 10) will + expand to 12 bit lengths of 8 (1 + 6 + 5) + 17: Repeat a bit length of 0 for 3 - 10 times. (3 bits of length) + 18: Repeat a bit length of 0 for 11 - 138 times (7 bits of length) + + The lengths of the bit length codes are sent packed 3 bits per value + (0 - 7) in the following order: + + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + + The Huffman codes should be built as described in the Implode algorithm + except codes are assigned starting at the shortest bit length, i.e. the + shortest code should be all 0's rather than all 1's. Also, codes with + a bit length of zero do not participate in the tree construction. The + codes are then used to decode the bit lengths for the literal and + distance tables. + + The bit lengths for the literal tables are sent first with the number + of entries sent described by the 5 bits sent earlier. There are up + to 286 literal characters; the first 256 represent the respective 8 + bit character, code 256 represents the End-Of-Block code, the remaining + 29 codes represent copy lengths of 3 thru 258. There are up to 30 + distance codes representing distances from 1 thru 32k as described + below. + + Length Codes + ------------ + Extra Extra Extra Extra + Code Bits Length Code Bits Lengths Code Bits Lengths Code Bits Length(s) + ---- ---- ------ ---- ---- ------- ---- ---- ------- ---- ---- --------- + 257 0 3 265 1 11,12 273 3 35-42 281 5 131-162 + 258 0 4 266 1 13,14 274 3 43-50 282 5 163-194 + 259 0 5 267 1 15,16 275 3 51-58 283 5 195-226 + 260 0 6 268 1 17,18 276 3 59-66 284 5 227-257 + 261 0 7 269 2 19-22 277 4 67-82 285 0 258 + 262 0 8 270 2 23-26 278 4 83-98 + 263 0 9 271 2 27-30 279 4 99-114 + 264 0 10 272 2 31-34 280 4 115-130 + + Distance Codes + -------------- + Extra Extra Extra Extra + Code Bits Dist Code Bits Dist Code Bits Distance Code Bits Distance + ---- ---- ---- ---- ---- ------ ---- ---- -------- ---- ---- -------- + 0 0 1 8 3 17-24 16 7 257-384 24 11 4097-6144 + 1 0 2 9 3 25-32 17 7 385-512 25 11 6145-8192 + 2 0 3 10 4 33-48 18 8 513-768 26 12 8193-12288 + 3 0 4 11 4 49-64 19 8 769-1024 27 12 12289-16384 + 4 1 5,6 12 5 65-96 20 9 1025-1536 28 13 16385-24576 + 5 1 7,8 13 5 97-128 21 9 1537-2048 29 13 24577-32768 + 6 2 9-12 14 6 129-192 22 10 2049-3072 + 7 2 13-16 15 6 193-256 23 10 3073-4096 + + 5.5.4 The compressed data stream begins immediately after the + compressed header data. The compressed data stream can be + interpreted as follows: + + do + read header from input stream. + + if stored block + skip bits until byte aligned + read count and 1's compliment of count + copy count bytes data block + otherwise + loop until end of block code sent + decode literal character from input stream + if literal < 256 + copy character to the output stream + otherwise + if literal = end of block + break from loop + otherwise + decode distance from input stream + + move backwards distance bytes in the output stream, and + copy length characters from this position to the output + stream. + end loop + while not last block + + if data descriptor exists + skip bits until byte aligned + read crc and sizes + endif + +5.6 Enhanced Deflating - Method 9 +--------------------------------- + + 5.6.1 The Enhanced Deflating algorithm is similar to Deflate but uses + a sliding dictionary of up to 64K. Deflate64(tm) is supported + by the Deflate extractor. + +5.7 BZIP2 - Method 12 +--------------------- + + 5.7.1 BZIP2 is an open-source data compression algorithm developed by + Julian Seward. Information and source code for this algorithm + can be found on the internet. + +5.8 LZMA - Method 14 +--------------------- + + 5.8.1 LZMA is a block-oriented, general purpose data compression + algorithm developed and maintained by Igor Pavlov. It is a derivative + of LZ77 that utilizes Markov chains and a range coder. Information and + source code for this algorithm can be found on the internet. Consult + with the author of this algorithm for information on terms or + restrictions on use. + + Support for LZMA within the ZIP format is defined as follows: + + 5.8.2 The Compression method field within the ZIP Local and Central + Header records will be set to the value 14 to indicate data was + compressed using LZMA. + + 5.8.3 The Version needed to extract field within the ZIP Local and + Central Header records will be set to 6.3 to indicate the minimum + ZIP format version supporting this feature. + + 5.8.4 File data compressed using the LZMA algorithm must be placed + immediately following the Local Header for the file. If a standard + ZIP encryption header is required, it will follow the Local Header + and will precede the LZMA compressed file data segment. The location + of LZMA compressed data segment within the ZIP format will be as shown: + + [local header file 1] + [encryption header file 1] + [LZMA compressed data segment for file 1] + [data descriptor 1] + [local header file 2] + + 5.8.5 The encryption header and data descriptor records may + be conditionally present. The LZMA Compressed Data Segment + will consist of an LZMA Properties Header followed by the + LZMA Compressed Data as shown: + + [LZMA properties header for file 1] + [LZMA compressed data for file 1] + + 5.8.6 The LZMA Compressed Data will be stored as provided by the + LZMA compression library. Compressed size, uncompressed size and + other file characteristics about the file being compressed must be + stored in standard ZIP storage format. + + 5.8.7 The LZMA Properties Header will store specific data required + to decompress the LZMA compressed Data. This data is set by the + LZMA compression engine using the function WriteCoderProperties() + as documented within the LZMA SDK. + + 5.8.8 Storage fields for the property information within the LZMA + Properties Header are as follows: + + LZMA Version Information 2 bytes + LZMA Properties Size 2 bytes + LZMA Properties Data variable, defined by "LZMA Properties Size" + + 5.8.8.1 LZMA Version Information - this field identifies which version + of the LZMA SDK was used to compress a file. The first byte will + store the major version number of the LZMA SDK and the second + byte will store the minor number. + + 5.8.8.2 LZMA Properties Size - this field defines the size of the + remaining property data. Typically this size should be determined by + the version of the SDK. This size field is included as a convenience + and to help avoid any ambiguity should it arise in the future due + to changes in this compression algorithm. + + 5.8.8.3 LZMA Property Data - this variable sized field records the + required values for the decompressor as defined by the LZMA SDK. + The data stored in this field should be obtained using the + WriteCoderProperties() in the version of the SDK defined by + the "LZMA Version Information" field. + + 5.8.8.4 The layout of the "LZMA Properties Data" field is a function of + the LZMA compression algorithm. It is possible that this layout may be + changed by the author over time. The data layout in version 4.3 of the + LZMA SDK defines a 5 byte array that uses 4 bytes to store the dictionary + size in little-endian order. This is preceded by a single packed byte as + the first element of the array that contains the following fields: + + PosStateBits + LiteralPosStateBits + LiteralContextBits + + Refer to the LZMA documentation for a more detailed explanation of + these fields. + + 5.8.9 Data compressed with method 14, LZMA, may include an end-of-stream + (EOS) marker ending the compressed data stream. This marker is not + required, but its use is highly recommended to facilitate processing + and implementers should include the EOS marker whenever possible. + When the EOS marker is used, general purpose bit 1 must be set. If + general purpose bit 1 is not set, the EOS marker is not present. + +5.9 WavPack - Method 97 +----------------------- + + 5.9.1 Information describing the use of compression method 97 is + provided by WinZIP International, LLC. This method relies on the + open source WavPack audio compression utility developed by David Bryant. + Information on WavPack is available at www.wavpack.com. Please consult + with the author of this algorithm for information on terms and + restrictions on use. + + 5.9.2 WavPack data for a file begins immediately after the end of the + local header data. This data is the output from WavPack compression + routines. Within the ZIP file, the use of WavPack compression is + indicated by setting the compression method field to a value of 97 + in both the local header and the central directory header. The Version + needed to extract and version made by fields use the same values as are + used for data compressed using the Deflate algorithm. + + 5.9.3 An implementation note for storing digital sample data when using + WavPack compression within ZIP files is that all of the bytes of + the sample data should be compressed. This includes any unused + bits up to the byte boundary. An example is a 2 byte sample that + uses only 12 bits for the sample data with 4 unused bits. If only + 12 bits are passed as the sample size to the WavPack routines, the 4 + unused bits will be set to 0 on extraction regardless of their original + state. To avoid this, the full 16 bits of the sample data size + should be provided. + +5.10 PPMd - Method 98 +--------------------- + + 5.10.1 PPMd is a data compression algorithm developed by Dmitry Shkarin + which includes a carryless rangecoder developed by Dmitry Subbotin. + This algorithm is based on predictive phrase matching on multiple + order contexts. Information and source code for this algorithm + can be found on the internet. Consult with the author of this + algorithm for information on terms or restrictions on use. + + 5.10.2 Support for PPMd within the ZIP format currently is provided only + for version I, revision 1 of the algorithm. Storage requirements + for using this algorithm are as follows: + + 5.10.3 Parameters needed to control the algorithm are stored in the two + bytes immediately preceding the compressed data. These bytes are + used to store the following fields: + + Model order - sets the maximum model order, default is 8, possible + values are from 2 to 16 inclusive + + Sub-allocator size - sets the size of sub-allocator in MB, default is 50, + possible values are from 1MB to 256MB inclusive + + Model restoration method - sets the method used to restart context + model at memory insufficiency, values are: + + 0 - restarts model from scratch - default + 1 - cut off model - decreases performance by as much as 2x + 2 - freeze context tree - not recommended + + 5.10.4 An example for packing these fields into the 2 byte storage field is + illustrated below. These values are stored in Intel low-byte/high-byte + order. + + wPPMd = (Model order - 1) + + ((Sub-allocator size - 1) << 4) + + (Model restoration method << 12) + + +6.0 Traditional PKWARE Encryption +---------------------------------- + + 6.0.1 The following information discusses the decryption steps + required to support traditional PKWARE encryption. This + form of encryption is considered weak by today's standards + and its use is recommended only for situations with + low security needs or for compatibility with older .ZIP + applications. + +6.1 Traditional PKWARE Decryption +--------------------------------- + + 6.1.1 PKWARE is grateful to Mr. Roger Schlafly for his expert + contribution towards the development of PKWARE's traditional + encryption. + + 6.1.2 PKZIP encrypts the compressed data stream. Encrypted files + must be decrypted before they can be extracted to their original + form. + + 6.1.3 Each encrypted file has an extra 12 bytes stored at the start + of the data area defining the encryption header for that file. The + encryption header is originally set to random values, and then + itself encrypted, using three, 32-bit keys. The key values are + initialized using the supplied encryption password. After each byte + is encrypted, the keys are then updated using pseudo-random number + generation techniques in combination with the same CRC-32 algorithm + used in PKZIP and described elsewhere in this document. + + 6.1.4 The following are the basic steps required to decrypt a file: + + 1) Initialize the three 32-bit keys with the password. + 2) Read and decrypt the 12-byte encryption header, further + initializing the encryption keys. + 3) Read and decrypt the compressed data stream using the + encryption keys. + + 6.1.5 Initializing the encryption keys + + Key(0) <- 305419896 + Key(1) <- 591751049 + Key(2) <- 878082192 + + loop for i <- 0 to length(password)-1 + update_keys(password(i)) + end loop + + Where update_keys() is defined as: + + update_keys(char): + Key(0) <- crc32(key(0),char) + Key(1) <- Key(1) + (Key(0) & 000000ffH) + Key(1) <- Key(1) * 134775813 + 1 + Key(2) <- crc32(key(2),key(1) >> 24) + end update_keys + + Where crc32(old_crc,char) is a routine that given a CRC value and a + character, returns an updated CRC value after applying the CRC-32 + algorithm described elsewhere in this document. + + 6.1.6 Decrypting the encryption header + + The purpose of this step is to further initialize the encryption + keys, based on random data, to render a plaintext attack on the + data ineffective. + + Read the 12-byte encryption header into Buffer, in locations + Buffer(0) thru Buffer(11). + + loop for i <- 0 to 11 + C <- buffer(i) ^ decrypt_byte() + update_keys(C) + buffer(i) <- C + end loop + + Where decrypt_byte() is defined as: + + unsigned char decrypt_byte() + local unsigned short temp + temp <- Key(2) | 2 + decrypt_byte <- (temp * (temp ^ 1)) >> 8 + end decrypt_byte + + After the header is decrypted, the last 1 or 2 bytes in Buffer + should be the high-order word/byte of the CRC for the file being + decrypted, stored in Intel low-byte/high-byte order. Versions of + PKZIP prior to 2.0 used a 2 byte CRC check; a 1 byte CRC check is + used on versions after 2.0. This can be used to test if the password + supplied is correct or not. + + 6.1.7 Decrypting the compressed data stream + + The compressed data stream can be decrypted as follows: + + loop until done + read a character into C + Temp <- C ^ decrypt_byte() + update_keys(temp) + output Temp + end loop + + +7.0 Strong Encryption Specification +----------------------------------- + + 7.0.1 Portions of the Strong Encryption technology defined in this + specification are covered under patents and pending patent applications. + Refer to the section in this document entitled "Incorporating + PKWARE Proprietary Technology into Your Product" for more information. + +7.1 Strong Encryption Overview +------------------------------ + + 7.1.1 Version 5.x of this specification introduced support for strong + encryption algorithms. These algorithms can be used with either + a password or an X.509v3 digital certificate to encrypt each file. + This format specification supports either password or certificate + based encryption to meet the security needs of today, to enable + interoperability between users within both PKI and non-PKI + environments, and to ensure interoperability between different + computing platforms that are running a ZIP program. + + 7.1.2 Password based encryption is the most common form of encryption + people are familiar with. However, inherent weaknesses with + passwords (e.g. susceptibility to dictionary/brute force attack) + as well as password management and support issues make certificate + based encryption a more secure and scalable option. Industry + efforts and support are defining and moving towards more advanced + security solutions built around X.509v3 digital certificates and + Public Key Infrastructures(PKI) because of the greater scalability, + administrative options, and more robust security over traditional + password based encryption. + + 7.1.3 Most standard encryption algorithms are supported with this + specification. Reference implementations for many of these + algorithms are available from either commercial or open source + distributors. Readily available cryptographic toolkits make + implementation of the encryption features straight-forward. + This document is not intended to provide a treatise on data + encryption principles or theory. Its purpose is to document the + data structures required for implementing interoperable data + encryption within the .ZIP format. It is strongly recommended that + you have a good understanding of data encryption before reading + further. + + 7.1.4 The algorithms introduced in Version 5.0 of this specification + include: + + RC2 40 bit, 64 bit, and 128 bit + RC4 40 bit, 64 bit, and 128 bit + DES + 3DES 112 bit and 168 bit + + Version 5.1 adds support for the following: + + AES 128 bit, 192 bit, and 256 bit + + + 7.1.5 Version 6.1 introduces encryption data changes to support + interoperability with Smartcard and USB Token certificate storage + methods which do not support the OAEP strengthening standard. + + 7.1.6 Version 6.2 introduces support for encrypting metadata by compressing + and encrypting the central directory data structure to reduce information + leakage. Information leakage can occur in legacy ZIP applications + through exposure of information about a file even though that file is + stored encrypted. The information exposed consists of file + characteristics stored within the records and fields defined by this + specification. This includes data such as a file's name, its original + size, timestamp and CRC32 value. + + 7.1.7 Version 6.3 introduces support for encrypting data using the Blowfish + and Twofish algorithms. These are symmetric block ciphers developed + by Bruce Schneier. Blowfish supports using a variable length key from + 32 to 448 bits. Block size is 64 bits. Implementations should use 16 + rounds and the only mode supported within ZIP files is CBC. Twofish + supports key sizes 128, 192 and 256 bits. Block size is 128 bits. + Implementations should use 16 rounds and the only mode supported within + ZIP files is CBC. Information and source code for both Blowfish and + Twofish algorithms can be found on the internet. Consult with the author + of these algorithms for information on terms or restrictions on use. + + 7.1.8 Central Directory Encryption provides greater protection against + information leakage by encrypting the Central Directory structure and + by masking key values that are replicated in the unencrypted Local + Header. ZIP compatible programs that cannot interpret an encrypted + Central Directory structure cannot rely on the data in the corresponding + Local Header for decompression information. + + 7.1.9 Extra Field records that may contain information about a file that should + not be exposed should not be stored in the Local Header and should only + be written to the Central Directory where they can be encrypted. This + design currently does not support streaming. Information in the End of + Central Directory record, the Zip64 End of Central Directory Locator, + and the Zip64 End of Central Directory records are not encrypted. Access + to view data on files within a ZIP file with an encrypted Central Directory + requires the appropriate password or private key for decryption prior to + viewing any files, or any information about the files, in the archive. + + 7.1.10 Older ZIP compatible programs not familiar with the Central Directory + Encryption feature will no longer be able to recognize the Central + Directory and may assume the ZIP file is corrupt. Programs that + attempt streaming access using Local Headers will see invalid + information for each file. Central Directory Encryption need not be + used for every ZIP file. Its use is recommended for greater security. + ZIP files not using Central Directory Encryption should operate as + in the past. + + 7.1.11 This strong encryption feature specification is intended to provide for + scalable, cross-platform encryption needs ranging from simple password + encryption to authenticated public/private key encryption. + + 7.1.12 Encryption provides data confidentiality and privacy. It is + recommended that you combine X.509 digital signing with encryption + to add authentication and non-repudiation. + + +7.2 Single Password Symmetric Encryption Method +----------------------------------------------- + + 7.2.1 The Single Password Symmetric Encryption Method using strong + encryption algorithms operates similarly to the traditional + PKWARE encryption defined in this format. Additional data + structures are added to support the processing needs of the + strong algorithms. + + The Strong Encryption data structures are: + + 7.2.2 General Purpose Bits - Bits 0 and 6 of the General Purpose bit + flag in both local and central header records. Both bits set + indicates strong encryption. Bit 13, when set indicates the Central + Directory is encrypted and that selected fields in the Local Header + are masked to hide their actual value. + + + 7.2.3 Extra Field 0x0017 in central header only. + + Fields to consider in this record are: + + 7.2.3.1 Format - the data format identifier for this record. The only + value allowed at this time is the integer value 2. + + 7.2.3.2 AlgId - integer identifier of the encryption algorithm from the + following range + + 0x6601 - DES + 0x6602 - RC2 (version needed to extract < 5.2) + 0x6603 - 3DES 168 + 0x6609 - 3DES 112 + 0x660E - AES 128 + 0x660F - AES 192 + 0x6610 - AES 256 + 0x6702 - RC2 (version needed to extract >= 5.2) + 0x6720 - Blowfish + 0x6721 - Twofish + 0x6801 - RC4 + 0xFFFF - Unknown algorithm + + 7.2.3.3 Bitlen - Explicit bit length of key + + 32 - 448 bits + + 7.2.3.4 Flags - Processing flags needed for decryption + + 0x0001 - Password is required to decrypt + 0x0002 - Certificates only + 0x0003 - Password or certificate required to decrypt + + Values > 0x0003 reserved for certificate processing + + + 7.2.4 Decryption header record preceding compressed file data. + + -Decryption Header: + + Value Size Description + ----- ---- ----------- + IVSize 2 bytes Size of initialization vector (IV) + IVData IVSize Initialization vector for this file + Size 4 bytes Size of remaining decryption header data + Format 2 bytes Format definition for this record + AlgID 2 bytes Encryption algorithm identifier + Bitlen 2 bytes Bit length of encryption key + Flags 2 bytes Processing flags + ErdSize 2 bytes Size of Encrypted Random Data + ErdData ErdSize Encrypted Random Data + Reserved1 4 bytes Reserved certificate processing data + Reserved2 (var) Reserved for certificate processing data + VSize 2 bytes Size of password validation data + VData VSize-4 Password validation data + VCRC32 4 bytes Standard ZIP CRC32 of password validation data + + 7.2.4.1 IVData - The size of the IV should match the algorithm block size. + The IVData can be completely random data. If the size of + the randomly generated data does not match the block size + it should be complemented with zero's or truncated as + necessary. If IVSize is 0,then IV = CRC32 + Uncompressed + File Size (as a 64 bit little-endian, unsigned integer value). + + 7.2.4.2 Format - the data format identifier for this record. The only + value allowed at this time is the integer value 3. + + 7.2.4.3 AlgId - integer identifier of the encryption algorithm from the + following range + + 0x6601 - DES + 0x6602 - RC2 (version needed to extract < 5.2) + 0x6603 - 3DES 168 + 0x6609 - 3DES 112 + 0x660E - AES 128 + 0x660F - AES 192 + 0x6610 - AES 256 + 0x6702 - RC2 (version needed to extract >= 5.2) + 0x6720 - Blowfish + 0x6721 - Twofish + 0x6801 - RC4 + 0xFFFF - Unknown algorithm + + 7.2.4.4 Bitlen - Explicit bit length of key + + 32 - 448 bits + + 7.2.4.5 Flags - Processing flags needed for decryption + + 0x0001 - Password is required to decrypt + 0x0002 - Certificates only + 0x0003 - Password or certificate required to decrypt + + Values > 0x0003 reserved for certificate processing + + 7.2.4.6 ErdData - Encrypted random data is used to store random data that + is used to generate a file session key for encrypting + each file. SHA1 is used to calculate hash data used to + derive keys. File session keys are derived from a master + session key generated from the user-supplied password. + If the Flags field in the decryption header contains + the value 0x4000, then the ErdData field must be + decrypted using 3DES. If the value 0x4000 is not set, + then the ErdData field must be decrypted using AlgId. + + + 7.2.4.7 Reserved1 - Reserved for certificate processing, if value is + zero, then Reserved2 data is absent. See the explanation + under the Certificate Processing Method for details on + this data structure. + + 7.2.4.8 Reserved2 - If present, the size of the Reserved2 data structure + is located by skipping the first 4 bytes of this field + and using the next 2 bytes as the remaining size. See + the explanation under the Certificate Processing Method + for details on this data structure. + + 7.2.4.9 VSize - This size value will always include the 4 bytes of the + VCRC32 data and will be greater than 4 bytes. + + 7.2.4.10 VData - Random data for password validation. This data is VSize + in length and VSize must be a multiple of the encryption + block size. VCRC32 is a checksum value of VData. + VData and VCRC32 are stored encrypted and start the + stream of encrypted data for a file. + + + 7.2.5 Useful Tips + + 7.2.5.1 Strong Encryption is always applied to a file after compression. The + block oriented algorithms all operate in Cypher Block Chaining (CBC) + mode. The block size used for AES encryption is 16. All other block + algorithms use a block size of 8. Two IDs are defined for RC2 to + account for a discrepancy found in the implementation of the RC2 + algorithm in the cryptographic library on Windows XP SP1 and all + earlier versions of Windows. It is recommended that zero length files + not be encrypted, however programs should be prepared to extract them + if they are found within a ZIP file. + + 7.2.5.2 A pseudo-code representation of the encryption process is as follows: + + Password = GetUserPassword() + MasterSessionKey = DeriveKey(SHA1(Password)) + RD = CryptographicStrengthRandomData() + For Each File + IV = CryptographicStrengthRandomData() + VData = CryptographicStrengthRandomData() + VCRC32 = CRC32(VData) + FileSessionKey = DeriveKey(SHA1(IV + RD) + ErdData = Encrypt(RD,MasterSessionKey,IV) + Encrypt(VData + VCRC32 + FileData, FileSessionKey,IV) + Done + + 7.2.5.3 The function names and parameter requirements will depend on + the choice of the cryptographic toolkit selected. Almost any + toolkit supporting the reference implementations for each + algorithm can be used. The RSA BSAFE(r), OpenSSL, and Microsoft + CryptoAPI libraries are all known to work well. + + + 7.3 Single Password - Central Directory Encryption + -------------------------------------------------- + + 7.3.1 Central Directory Encryption is achieved within the .ZIP format by + encrypting the Central Directory structure. This encapsulates the metadata + most often used for processing .ZIP files. Additional metadata is stored for + redundancy in the Local Header for each file. The process of concealing + metadata by encrypting the Central Directory does not protect the data within + the Local Header. To avoid information leakage from the exposed metadata + in the Local Header, the fields containing information about a file are masked. + + 7.3.2 Local Header + + Masking replaces the true content of the fields for a file in the Local + Header with false information. When masked, the Local Header is not + suitable for streaming access and the options for data recovery of damaged + archives is reduced. Extra Data fields that may contain confidential + data should not be stored within the Local Header. The value set into + the Version needed to extract field should be the correct value needed to + extract the file without regard to Central Directory Encryption. The fields + within the Local Header targeted for masking when the Central Directory is + encrypted are: + + Field Name Mask Value + ------------------ --------------------------- + compression method 0 + last mod file time 0 + last mod file date 0 + crc-32 0 + compressed size 0 + uncompressed size 0 + file name (variable size) Base 16 value from the + range 1 - 0xFFFFFFFFFFFFFFFF + represented as a string whose + size will be set into the + file name length field + + The Base 16 value assigned as a masked file name is simply a sequentially + incremented value for each file starting with 1 for the first file. + Modifications to a ZIP file may cause different values to be stored for + each file. For compatibility, the file name field in the Local Header + should never be left blank. As of Version 6.2 of this specification, + the Compression Method and Compressed Size fields are not yet masked. + Fields having a value of 0xFFFF or 0xFFFFFFFF for the ZIP64 format + should not be masked. + + 7.3.3 Encrypting the Central Directory + + Encryption of the Central Directory does not include encryption of the + Central Directory Signature data, the Zip64 End of Central Directory + record, the Zip64 End of Central Directory Locator, or the End + of Central Directory record. The ZIP file comment data is never + encrypted. + + Before encrypting the Central Directory, it may optionally be compressed. + Compression is not required, but for storage efficiency it is assumed + this structure will be compressed before encrypting. Similarly, this + specification supports compressing the Central Directory without + requiring that it also be encrypted. Early implementations of this + feature will assume the encryption method applied to files matches the + encryption applied to the Central Directory. + + Encryption of the Central Directory is done in a manner similar to + that of file encryption. The encrypted data is preceded by a + decryption header. The decryption header is known as the Archive + Decryption Header. The fields of this record are identical to + the decryption header preceding each encrypted file. The location + of the Archive Decryption Header is determined by the value in the + Start of the Central Directory field in the Zip64 End of Central + Directory record. When the Central Directory is encrypted, the + Zip64 End of Central Directory record will always be present. + + The layout of the Zip64 End of Central Directory record for all + versions starting with 6.2 of this specification will follow the + Version 2 format. The Version 2 format is as follows: + + The leading fixed size fields within the Version 1 format for this + record remain unchanged. The record signature for both Version 1 + and Version 2 will be 0x06064b50. Immediately following the last + byte of the field known as the Offset of Start of Central + Directory With Respect to the Starting Disk Number will begin the + new fields defining Version 2 of this record. + + 7.3.4 New fields for Version 2 + + Note: all fields stored in Intel low-byte/high-byte order. + + Value Size Description + ----- ---- ----------- + Compression Method 2 bytes Method used to compress the + Central Directory + Compressed Size 8 bytes Size of the compressed data + Original Size 8 bytes Original uncompressed size + AlgId 2 bytes Encryption algorithm ID + BitLen 2 bytes Encryption key length + Flags 2 bytes Encryption flags + HashID 2 bytes Hash algorithm identifier + Hash Length 2 bytes Length of hash data + Hash Data (variable) Hash data + + The Compression Method accepts the same range of values as the + corresponding field in the Central Header. + + The Compressed Size and Original Size values will not include the + data of the Central Directory Signature which is compressed or + encrypted. + + The AlgId, BitLen, and Flags fields accept the same range of values + the corresponding fields within the 0x0017 record. + + Hash ID identifies the algorithm used to hash the Central Directory + data. This data does not have to be hashed, in which case the + values for both the HashID and Hash Length will be 0. Possible + values for HashID are: + + Value Algorithm + ------ --------- + 0x0000 none + 0x0001 CRC32 + 0x8003 MD5 + 0x8004 SHA1 + 0x8007 RIPEMD160 + 0x800C SHA256 + 0x800D SHA384 + 0x800E SHA512 + + 7.3.5 When the Central Directory data is signed, the same hash algorithm + used to hash the Central Directory for signing should be used. + This is recommended for processing efficiency, however, it is + permissible for any of the above algorithms to be used independent + of the signing process. + + The Hash Data will contain the hash data for the Central Directory. + The length of this data will vary depending on the algorithm used. + + The Version Needed to Extract should be set to 62. + + The value for the Total Number of Entries on the Current Disk will + be 0. These records will no longer support random access when + encrypting the Central Directory. + + 7.3.6 When the Central Directory is compressed and/or encrypted, the + End of Central Directory record will store the value 0xFFFFFFFF + as the value for the Total Number of Entries in the Central + Directory. The value stored in the Total Number of Entries in + the Central Directory on this Disk field will be 0. The actual + values will be stored in the equivalent fields of the Zip64 + End of Central Directory record. + + 7.3.7 Decrypting and decompressing the Central Directory is accomplished + in the same manner as decrypting and decompressing a file. + + 7.4 Certificate Processing Method + --------------------------------- + + The Certificate Processing Method for ZIP file encryption + defines the following additional data fields: + + 7.4.1 Certificate Flag Values + + Additional processing flags that can be present in the Flags field of both + the 0x0017 field of the central directory Extra Field and the Decryption + header record preceding compressed file data are: + + 0x0007 - reserved for future use + 0x000F - reserved for future use + 0x0100 - Indicates non-OAEP key wrapping was used. If this + this field is set, the version needed to extract must + be at least 61. This means OAEP key wrapping is not + used when generating a Master Session Key using + ErdData. + 0x4000 - ErdData must be decrypted using 3DES-168, otherwise use the + same algorithm used for encrypting the file contents. + 0x8000 - reserved for future use + + + 7.4.2 CertData - Extra Field 0x0017 record certificate data structure + + The data structure used to store certificate data within the section + of the Extra Field defined by the CertData field of the 0x0017 + record are as shown: + + Value Size Description + ----- ---- ----------- + RCount 4 bytes Number of recipients. + HashAlg 2 bytes Hash algorithm identifier + HSize 2 bytes Hash size + SRList (var) Simple list of recipients hashed public keys + + + RCount This defines the number intended recipients whose + public keys were used for encryption. This identifies + the number of elements in the SRList. + + HashAlg This defines the hash algorithm used to calculate + the public key hash of each public key used + for encryption. This field currently supports + only the following value for SHA-1 + + 0x8004 - SHA1 + + HSize This defines the size of a hashed public key. + + SRList This is a variable length list of the hashed + public keys for each intended recipient. Each + element in this list is HSize. The total size of + SRList is determined using RCount * HSize. + + + 7.4.3 Reserved1 - Certificate Decryption Header Reserved1 Data + + Value Size Description + ----- ---- ----------- + RCount 4 bytes Number of recipients. + + RCount This defines the number intended recipients whose + public keys were used for encryption. This defines + the number of elements in the REList field defined below. + + + 7.4.4 Reserved2 - Certificate Decryption Header Reserved2 Data Structures + + + Value Size Description + ----- ---- ----------- + HashAlg 2 bytes Hash algorithm identifier + HSize 2 bytes Hash size + REList (var) List of recipient data elements + + + HashAlg This defines the hash algorithm used to calculate + the public key hash of each public key used + for encryption. This field currently supports + only the following value for SHA-1 + + 0x8004 - SHA1 + + HSize This defines the size of a hashed public key + defined in REHData. + + REList This is a variable length of list of recipient data. + Each element in this list consists of a Recipient + Element data structure as follows: + + + Recipient Element (REList) Data Structure: + + Value Size Description + ----- ---- ----------- + RESize 2 bytes Size of REHData + REKData + REHData HSize Hash of recipients public key + REKData (var) Simple key blob + + + RESize This defines the size of an individual REList + element. This value is the combined size of the + REHData field + REKData field. REHData is defined by + HSize. REKData is variable and can be calculated + for each REList element using RESize and HSize. + + REHData Hashed public key for this recipient. + + REKData Simple Key Blob. The format of this data structure + is identical to that defined in the Microsoft + CryptoAPI and generated using the CryptExportKey() + function. The version of the Simple Key Blob + supported at this time is 0x02 as defined by + Microsoft. + +7.5 Certificate Processing - Central Directory Encryption +--------------------------------------------------------- + + 7.5.1 Central Directory Encryption using Digital Certificates will + operate in a manner similar to that of Single Password Central + Directory Encryption. This record will only be present when there + is data to place into it. Currently, data is placed into this + record when digital certificates are used for either encrypting + or signing the files within a ZIP file. When only password + encryption is used with no certificate encryption or digital + signing, this record is not currently needed. When present, this + record will appear before the start of the actual Central Directory + data structure and will be located immediately after the Archive + Decryption Header if the Central Directory is encrypted. + + 7.5.2 The Archive Extra Data record will be used to store the following + information. Additional data may be added in future versions. + + Extra Data Fields: + + 0x0014 - PKCS#7 Store for X.509 Certificates + 0x0016 - X.509 Certificate ID and Signature for central directory + 0x0019 - PKCS#7 Encryption Recipient Certificate List + + The 0x0014 and 0x0016 Extra Data records that otherwise would be + located in the first record of the Central Directory for digital + certificate processing. When encrypting or compressing the Central + Directory, the 0x0014 and 0x0016 records must be located in the + Archive Extra Data record and they should not remain in the first + Central Directory record. The Archive Extra Data record will also + be used to store the 0x0019 data. + + 7.5.3 When present, the size of the Archive Extra Data record will be + included in the size of the Central Directory. The data of the + Archive Extra Data record will also be compressed and encrypted + along with the Central Directory data structure. + +7.6 Certificate Processing Differences +-------------------------------------- + + 7.6.1 The Certificate Processing Method of encryption differs from the + Single Password Symmetric Encryption Method as follows. Instead + of using a user-defined password to generate a master session key, + cryptographically random data is used. The key material is then + wrapped using standard key-wrapping techniques. This key material + is wrapped using the public key of each recipient that will need + to decrypt the file using their corresponding private key. + + 7.6.2 This specification currently assumes digital certificates will follow + the X.509 V3 format for 1024 bit and higher RSA format digital + certificates. Implementation of this Certificate Processing Method + requires supporting logic for key access and management. This logic + is outside the scope of this specification. + +7.7 OAEP Processing with Certificate-based Encryption +----------------------------------------------------- + + 7.7.1 OAEP stands for Optimal Asymmetric Encryption Padding. It is a + strengthening technique used for small encoded items such as decryption + keys. This is commonly applied in cryptographic key-wrapping techniques + and is supported by PKCS #1. Versions 5.0 and 6.0 of this specification + were designed to support OAEP key-wrapping for certificate-based + decryption keys for additional security. + + 7.7.2 Support for private keys stored on Smartcards or Tokens introduced + a conflict with this OAEP logic. Most card and token products do + not support the additional strengthening applied to OAEP key-wrapped + data. In order to resolve this conflict, versions 6.1 and above of this + specification will no longer support OAEP when encrypting using + digital certificates. + + 7.7.3 Versions of PKZIP available during initial development of the + certificate processing method set a value of 61 into the + version needed to extract field for a file. This indicates that + non-OAEP key wrapping is used. This affects certificate encryption + only, and password encryption functions should not be affected by + this value. This means values of 61 may be found on files encrypted + with certificates only, or on files encrypted with both password + encryption and certificate encryption. Files encrypted with both + methods can safely be decrypted using the password methods documented. + +8.0 Splitting and Spanning ZIP files +------------------------------------- + + 8.1 Spanned ZIP files + + 8.1.1 Spanning is the process of segmenting a ZIP file across + multiple removable media. This support has typically only + been provided for DOS formatted floppy diskettes. + + 8.2 Split ZIP files + + 8.2.1 File splitting is a newer derivation of spanning. + Splitting follows the same segmentation process as + spanning, however, it does not require writing each + segment to a unique removable medium and instead supports + placing all pieces onto local or non-removable locations + such as file systems, local drives, folders, etc. + + 8.3 File Naming Differences + + 8.3.1 A key difference between spanned and split ZIP files is + that all pieces of a spanned ZIP file have the same name. + Since each piece is written to a separate volume, no name + collisions occur and each segment can reuse the original + .ZIP file name given to the archive. + + 8.3.2 Sequence ordering for DOS spanned archives uses the DOS + volume label to determine segment numbers. Volume labels + for each segment are written using the form PKBACK#xxx, + where xxx is the segment number written as a decimal + value from 001 - nnn. + + 8.3.3 Split ZIP files are typically written to the same location + and are subject to name collisions if the spanned name + format is used since each segment will reside on the same + drive. To avoid name collisions, split archives are named + as follows. + + Segment 1 = filename.z01 + Segment n-1 = filename.z(n-1) + Segment n = filename.zip + + 8.3.4 The .ZIP extension is used on the last segment to support + quickly reading the central directory. The segment number + n should be a decimal value. + + 8.4 Spanned Self-extracting ZIP Files + + 8.4.1 Spanned ZIP files may be PKSFX Self-extracting ZIP files. + PKSFX files may also be split, however, in this case + the first segment must be named filename.exe. The first + segment of a split PKSFX archive must be large enough to + include the entire executable program. + + 8.5 Capacities and Markers + + 8.5.1 Capacities for split archives are as follows: + + Maximum number of segments = 4,294,967,295 - 1 + Maximum .ZIP segment size = 4,294,967,295 bytes + Minimum segment size = 64K + Maximum PKSFX segment size = 2,147,483,647 bytes + + 8.5.2 Segment sizes may be different however by convention, all + segment sizes should be the same with the exception of the + last, which may be smaller. Local and central directory + header records must never be split across a segment boundary. + When writing a header record, if the number of bytes remaining + within a segment is less than the size of the header record, + end the current segment and write the header at the start + of the next segment. The central directory may span segment + boundaries, but no single record in the central directory + should be split across segments. + + 8.5.3 Spanned/Split archives created using PKZIP for Windows + (V2.50 or greater), PKZIP Command Line (V2.50 or greater), + or PKZIP Explorer will include a special spanning + signature as the first 4 bytes of the first segment of + the archive. This signature (0x08074b50) will be + followed immediately by the local header signature for + the first file in the archive. + + 8.5.4 A special spanning marker may also appear in spanned/split + archives if the spanning or splitting process starts but + only requires one segment. In this case the 0x08074b50 + signature will be replaced with the temporary spanning + marker signature of 0x30304b50. Split archives can + only be uncompressed by other versions of PKZIP that + know how to create a split archive. + + 8.5.5 The signature value 0x08074b50 is also used by some + ZIP implementations as a marker for the Data Descriptor + record. Conflict in this alternate assignment can be + avoided by ensuring the position of the signature + within the ZIP file to determine the use for which it + is intended. + +9.0 Change Process +------------------ + + 9.1 In order for the .ZIP file format to remain a viable technology, this + specification should be considered as open for periodic review and + revision. Although this format was originally designed with a + certain level of extensibility, not all changes in technology + (present or future) were or will be necessarily considered in its + design. + + 9.2 If your application requires new definitions to the + extensible sections in this format, or if you would like to + submit new data structures or new capabilities, please forward + your request to zipformat@pkware.com. All submissions will be + reviewed by the ZIP File Specification Committee for possible + inclusion into future versions of this specification. + + 9.3 Periodic revisions to this specification will be published as + DRAFT or as FINAL status to ensure interoperability. We encourage + comments and feedback that may help improve clarity or content. + + +10.0 Incorporating PKWARE Proprietary Technology into Your Product +------------------------------------------------------------------ + + 10.1 The Use or Implementation in a product of APPNOTE technological + components pertaining to either strong encryption or patching requires + a separate, executed license agreement from PKWARE. Please contact + PKWARE at zipformat@pkware.com or +1-414-289-9788 with regard to + acquiring such a license. + + 10.2 Additional information regarding PKWARE proprietray technology is + available at http://www.pkware.com/appnote. + +11.0 Acknowledgements +--------------------- + + In addition to the above mentioned contributors to PKZIP and PKUNZIP, + PKWARE would like to extend special thanks to Robert Mahoney for + suggesting the extension .ZIP for this software. + +12.0 References +--------------- + + Fiala, Edward R., and Greene, Daniel H., "Data compression with + finite windows", Communications of the ACM, Volume 32, Number 4, + April 1989, pages 490-505. + + Held, Gilbert, "Data Compression, Techniques and Applications, + Hardware and Software Considerations", John Wiley & Sons, 1987. + + Huffman, D.A., "A method for the construction of minimum-redundancy + codes", Proceedings of the IRE, Volume 40, Number 9, September 1952, + pages 1098-1101. + + Nelson, Mark, "LZW Data Compression", Dr. Dobbs Journal, Volume 14, + Number 10, October 1989, pages 29-37. + + Nelson, Mark, "The Data Compression Book", M&T Books, 1991. + + Storer, James A., "Data Compression, Methods and Theory", + Computer Science Press, 1988 + + Welch, Terry, "A Technique for High-Performance Data Compression", + IEEE Computer, Volume 17, Number 6, June 1984, pages 8-19. + + Ziv, J. and Lempel, A., "A universal algorithm for sequential data + compression", Communications of the ACM, Volume 30, Number 6, + June 1987, pages 520-540. + + Ziv, J. and Lempel, A., "Compression of individual sequences via + variable-rate coding", IEEE Transactions on Information Theory, + Volume 24, Number 5, September 1978, pages 530-536. + + +APPENDIX A - AS/400 Extra Field (0x0065) Attribute Definitions +-------------------------------------------------------------- + +A.1 Field Definition Structure: + + a. field length including length 2 bytes + b. field code 2 bytes + c. data x bytes + +A.2 Field Code Description + + 4001 Source type i.e. CLP etc + 4002 The text description of the library + 4003 The text description of the file + 4004 The text description of the member + 4005 x'F0' or 0 is PF-DTA, x'F1' or 1 is PF_SRC + 4007 Database Type Code 1 byte + 4008 Database file and fields definition + 4009 GZIP file type 2 bytes + 400B IFS code page 2 bytes + 400C IFS Creation Time 4 bytes + 400D IFS Access Time 4 bytes + 400E IFS Modification time 4 bytes + 005C Length of the records in the file 2 bytes + 0068 GZIP two words 8 bytes + +APPENDIX B - z/OS Extra Field (0x0065) Attribute Definitions +------------------------------------------------------------ + +B.1 Field Definition Structure: + + a. field length including length 2 bytes + b. field code 2 bytes + c. data x bytes + +B.2 Field Code Description + + 0001 File Type 2 bytes + 0002 NonVSAM Record Format 1 byte + 0003 Reserved + 0004 NonVSAM Block Size 2 bytes Big Endian + 0005 Primary Space Allocation 3 bytes Big Endian + 0006 Secondary Space Allocation 3 bytes Big Endian + 0007 Space Allocation Type1 byte flag + 0008 Modification Date Retired with PKZIP 5.0 + + 0009 Expiration Date Retired with PKZIP 5.0 + + 000A PDS Directory Block Allocation 3 bytes Big Endian binary value + 000B NonVSAM Volume List variable + 000C UNIT Reference Retired with PKZIP 5.0 + + 000D DF/SMS Management Class 8 bytes EBCDIC Text Value + 000E DF/SMS Storage Class 8 bytes EBCDIC Text Value + 000F DF/SMS Data Class 8 bytes EBCDIC Text Value + 0010 PDS/PDSE Member Info. 30 bytes + 0011 VSAM sub-filetype 2 bytes + 0012 VSAM LRECL 13 bytes EBCDIC "(num_avg num_max)" + 0013 VSAM Cluster Name Retired with PKZIP 5.0 + + 0014 VSAM KSDS Key Information 13 bytes EBCDIC "(num_length num_position)" + 0015 VSAM Average LRECL 5 bytes EBCDIC num_value padded with blanks + 0016 VSAM Maximum LRECL 5 bytes EBCDIC num_value padded with blanks + 0017 VSAM KSDS Key Length 5 bytes EBCDIC num_value padded with blanks + 0018 VSAM KSDS Key Position 5 bytes EBCDIC num_value padded with blanks + 0019 VSAM Data Name 1-44 bytes EBCDIC text string + 001A VSAM KSDS Index Name 1-44 bytes EBCDIC text string + 001B VSAM Catalog Name 1-44 bytes EBCDIC text string + 001C VSAM Data Space Type 9 bytes EBCDIC text string + 001D VSAM Data Space Primary 9 bytes EBCDIC num_value left-justified + 001E VSAM Data Space Secondary 9 bytes EBCDIC num_value left-justified + 001F VSAM Data Volume List variable EBCDIC text list of 6-character Volume IDs + 0020 VSAM Data Buffer Space 8 bytes EBCDIC num_value left-justified + 0021 VSAM Data CISIZE 5 bytes EBCDIC num_value left-justified + 0022 VSAM Erase Flag 1 byte flag + 0023 VSAM Free CI % 3 bytes EBCDIC num_value left-justified + 0024 VSAM Free CA % 3 bytes EBCDIC num_value left-justified + 0025 VSAM Index Volume List variable EBCDIC text list of 6-character Volume IDs + 0026 VSAM Ordered Flag 1 byte flag + 0027 VSAM REUSE Flag 1 byte flag + 0028 VSAM SPANNED Flag 1 byte flag + 0029 VSAM Recovery Flag 1 byte flag + 002A VSAM WRITECHK Flag 1 byte flag + 002B VSAM Cluster/Data SHROPTS 3 bytes EBCDIC "n,y" + 002C VSAM Index SHROPTS 3 bytes EBCDIC "n,y" + 002D VSAM Index Space Type 9 bytes EBCDIC text string + 002E VSAM Index Space Primary 9 bytes EBCDIC num_value left-justified + 002F VSAM Index Space Secondary 9 bytes EBCDIC num_value left-justified + 0030 VSAM Index CISIZE 5 bytes EBCDIC num_value left-justified + 0031 VSAM Index IMBED 1 byte flag + 0032 VSAM Index Ordered Flag 1 byte flag + 0033 VSAM REPLICATE Flag 1 byte flag + 0034 VSAM Index REUSE Flag 1 byte flag + 0035 VSAM Index WRITECHK Flag 1 byte flag Retired with PKZIP 5.0 + + 0036 VSAM Owner 8 bytes EBCDIC text string + 0037 VSAM Index Owner 8 bytes EBCDIC text string + 0038 Reserved + 0039 Reserved + 003A Reserved + 003B Reserved + 003C Reserved + 003D Reserved + 003E Reserved + 003F Reserved + 0040 Reserved + 0041 Reserved + 0042 Reserved + 0043 Reserved + 0044 Reserved + 0045 Reserved + 0046 Reserved + 0047 Reserved + 0048 Reserved + 0049 Reserved + 004A Reserved + 004B Reserved + 004C Reserved + 004D Reserved + 004E Reserved + 004F Reserved + 0050 Reserved + 0051 Reserved + 0052 Reserved + 0053 Reserved + 0054 Reserved + 0055 Reserved + 0056 Reserved + 0057 Reserved + 0058 PDS/PDSE Member TTR Info. 6 bytes Big Endian + 0059 PDS 1st LMOD Text TTR 3 bytes Big Endian + 005A PDS LMOD EP Rec # 4 bytes Big Endian + 005B Reserved + 005C Max Length of records 2 bytes Big Endian + 005D PDSE Flag 1 byte flag + 005E Reserved + 005F Reserved + 0060 Reserved + 0061 Reserved + 0062 Reserved + 0063 Reserved + 0064 Reserved + 0065 Last Date Referenced 4 bytes Packed Hex "yyyymmdd" + 0066 Date Created 4 bytes Packed Hex "yyyymmdd" + 0068 GZIP two words 8 bytes + 0071 Extended NOTE Location 12 bytes Big Endian + 0072 Archive device UNIT 6 bytes EBCDIC + 0073 Archive 1st Volume 6 bytes EBCDIC + 0074 Archive 1st VOL File Seq# 2 bytes Binary + +APPENDIX C - Zip64 Extensible Data Sector Mappings +--------------------------------------------------- + + -Z390 Extra Field: + + The following is the general layout of the attributes for the + ZIP 64 "extra" block for extended tape operations. + + Note: some fields stored in Big Endian format. All text is + in EBCDIC format unless otherwise specified. + + Value Size Description + ----- ---- ----------- + (Z390) 0x0065 2 bytes Tag for this "extra" block type + Size 4 bytes Size for the following data block + Tag 4 bytes EBCDIC "Z390" + Length71 2 bytes Big Endian + Subcode71 2 bytes Enote type code + FMEPos 1 byte + Length72 2 bytes Big Endian + Subcode72 2 bytes Unit type code + Unit 1 byte Unit + Length73 2 bytes Big Endian + Subcode73 2 bytes Volume1 type code + FirstVol 1 byte Volume + Length74 2 bytes Big Endian + Subcode74 2 bytes FirstVol file sequence + FileSeq 2 bytes Sequence + +APPENDIX D - Language Encoding (EFS) +------------------------------------ + +D.1 The ZIP format has historically supported only the original IBM PC character +encoding set, commonly referred to as IBM Code Page 437. This limits storing +file name characters to only those within the original MS-DOS range of values +and does not properly support file names in other character encodings, or +languages. To address this limitation, this specification will support the +following change. + +D.2 If general purpose bit 11 is unset, the file name and comment should conform +to the original ZIP character encoding. If general purpose bit 11 is set, the +filename and comment must support The Unicode Standard, Version 4.1.0 or +greater using the character encoding form defined by the UTF-8 storage +specification. The Unicode Standard is published by the The Unicode +Consortium (www.unicode.org). UTF-8 encoded data stored within ZIP files +is expected to not include a byte order mark (BOM). + +D.3 Applications may choose to supplement this file name storage through the use +of the 0x0008 Extra Field. Storage for this optional field is currently +undefined, however it will be used to allow storing extended information +on source or target encoding that may further assist applications with file +name, or file content encoding tasks. Please contact PKWARE with any +requirements on how this field should be used. + +D.4 The 0x0008 Extra Field storage may be used with either setting for general +purpose bit 11. Examples of the intended usage for this field is to store +whether "modified-UTF-8" (JAVA) is used, or UTF-8-MAC. Similarly, other +commonly used character encoding (code page) designations can be indicated +through this field. Formalized values for use of the 0x0008 record remain +undefined at this time. The definition for the layout of the 0x0008 field +will be published when available. Use of the 0x0008 Extra Field provides +for storing data within a ZIP file in an encoding other than IBM Code +Page 437 or UTF-8. + +D.5 General purpose bit 11 will not imply any encoding of file content or +password. Values defining character encoding for file content or +password must be stored within the 0x0008 Extended Language Encoding +Extra Field. + +D.6 Ed Gordon of the Info-ZIP group has defined a pair of "extra field" records +that can be used to store UTF-8 file name and file comment fields. These +records can be used for cases when the general purpose bit 11 method +for storing UTF-8 data in the standard file name and comment fields is +not desirable. A common case for this alternate method is if backward +compatibility with older programs is required. + +D.7 Definitions for the record structure of these fields are included above +in the section on 3rd party mappings for "extra field" records. These +records are identified by Header ID's 0x6375 (Info-ZIP Unicode Comment +Extra Field) and 0x7075 (Info-ZIP Unicode Path Extra Field). + +D.8 The choice of which storage method to use when writing a ZIP file is left +to the implementation. Developers should expect that a ZIP file may +contain either method and should provide support for reading data in +either format. Use of general purpose bit 11 reduces storage requirements +for file name data by not requiring additional "extra field" data for +each file, but can result in older ZIP programs not being able to extract +files. Use of the 0x6375 and 0x7075 records will result in a ZIP file +that should always be readable by older ZIP programs, but requires more +storage per file to write file name and/or file comment fields. diff --git a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs index 5e32020d..c4002e22 100644 --- a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs @@ -34,16 +34,16 @@ namespace SharpCompress.Common.Zip.Headers byte[] extra = reader.ReadBytes(extraLength); byte[] comment = reader.ReadBytes(commentLength); - if ((Flags & HeaderFlags.UTF8) == 0) - { - Name = ArchiveEncoding.Decode437(name); - Comment = ArchiveEncoding.Decode437(comment); - } - else + if (Flags.HasFlag(HeaderFlags.EFS)) { Name = ArchiveEncoding.Decode(name); Comment = ArchiveEncoding.Decode(comment); } + else + { + Name = ArchiveEncoding.Decode437(name); + Comment = ArchiveEncoding.Decode437(comment); + } LoadExtra(extra); diff --git a/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs b/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs index 6e35191c..b5f363d7 100644 --- a/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs +++ b/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs @@ -11,6 +11,7 @@ namespace SharpCompress.Common.Zip.Headers Bit2 = 4, UsePostDataDescriptor = 8, EnhancedDeflate = 16, - UTF8 = 2048 + //Bit 11: Language encoding flag + EFS = 2048 } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs index 582d04f7..4b9e8178 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs @@ -25,14 +25,15 @@ namespace SharpCompress.Common.Zip.Headers ushort extraLength = reader.ReadUInt16(); byte[] name = reader.ReadBytes(nameLength); byte[] extra = reader.ReadBytes(extraLength); - if ((Flags & HeaderFlags.UTF8) == 0) + + if (Flags.HasFlag(HeaderFlags.EFS)) { - // Use IBM Code Page 437 (IBM PC character encoding set) - Name = ArchiveEncoding.Decode437(name); + Name = ArchiveEncoding.Decode(name); } else { - Name = ArchiveEncoding.Decode(name); + // Use IBM Code Page 437 (IBM PC character encoding set) + Name = ArchiveEncoding.Decode437(name); } LoadExtra(extra); diff --git a/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs b/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs index f4237e91..0a388651 100644 --- a/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs +++ b/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs @@ -44,7 +44,7 @@ namespace SharpCompress.Writers.Zip var extralength = zip64 ? (2 + 2 + 8 + 8 + 8 + 4) : 0; var version = (byte)(zip64 ? 45 : 20); // Version 20 required for deflate/encryption - HeaderFlags flags = Equals(archiveEncoding.GetEncoding(), Encoding.UTF8) ? HeaderFlags.UTF8 : HeaderFlags.None; + HeaderFlags flags = Equals(archiveEncoding.GetEncoding(), Encoding.UTF8) ? HeaderFlags.EFS : HeaderFlags.None; if (!outputStream.CanSeek) { // Cannot use data descriptors with zip64: diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index 3a29ac63..f487af43 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -169,7 +169,7 @@ namespace SharpCompress.Writers.Zip { OutputStream.Write(new byte[] { 63, 0 }, 0, 2); //version says we used PPMd or LZMA } - HeaderFlags flags = Equals(WriterOptions.ArchiveEncoding.GetEncoding(), Encoding.UTF8) ? HeaderFlags.UTF8 : 0; + HeaderFlags flags = Equals(WriterOptions.ArchiveEncoding.GetEncoding(), Encoding.UTF8) ? HeaderFlags.EFS : 0; if (!OutputStream.CanSeek) { flags |= HeaderFlags.UsePostDataDescriptor; From 80ceb1c375fdb1b4ffba16528c99089e804ce61f Mon Sep 17 00:00:00 2001 From: odinn1986 Date: Wed, 2 May 2018 22:46:01 +0300 Subject: [PATCH 104/124] fix: prevent extracting archived files outside of target path This PR is meant to fix an arbitrary file write vulnerability, that can be achieved using a specially crafted zip archive, that holds path traversal filenames. When the filename gets concatenated to the target extraction directory, the final path ends up outside of the target folder. A sample malicious zip file named Zip.Evil.zip was used, and when running the code below, resulted in the creation of C:/Temp/evil.txt outside of the intended target directory. There are various possible ways to avoid this issue, some include checking for .. (dot dot) characters in the filename, but the best solution in our opinion is to check if the final target filename, starts with the target folder (after both are resolved to their absolute path). Stay secure, Snyk Team --- .../Archives/IArchiveEntryExtensions.cs | 21 ++++++++++++-- .../SharpCompress.Test/Zip/ZipArchiveTests.cs | 27 ++++++++++++++++++ tests/TestArchives/Archives/Zip.Evil.zip | Bin 0 -> 547 bytes 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/TestArchives/Archives/Zip.Evil.zip diff --git a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs index f4f8cb6b..d66669b9 100644 --- a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs @@ -48,6 +48,7 @@ namespace SharpCompress.Archives { string destinationFileName; string file = Path.GetFileName(entry.Key); + string fullDestinationDirectoryPath = Path.GetFullPath(destinationDirectory); options = options ?? new ExtractionOptions() { @@ -58,19 +59,35 @@ namespace SharpCompress.Archives if (options.ExtractFullPath) { string folder = Path.GetDirectoryName(entry.Key); - string destdir = Path.Combine(destinationDirectory, folder); + string destdir = Path.GetFullPath( + Path.Combine(fullDestinationDirectoryPath, folder) + ); + if (!Directory.Exists(destdir)) { + if (!destdir.StartsWith(fullDestinationDirectoryPath)) + { + throw new ExtractionException("Entry is trying to create a directory outside of the destination directory."); + } + Directory.CreateDirectory(destdir); } destinationFileName = Path.Combine(destdir, file); } else { - destinationFileName = Path.Combine(destinationDirectory, file); + destinationFileName = Path.Combine(fullDestinationDirectoryPath, file); } + if (!entry.IsDirectory) { + destinationFileName = Path.GetFullPath(destinationFileName); + + if (!destinationFileName.StartsWith(fullDestinationDirectoryPath)) + { + throw new ExtractionException("Entry is trying to write a file outside of the destination directory."); + } + entry.WriteToFile(destinationFileName, options); } } diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index a613abad..c776dcf9 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -433,7 +433,34 @@ namespace SharpCompress.Test.Zip } } } + } + [Fact] + public void Zip_Evil_Throws_Exception() + { + Exception expectedExcetpion = null; + string zipFile = Path.Combine(TEST_ARCHIVES_PATH, "Zip.Evil.zip"); + + try + { + using (var archive = ZipArchive.Open(zipFile)) + { + foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) + { + entry.WriteToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() + { + ExtractFullPath = true, + Overwrite = true + }); + } + } + } + catch (Exception ex) + { + expectedExcetpion = ex; + } + + Assert.NotEqual(expectedExcetpion, null); } class NonSeekableMemoryStream : MemoryStream diff --git a/tests/TestArchives/Archives/Zip.Evil.zip b/tests/TestArchives/Archives/Zip.Evil.zip new file mode 100644 index 0000000000000000000000000000000000000000..3474c88bec74e6381fb9e1e598f98076c64f2d68 GIT binary patch literal 547 zcmWIWW@h1H0D=Au{XYEp{-1>z7#Kj9gF%KNJwHE1ucV?RG=!6Z*>=(8s0P(*QKc2! z42&$_7#SE?L>L$tN-{Ew6*7wz5*0wI74q{^xj=@3q!<{cZs_+(aICfj@j$qhfq_9! zFJ?ecNNR3DOlnzX4#-DvSLZ2!grLr5WRhdX6&eztKw@BIU|?9%2x5YK3o Date: Sat, 5 May 2018 09:05:32 +0100 Subject: [PATCH 105/124] Evil zip is a windows only test because of paths --- .../SharpCompress.Test.csproj | 1 + tests/SharpCompress.Test/Zip/ZipArchiveTests.cs | 17 ++++++----------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/tests/SharpCompress.Test/SharpCompress.Test.csproj b/tests/SharpCompress.Test/SharpCompress.Test.csproj index 5635735e..6ae16ea3 100644 --- a/tests/SharpCompress.Test/SharpCompress.Test.csproj +++ b/tests/SharpCompress.Test/SharpCompress.Test.csproj @@ -15,5 +15,6 @@ + \ No newline at end of file diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index c776dcf9..da17ccbf 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -435,14 +435,15 @@ namespace SharpCompress.Test.Zip } } - [Fact] + [SkippableFact] public void Zip_Evil_Throws_Exception() { - Exception expectedExcetpion = null; + //windows only because of the paths + Skip.IfNot(Environment.OSVersion.Platform == PlatformID.Win32NT); + string zipFile = Path.Combine(TEST_ARCHIVES_PATH, "Zip.Evil.zip"); - try - { + Assert.ThrowsAny(() => { using (var archive = ZipArchive.Open(zipFile)) { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) @@ -454,13 +455,7 @@ namespace SharpCompress.Test.Zip }); } } - } - catch (Exception ex) - { - expectedExcetpion = ex; - } - - Assert.NotEqual(expectedExcetpion, null); + }); } class NonSeekableMemoryStream : MemoryStream From 1ce37ef7a87db548e73005bf9c5ccb396b97e285 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 5 May 2018 09:18:01 +0100 Subject: [PATCH 106/124] Fixes lzip stream disposal --- .../Compressors/LZMA/LZipStream.cs | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.cs index fbb78f5e..9e71d6f6 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.cs @@ -16,18 +16,18 @@ namespace SharpCompress.Compressors.LZMA /// public class LZipStream : Stream { - private readonly Stream stream; - private readonly CountingWritableSubStream rawStream; - private bool disposed; - private readonly bool leaveOpen; - private bool finished; + private readonly Stream _stream; + private readonly CountingWritableSubStream _countingWritableSubStream; + private bool _disposed; + private readonly bool _leaveOpen; + private bool _finished; - private long writeCount; + private long _writeCount; public LZipStream(Stream stream, CompressionMode mode, bool leaveOpen = false) { Mode = mode; - this.leaveOpen = leaveOpen; + _leaveOpen = leaveOpen; if (mode == CompressionMode.Decompress) { @@ -37,7 +37,7 @@ namespace SharpCompress.Compressors.LZMA throw new IOException("Not an LZip stream"); } byte[] properties = GetProperties(dSize); - this.stream = new LzmaStream(properties, stream); + _stream = new LzmaStream(properties, stream); } else { @@ -45,33 +45,33 @@ namespace SharpCompress.Compressors.LZMA int dSize = 104 * 1024; WriteHeaderSize(stream); - rawStream = new CountingWritableSubStream(stream); - this.stream = new Crc32Stream(new LzmaStream(new LzmaEncoderProperties(true, dSize), false, rawStream)); + _countingWritableSubStream = new CountingWritableSubStream(stream); + _stream = new Crc32Stream(new LzmaStream(new LzmaEncoderProperties(true, dSize), false, _countingWritableSubStream)); } } public void Finish() { - if (!finished) + if (!_finished) { if (Mode == CompressionMode.Compress) { - var crc32Stream = (Crc32Stream)stream; + var crc32Stream = (Crc32Stream)_stream; crc32Stream.WrappedStream.Dispose(); crc32Stream.Dispose(); - var compressedCount = rawStream.Count; + var compressedCount = _countingWritableSubStream.Count; var bytes = DataConverter.LittleEndian.GetBytes(crc32Stream.Crc); - rawStream.Write(bytes, 0, bytes.Length); + _countingWritableSubStream.Write(bytes, 0, bytes.Length); - bytes = DataConverter.LittleEndian.GetBytes(writeCount); - rawStream.Write(bytes, 0, bytes.Length); + bytes = DataConverter.LittleEndian.GetBytes(_writeCount); + _countingWritableSubStream.Write(bytes, 0, bytes.Length); //total with headers bytes = DataConverter.LittleEndian.GetBytes(compressedCount + 6 + 20); - rawStream.Write(bytes, 0, bytes.Length); + _countingWritableSubStream.Write(bytes, 0, bytes.Length); } - finished = true; + _finished = true; } } @@ -79,17 +79,17 @@ namespace SharpCompress.Compressors.LZMA protected override void Dispose(bool disposing) { - if (disposed) + if (_disposed) { return; } - disposed = true; + _disposed = true; if (disposing) { Finish(); - if (!leaveOpen) + if (!_leaveOpen) { - rawStream.Dispose(); + _stream.Dispose(); } } } @@ -104,7 +104,7 @@ namespace SharpCompress.Compressors.LZMA public override void Flush() { - stream.Flush(); + _stream.Flush(); } // TODO: Both Length and Position are sometimes feasible, but would require @@ -113,7 +113,7 @@ namespace SharpCompress.Compressors.LZMA public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public override int Read(byte[] buffer, int offset, int count) => stream.Read(buffer, offset, count); + public override int Read(byte[] buffer, int offset, int count) => _stream.Read(buffer, offset, count); public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); @@ -121,8 +121,8 @@ namespace SharpCompress.Compressors.LZMA public override void Write(byte[] buffer, int offset, int count) { - stream.Write(buffer, offset, count); - writeCount += count; + _stream.Write(buffer, offset, count); + _writeCount += count; } #endregion From 18475cc86deb9d7839ffcb5706ace83d842a7eb6 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 5 May 2018 09:38:56 +0100 Subject: [PATCH 107/124] Use proper xunit single threading --- tests/SharpCompress.Test/TestBase.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/tests/SharpCompress.Test/TestBase.cs b/tests/SharpCompress.Test/TestBase.cs index d8e5b587..6f6d314b 100644 --- a/tests/SharpCompress.Test/TestBase.cs +++ b/tests/SharpCompress.Test/TestBase.cs @@ -6,9 +6,11 @@ using System.Threading; using SharpCompress.Readers; using Xunit; +[assembly: CollectionBehavior(DisableTestParallelization = true)] + namespace SharpCompress.Test { - public class TestBase : IDisposable + public class TestBase { protected string SOLUTION_BASE_PATH = null; protected string TEST_ARCHIVES_PATH; @@ -270,12 +272,8 @@ namespace SharpCompress.Test return false; } - private static readonly object lockObject = new object(); - public TestBase() { - Monitor.Enter(lockObject); - var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf("SharpCompress.Test", StringComparison.OrdinalIgnoreCase); SOLUTION_BASE_PATH = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory.Substring(0, index)); @@ -285,10 +283,5 @@ namespace SharpCompress.Test SCRATCH_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Scratch"); SCRATCH2_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Scratch2"); } - - public void Dispose() - { - Monitor.Exit(lockObject); - } } } From e95559b4fcd7595cfd1827804a10b09a63342b26 Mon Sep 17 00:00:00 2001 From: Christopher Lees Date: Sat, 5 May 2018 15:08:56 +0100 Subject: [PATCH 108/124] Fix broken link in usage.md --- USAGE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/USAGE.md b/USAGE.md index 8470ca1f..93f89b51 100644 --- a/USAGE.md +++ b/USAGE.md @@ -32,7 +32,7 @@ To be explicit though, consider always using the overloads that use `ReaderOptio ## Samples -Also, look over the tests for more thorough [examples](https://github.com/adamhathcock/sharpcompress/tree/master/test/SharpCompress.Test) +Also, look over the tests for more thorough [examples](https://github.com/adamhathcock/sharpcompress/tree/master/tests/SharpCompress.Test) ### Create Zip Archive from all files in a directory to a file From 5fdae1cf8257f48b77d6c8a78827cb2e93bfeaa1 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 5 May 2018 19:23:34 +0100 Subject: [PATCH 109/124] Make readonly and fix visibility --- .../Zip/Headers/Zip64DirectoryEndHeader.cs | 2 +- .../Common/Zip/WinzipAesEncryptionData.cs | 8 +- src/SharpCompress/Compressors/ADC/ADCBase.cs | 12 +- .../Compressors/LZMA/LzmaDecoder.cs | 146 +++++++++--------- .../Compressors/LZMA/LzmaEncoder.cs | 42 ++--- .../Compressors/Rar/IRarUnpack.cs | 2 +- .../Compressors/Rar/UnpackV1/Unpack50.cs | 18 +-- .../Compressors/Rar/UnpackV1/UnpackInline.cs | 2 +- .../Rar/UnpackV2017/BitInput.getbits_cpp.cs | 3 +- .../Rar/UnpackV2017/BitInput.getbits_hpp.cs | 2 +- .../FragmentedWindow.unpack50frag_cpp.cs | 5 +- .../Rar/UnpackV2017/Unpack.rawint_hpp.cs | 6 +- .../Rar/UnpackV2017/Unpack.unpack15_cpp.cs | 80 +++++----- .../Rar/UnpackV2017/Unpack.unpack20_cpp.cs | 21 +-- .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 38 ++--- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 11 +- .../UnpackV2017/Unpack.unpackinline_cpp.cs | 11 +- .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 124 ++++++++------- src/SharpCompress/Compressors/Xz/Crc32.cs | 6 +- .../Compressors/Xz/Filters/BlockFilter.cs | 2 +- .../Compressors/Xz/Filters/Lzma2Filter.cs | 2 +- src/SharpCompress/Compressors/Xz/XZBlock.cs | 2 +- src/SharpCompress/Compressors/Xz/XZIndex.cs | 2 +- src/SharpCompress/Compressors/Xz/XZStream.cs | 2 +- src/SharpCompress/Converters/DataConverter.cs | 8 +- src/SharpCompress/Crypto/RijndaelEngine.cs | 4 +- src/SharpCompress/Writers/Tar/TarWriter.cs | 2 +- tests/SharpCompress.Test/ArchiveTests.cs | 6 +- .../Xz/Filters/Lzma2Tests.cs | 2 +- tests/SharpCompress.Test/Zip/Zip64Tests.cs | 2 +- .../SharpCompress.Test/Zip/ZipArchiveTests.cs | 2 +- .../SharpCompress.Test/Zip/ZipReaderTests.cs | 2 +- 32 files changed, 272 insertions(+), 305 deletions(-) diff --git a/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs b/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs index 2f97ad53..41a74ef2 100644 --- a/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs @@ -24,7 +24,7 @@ namespace SharpCompress.Common.Zip.Headers DataSector = reader.ReadBytes((int)(SizeOfDirectoryEndRecord - SizeOfFixedHeaderDataExceptSignatureAndSizeFields)); } - const int SizeOfFixedHeaderDataExceptSignatureAndSizeFields = 44; + private const int SizeOfFixedHeaderDataExceptSignatureAndSizeFields = 44; public long SizeOfDirectoryEndRecord { get; private set; } diff --git a/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs b/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs index 1966704c..0d85bafc 100644 --- a/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs +++ b/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs @@ -10,10 +10,10 @@ namespace SharpCompress.Common.Zip { private const int RFC2898_ITERATIONS = 1000; - private byte[] salt; - private WinzipAesKeySize keySize; - private byte[] passwordVerifyValue; - private string password; + private readonly byte[] salt; + private readonly WinzipAesKeySize keySize; + private readonly byte[] passwordVerifyValue; + private readonly string password; private byte[] generatedVerifyValue; diff --git a/src/SharpCompress/Compressors/ADC/ADCBase.cs b/src/SharpCompress/Compressors/ADC/ADCBase.cs index 59b42016..83250cee 100644 --- a/src/SharpCompress/Compressors/ADC/ADCBase.cs +++ b/src/SharpCompress/Compressors/ADC/ADCBase.cs @@ -33,11 +33,11 @@ namespace SharpCompress.Compressors.ADC /// public static class ADCBase { - const int Plain = 1; - const int TwoByte = 2; - const int ThreeByte = 3; + private const int Plain = 1; + private const int TwoByte = 2; + private const int ThreeByte = 3; - static int GetChunkType(byte byt) + private static int GetChunkType(byte byt) { if ((byt & 0x80) == 0x80) { @@ -50,7 +50,7 @@ namespace SharpCompress.Compressors.ADC return TwoByte; } - static int GetChunkSize(byte byt) + private static int GetChunkSize(byte byt) { switch (GetChunkType(byt)) { @@ -65,7 +65,7 @@ namespace SharpCompress.Compressors.ADC } } - static int GetOffset(byte[] chunk, int position) + private static int GetOffset(byte[] chunk, int position) { switch (GetChunkType(chunk[position])) { diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index 063b9e3e..6351a06a 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -9,50 +9,50 @@ namespace SharpCompress.Compressors.LZMA { private class LenDecoder { - private BitDecoder m_Choice = new BitDecoder(); - private BitDecoder m_Choice2 = new BitDecoder(); - private readonly BitTreeDecoder[] m_LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; - private readonly BitTreeDecoder[] m_MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; - private BitTreeDecoder m_HighCoder = new BitTreeDecoder(Base.kNumHighLenBits); - private uint m_NumPosStates; + private BitDecoder _Choice = new BitDecoder(); + private BitDecoder _Choice2 = new BitDecoder(); + private readonly BitTreeDecoder[] _LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; + private readonly BitTreeDecoder[] _MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; + private BitTreeDecoder _HighCoder = new BitTreeDecoder(Base.kNumHighLenBits); + private uint _NumPosStates; public void Create(uint numPosStates) { - for (uint posState = m_NumPosStates; posState < numPosStates; posState++) + for (uint posState = _NumPosStates; posState < numPosStates; posState++) { - m_LowCoder[posState] = new BitTreeDecoder(Base.kNumLowLenBits); - m_MidCoder[posState] = new BitTreeDecoder(Base.kNumMidLenBits); + _LowCoder[posState] = new BitTreeDecoder(Base.kNumLowLenBits); + _MidCoder[posState] = new BitTreeDecoder(Base.kNumMidLenBits); } - m_NumPosStates = numPosStates; + _NumPosStates = numPosStates; } public void Init() { - m_Choice.Init(); - for (uint posState = 0; posState < m_NumPosStates; posState++) + _Choice.Init(); + for (uint posState = 0; posState < _NumPosStates; posState++) { - m_LowCoder[posState].Init(); - m_MidCoder[posState].Init(); + _LowCoder[posState].Init(); + _MidCoder[posState].Init(); } - m_Choice2.Init(); - m_HighCoder.Init(); + _Choice2.Init(); + _HighCoder.Init(); } public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState) { if (m_Choice.Decode(rangeDecoder) == 0) { - return m_LowCoder[posState].Decode(rangeDecoder); + return _LowCoder[posState].Decode(rangeDecoder); } uint symbol = Base.kNumLowLenSymbols; if (m_Choice2.Decode(rangeDecoder) == 0) { - symbol += m_MidCoder[posState].Decode(rangeDecoder); + symbol += _MidCoder[posState].Decode(rangeDecoder); } else { symbol += Base.kNumMidLenSymbols; - symbol += m_HighCoder.Decode(rangeDecoder); + symbol += _HighCoder.Decode(rangeDecoder); } return symbol; } @@ -62,18 +62,18 @@ namespace SharpCompress.Compressors.LZMA { private struct Decoder2 { - private BitDecoder[] m_Decoders; + private BitDecoder[] _Decoders; public void Create() { - m_Decoders = new BitDecoder[0x300]; + _Decoders = new BitDecoder[0x300]; } public void Init() { for (int i = 0; i < 0x300; i++) { - m_Decoders[i].Init(); + _Decoders[i].Init(); } } @@ -82,7 +82,7 @@ namespace SharpCompress.Compressors.LZMA uint symbol = 1; do { - symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder); + symbol = (symbol << 1) | _Decoders[symbol].Decode(rangeDecoder); } while (symbol < 0x100); return (byte)symbol; @@ -95,13 +95,13 @@ namespace SharpCompress.Compressors.LZMA { uint matchBit = (uint)(matchByte >> 7) & 1; matchByte <<= 1; - uint bit = m_Decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); + uint bit = _Decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); symbol = (symbol << 1) | bit; if (matchBit != bit) { while (symbol < 0x100) { - symbol = (symbol << 1) | m_Decoders[symbol].Decode(rangeDecoder); + symbol = (symbol << 1) | _Decoders[symbol].Decode(rangeDecoder); } break; } @@ -111,86 +111,86 @@ namespace SharpCompress.Compressors.LZMA } } - private Decoder2[] m_Coders; - private int m_NumPrevBits; - private int m_NumPosBits; - private uint m_PosMask; + private Decoder2[] _Coders; + private int _NumPrevBits; + private int _NumPosBits; + private uint _PosMask; public void Create(int numPosBits, int numPrevBits) { - if (m_Coders != null && m_NumPrevBits == numPrevBits && - m_NumPosBits == numPosBits) + if (m_Coders != null && _NumPrevBits == numPrevBits && + _NumPosBits == numPosBits) { return; } - m_NumPosBits = numPosBits; - m_PosMask = ((uint)1 << numPosBits) - 1; - m_NumPrevBits = numPrevBits; - uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); - m_Coders = new Decoder2[numStates]; + _NumPosBits = numPosBits; + _PosMask = ((uint)1 << numPosBits) - 1; + _NumPrevBits = numPrevBits; + uint numStates = (uint)1 << (m_NumPrevBits + _NumPosBits); + _Coders = new Decoder2[numStates]; for (uint i = 0; i < numStates; i++) { - m_Coders[i].Create(); + _Coders[i].Create(); } } public void Init() { - uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + uint numStates = (uint)1 << (m_NumPrevBits + _NumPosBits); for (uint i = 0; i < numStates; i++) { - m_Coders[i].Init(); + _Coders[i].Init(); } } private uint GetState(uint pos, byte prevByte) { - return ((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits)); + return ((pos & _PosMask) << _NumPrevBits) + (uint)(prevByte >> (8 - _NumPrevBits)); } public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte) { - return m_Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); + return _Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); } public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte) { - return m_Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); + return _Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); } } - private OutWindow m_OutWindow; + private OutWindow _OutWindow; - private readonly BitDecoder[] m_IsMatchDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; - private readonly BitDecoder[] m_IsRepDecoders = new BitDecoder[Base.kNumStates]; - private readonly BitDecoder[] m_IsRepG0Decoders = new BitDecoder[Base.kNumStates]; - private readonly BitDecoder[] m_IsRepG1Decoders = new BitDecoder[Base.kNumStates]; - private readonly BitDecoder[] m_IsRepG2Decoders = new BitDecoder[Base.kNumStates]; - private readonly BitDecoder[] m_IsRep0LongDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + private readonly BitDecoder[] _IsMatchDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + private readonly BitDecoder[] _IsRepDecoders = new BitDecoder[Base.kNumStates]; + private readonly BitDecoder[] _IsRepG0Decoders = new BitDecoder[Base.kNumStates]; + private readonly BitDecoder[] _IsRepG1Decoders = new BitDecoder[Base.kNumStates]; + private readonly BitDecoder[] _IsRepG2Decoders = new BitDecoder[Base.kNumStates]; + private readonly BitDecoder[] _IsRep0LongDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; - private readonly BitTreeDecoder[] m_PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates]; - private readonly BitDecoder[] m_PosDecoders = new BitDecoder[Base.kNumFullDistances - Base.kEndPosModelIndex]; + private readonly BitTreeDecoder[] _PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates]; + private readonly BitDecoder[] _PosDecoders = new BitDecoder[Base.kNumFullDistances - Base.kEndPosModelIndex]; - private BitTreeDecoder m_PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits); + private BitTreeDecoder _PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits); - private readonly LenDecoder m_LenDecoder = new LenDecoder(); - private readonly LenDecoder m_RepLenDecoder = new LenDecoder(); + private readonly LenDecoder _LenDecoder = new LenDecoder(); + private readonly LenDecoder _RepLenDecoder = new LenDecoder(); - private readonly LiteralDecoder m_LiteralDecoder = new LiteralDecoder(); + private readonly LiteralDecoder _LiteralDecoder = new LiteralDecoder(); - private int m_DictionarySize; + private int _DictionarySize; - private uint m_PosStateMask; + private uint _PosStateMask; private Base.State state = new Base.State(); private uint rep0, rep1, rep2, rep3; public Decoder() { - m_DictionarySize = -1; + _DictionarySize = -1; for (int i = 0; i < Base.kNumLenToPosStates; i++) { - m_PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits); + _PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits); } } @@ -200,9 +200,9 @@ namespace SharpCompress.Compressors.LZMA { throw new InvalidParamException(); } - m_OutWindow = new OutWindow(); + _OutWindow = new OutWindow(); int blockSize = Math.Max(m_DictionarySize, (1 << 12)); - m_OutWindow.Create(blockSize); + _OutWindow.Create(blockSize); } private void SetLiteralProperties(int lp, int lc) @@ -215,7 +215,7 @@ namespace SharpCompress.Compressors.LZMA { throw new InvalidParamException(); } - m_LiteralDecoder.Create(lp, lc); + _LiteralDecoder.Create(lp, lc); } private void SetPosBitsProperties(int pb) @@ -225,9 +225,9 @@ namespace SharpCompress.Compressors.LZMA throw new InvalidParamException(); } uint numPosStates = (uint)1 << pb; - m_LenDecoder.Create(numPosStates); - m_RepLenDecoder.Create(numPosStates); - m_PosStateMask = numPosStates - 1; + _LenDecoder.Create(numPosStates); + _RepLenDecoder.Create(numPosStates); + _PosStateMask = numPosStates - 1; } private void Init() @@ -235,19 +235,19 @@ namespace SharpCompress.Compressors.LZMA uint i; for (i = 0; i < Base.kNumStates; i++) { - for (uint j = 0; j <= m_PosStateMask; j++) + for (uint j = 0; j <= _PosStateMask; j++) { uint index = (i << Base.kNumPosStatesBitsMax) + j; - m_IsMatchDecoders[index].Init(); - m_IsRep0LongDecoders[index].Init(); + _IsMatchDecoders[index].Init(); + _IsRep0LongDecoders[index].Init(); } - m_IsRepDecoders[i].Init(); - m_IsRepG0Decoders[i].Init(); - m_IsRepG1Decoders[i].Init(); - m_IsRepG2Decoders[i].Init(); + _IsRepDecoders[i].Init(); + _IsRepG0Decoders[i].Init(); + _IsRepG1Decoders[i].Init(); + _IsRepG2Decoders[i].Init(); } - m_LiteralDecoder.Init(); + _LiteralDecoder.Init(); for (i = 0; i < Base.kNumLenToPosStates; i++) { m_PosSlotDecoder[i].Init(); diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs index 6f5b0145..a4e76b72 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs @@ -80,18 +80,18 @@ namespace SharpCompress.Compressors.LZMA { public struct Encoder2 { - private BitEncoder[] m_Encoders; + private BitEncoder[] _Encoders; public void Create() { - m_Encoders = new BitEncoder[0x300]; + _Encoders = new BitEncoder[0x300]; } public void Init() { for (int i = 0; i < 0x300; i++) { - m_Encoders[i].Init(); + _Encoders[i].Init(); } } @@ -101,7 +101,7 @@ namespace SharpCompress.Compressors.LZMA for (int i = 7; i >= 0; i--) { uint bit = (uint)((symbol >> i) & 1); - m_Encoders[context].Encode(rangeEncoder, bit); + _Encoders[context].Encode(rangeEncoder, bit); context = (context << 1) | bit; } } @@ -120,7 +120,7 @@ namespace SharpCompress.Compressors.LZMA state += ((1 + matchBit) << 8); same = (matchBit == bit); } - m_Encoders[state].Encode(rangeEncoder, bit); + _Encoders[state].Encode(rangeEncoder, bit); context = (context << 1) | bit; } } @@ -136,7 +136,7 @@ namespace SharpCompress.Compressors.LZMA { uint matchBit = (uint)(matchByte >> i) & 1; uint bit = (uint)(symbol >> i) & 1; - price += m_Encoders[((1 + matchBit) << 8) + context].GetPrice(bit); + price += _Encoders[((1 + matchBit) << 8) + context].GetPrice(bit); context = (context << 1) | bit; if (matchBit != bit) { @@ -148,47 +148,47 @@ namespace SharpCompress.Compressors.LZMA for (; i >= 0; i--) { uint bit = (uint)(symbol >> i) & 1; - price += m_Encoders[context].GetPrice(bit); + price += _Encoders[context].GetPrice(bit); context = (context << 1) | bit; } return price; } } - private Encoder2[] m_Coders; - private int m_NumPrevBits; - private int m_NumPosBits; - private uint m_PosMask; + private Encoder2[] _Coders; + private int _NumPrevBits; + private int _NumPosBits; + private uint _PosMask; public void Create(int numPosBits, int numPrevBits) { - if (m_Coders != null && m_NumPrevBits == numPrevBits && m_NumPosBits == numPosBits) + if (m_Coders != null && _NumPrevBits == numPrevBits && _NumPosBits == numPosBits) { return; } - m_NumPosBits = numPosBits; - m_PosMask = ((uint)1 << numPosBits) - 1; - m_NumPrevBits = numPrevBits; - uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); - m_Coders = new Encoder2[numStates]; + _NumPosBits = numPosBits; + _PosMask = ((uint)1 << numPosBits) - 1; + _NumPrevBits = numPrevBits; + uint numStates = (uint)1 << (m_NumPrevBits + _NumPosBits); + _Coders = new Encoder2[numStates]; for (uint i = 0; i < numStates; i++) { - m_Coders[i].Create(); + _Coders[i].Create(); } } public void Init() { - uint numStates = (uint)1 << (m_NumPrevBits + m_NumPosBits); + uint numStates = (uint)1 << (m_NumPrevBits + _NumPosBits); for (uint i = 0; i < numStates; i++) { - m_Coders[i].Init(); + _Coders[i].Init(); } } public Encoder2 GetSubCoder(UInt32 pos, Byte prevByte) { - return m_Coders[((pos & m_PosMask) << m_NumPrevBits) + (uint)(prevByte >> (8 - m_NumPrevBits))]; + return _Coders[((pos & _PosMask) << _NumPrevBits) + (uint)(prevByte >> (8 - _NumPrevBits))]; } } diff --git a/src/SharpCompress/Compressors/Rar/IRarUnpack.cs b/src/SharpCompress/Compressors/Rar/IRarUnpack.cs index ecd70a47..0cc86a78 100644 --- a/src/SharpCompress/Compressors/Rar/IRarUnpack.cs +++ b/src/SharpCompress/Compressors/Rar/IRarUnpack.cs @@ -3,7 +3,7 @@ using SharpCompress.Common.Rar.Headers; namespace SharpCompress.Compressors.Rar { - interface IRarUnpack + internal interface IRarUnpack { void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream); void DoUnpack(); diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs index 01d3604f..ec3fbdc1 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs @@ -298,8 +298,7 @@ public bool TablePresent; UnpWriteBuf(); } - - uint ReadFilterData() + private uint ReadFilterData() { uint ByteCount=(Inp.fgetbits()>>14)+1; Inp.AddBits(2); @@ -314,8 +313,7 @@ public bool TablePresent; return Data; } - - bool ReadFilter(UnpackFilter Filter) + private bool ReadFilter(UnpackFilter Filter) { if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16) if (!UnpReadBuf()) @@ -340,8 +338,7 @@ public bool TablePresent; return true; } - - bool AddFilter(UnpackFilter Filter) + private bool AddFilter(UnpackFilter Filter) { if (Filters.Count>=MAX_UNPACK_FILTERS) { @@ -360,8 +357,7 @@ public bool TablePresent; return true; } - - bool UnpReadBuf() + private bool UnpReadBuf() { int DataSize=ReadTop-Inp.InAddr; // Data left to process. if (DataSize<0) @@ -675,15 +671,13 @@ public bool TablePresent; // WrittenFileSize+=Size; // } - - void UnpInitData50(bool Solid) + private void UnpInitData50(bool Solid) { if (!Solid) TablesRead5=false; } - - bool ReadBlockHeader() + private bool ReadBlockHeader() { Header.HeaderSize=0; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs index 35b65eb7..1bb2893d 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/UnpackInline.cs @@ -3,7 +3,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 { internal partial class Unpack { - uint SlotToLength(uint Slot) + private uint SlotToLength(uint Slot) { //uint LBits,Length=2; int LBits; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_cpp.cs index f702fb48..ff7b70d7 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_cpp.cs @@ -55,8 +55,7 @@ uint fgetbits() return getbits(); } - -void SetExternalBuffer(byte []Buf) + private void SetExternalBuffer(byte []Buf) { //if (InBuf!=NULL && !ExternalBuffer) // delete[] InBuf; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs index 0eb0b64a..4f16592e 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/BitInput.getbits_hpp.cs @@ -59,7 +59,7 @@ internal partial class BitInput // Check if buffer has enough space for IncPtr bytes. Returns 'true' // if buffer will be overflown. - bool Overflow(uint IncPtr) + private bool Overflow(uint IncPtr) { return InAddr+IncPtr>=MAX_SIZE; } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs index cd0b5eb0..e3614217 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/FragmentedWindow.unpack50frag_cpp.cs @@ -13,7 +13,7 @@ using System; namespace SharpCompress.Compressors.Rar.UnpackV2017 { - partial class FragmentedWindow + internal partial class FragmentedWindow { public FragmentedWindow() @@ -28,8 +28,7 @@ public FragmentedWindow() // Reset(); //} - -void Reset() + private void Reset() { for (uint I=0;I>(xsize-(n)))) //#define rotrs(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n)))) @@ -29,7 +29,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 // return D[0]+(D[1]<<8); //} -uint32 RawGet4(byte[] D, int offset) + private uint32 RawGet4(byte[] D, int offset) { return (uint)(D[offset]+(D[offset+1]<<8)+(D[offset+2]<<16)+(D[offset+3]<<24)); } @@ -52,7 +52,7 @@ uint32 RawGet4(byte[] D, int offset) // D[1]=(byte)(Field>>8); //} -void RawPut4(uint32 Field,byte[] D, int offset) + private void RawPut4(uint32 Field,byte[] D, int offset) { D[offset]=(byte)(Field); D[offset+1]=(byte)(Field>>8); diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs index d76ee10c..8eeef6fe 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs @@ -4,47 +4,53 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { + private const int STARTL1 =2; -const int STARTL1 =2; -static uint[] DecL1={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00, + private static readonly uint[] DecL1={0x8000,0xa000,0xc000,0xd000,0xe000,0xea00, 0xee00,0xf000,0xf200,0xf200,0xffff}; -static uint[] PosL1={0,0,0,2,3,5,7,11,16,20,24,32,32}; -const int STARTL2 =3; -static uint[] DecL2={0xa000,0xc000,0xd000,0xe000,0xea00,0xee00, + private static readonly uint[] PosL1={0,0,0,2,3,5,7,11,16,20,24,32,32}; + + private const int STARTL2 =3; + + private static readonly uint[] DecL2={0xa000,0xc000,0xd000,0xe000,0xea00,0xee00, 0xf000,0xf200,0xf240,0xffff}; -static uint[] PosL2={0,0,0,0,5,7,9,13,18,22,26,34,36}; -const int STARTHF0 =4; -static uint[] DecHf0={0x8000,0xc000,0xe000,0xf200,0xf200,0xf200, + private static readonly uint[] PosL2={0,0,0,0,5,7,9,13,18,22,26,34,36}; + + private const int STARTHF0 =4; + + private static readonly uint[] DecHf0={0x8000,0xc000,0xe000,0xf200,0xf200,0xf200, 0xf200,0xf200,0xffff}; -static uint[] PosHf0={0,0,0,0,0,8,16,24,33,33,33,33,33}; + private static readonly uint[] PosHf0={0,0,0,0,0,8,16,24,33,33,33,33,33}; -const int STARTHF1 =5; -static uint[] DecHf1={0x2000,0xc000,0xe000,0xf000,0xf200,0xf200, + private const int STARTHF1 =5; + + private static readonly uint[] DecHf1={0x2000,0xc000,0xe000,0xf000,0xf200,0xf200, 0xf7e0,0xffff}; -static uint[] PosHf1={0,0,0,0,0,0,4,44,60,76,80,80,127}; + private static readonly uint[] PosHf1={0,0,0,0,0,0,4,44,60,76,80,80,127}; -const int STARTHF2 =5; -static uint[] DecHf2={0x1000,0x2400,0x8000,0xc000,0xfa00,0xffff, + private const int STARTHF2 =5; + + private static readonly uint[] DecHf2={0x1000,0x2400,0x8000,0xc000,0xfa00,0xffff, 0xffff,0xffff}; -static uint[] PosHf2={0,0,0,0,0,0,2,7,53,117,233,0,0}; + private static readonly uint[] PosHf2={0,0,0,0,0,0,2,7,53,117,233,0,0}; -const int STARTHF3 =6; -static uint[] DecHf3={0x800,0x2400,0xee00,0xfe80,0xffff,0xffff, + private const int STARTHF3 =6; + + private static readonly uint[] DecHf3={0x800,0x2400,0xee00,0xfe80,0xffff,0xffff, 0xffff}; -static uint[] PosHf3={0,0,0,0,0,0,0,2,16,218,251,0,0}; + private static readonly uint[] PosHf3={0,0,0,0,0,0,0,2,16,218,251,0,0}; -const int STARTHF4 =8; -static uint[] DecHf4={0xff00,0xffff,0xffff,0xffff,0xffff,0xffff}; -static uint[] PosHf4={0,0,0,0,0,0,0,0,0,255,0,0,0}; + private const int STARTHF4 =8; + private static readonly uint[] DecHf4={0xff00,0xffff,0xffff,0xffff,0xffff,0xffff}; + private static readonly uint[] PosHf4={0,0,0,0,0,0,0,0,0,255,0,0,0}; - -void Unpack15(bool Solid) + private void Unpack15(bool Solid) { UnpInitData(Solid); UnpInitData15(Solid); @@ -119,9 +125,9 @@ void Unpack15(bool Solid) //#define GetShortLen1(pos) ((pos)==1 ? Buf60+3:ShortLen1[pos]) -uint GetShortLen1(uint pos) { return ((pos)==1 ? (uint)(this.Buf60+3):ShortLen1[pos]); } + private uint GetShortLen1(uint pos) { return ((pos)==1 ? (uint)(this.Buf60+3):ShortLen1[pos]); } //#define GetShortLen2(pos) ((pos)==3 ? Buf60+3:ShortLen2[pos]) -uint GetShortLen2(uint pos) { return ((pos)==3 ? (uint)(this.Buf60+3):ShortLen2[pos]); } + private uint GetShortLen2(uint pos) { return ((pos)==3 ? (uint)(this.Buf60+3):ShortLen2[pos]); } internal static class Unpack15Local { public static readonly uint[] ShortLen1={1,3,4,4,5,6,7,8,8,4,4,5,6,6,4,0}; @@ -132,7 +138,7 @@ internal static class Unpack15Local { 0xfc,0xc0,0x80,0x90,0x98,0x9c,0xb0}; } -void ShortLZ() + private void ShortLZ() { uint Length,SaveLength; uint LastDistance; @@ -235,8 +241,7 @@ void ShortLZ() CopyString15(Distance,Length); } - -void LongLZ() + private void LongLZ() { uint Length; uint Distance; @@ -327,8 +332,7 @@ void LongLZ() CopyString15(Distance,Length); } - -void HuffDecode() + private void HuffDecode() { uint CurByte,NewBytePlace; uint Length; @@ -405,8 +409,7 @@ void HuffDecode() ChSet[NewBytePlace]=(ushort)CurByte; } - -void GetFlagsBuf() + private void GetFlagsBuf() { uint Flags,NewFlagsPlace; uint FlagsPlace=DecodeNum(Inp.fgetbits(),STARTHF2,DecHf2,PosHf2); @@ -433,7 +436,7 @@ void GetFlagsBuf() ChSetC[NewFlagsPlace]=(ushort)Flags; } -void UnpInitData15(bool Solid) + private void UnpInitData15(bool Solid) { if (!Solid) { @@ -450,7 +453,7 @@ void UnpInitData15(bool Solid) ReadTop=0; } -void InitHuff() + private void InitHuff() { for (uint I=0;I<256;I++) { @@ -464,8 +467,7 @@ void InitHuff() CorrHuff(ChSetB,NToPlB); } - -void CorrHuff(ushort[] CharSet,byte[] NumToPlace) + private void CorrHuff(ushort[] CharSet,byte[] NumToPlace) { int I,J; for (I=7;I>=0;I--) @@ -476,8 +478,7 @@ void CorrHuff(ushort[] CharSet,byte[] NumToPlace) NumToPlace[I]=(byte)((7-I)*32); } - -void CopyString15(uint Distance,uint Length) + private void CopyString15(uint Distance,uint Length) { DestUnpSize-=Length; while (Length-- != 0) @@ -487,8 +488,7 @@ void CopyString15(uint Distance,uint Length) } } - -uint DecodeNum(uint Num,uint StartPos,uint[] DecTab,uint[] PosTab) + private uint DecodeNum(uint Num,uint StartPos,uint[] DecTab,uint[] PosTab) { int I; for (Num&=0xfff0,I=0;DecTab[I]<=Num;I++) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs index 57ecdc02..72d1b529 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs @@ -17,8 +17,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { - -void CopyString20(uint Length,uint Distance) + private void CopyString20(uint Length,uint Distance) { LastDist=OldDist[OldDistPtr++ & 3]=Distance; LastLength=Length; @@ -35,7 +34,8 @@ internal static class Unpack20Local { public static readonly byte[] SDDecode={0,4,8,16,32,64,128,192}; public static readonly byte[] SDBits= {2,2,3, 4, 5, 6, 6, 6}; } -void Unpack20(bool Solid) + + private void Unpack20(bool Solid) { uint Bits; @@ -165,8 +165,7 @@ void Unpack20(bool Solid) UnpWriteBuf20(); } - -void UnpWriteBuf20() + private void UnpWriteBuf20() { if (UnpPtr!=WrPtr) UnpSomeRead=true; @@ -181,8 +180,7 @@ void UnpWriteBuf20() WrPtr=UnpPtr; } - -bool ReadTables20() + private bool ReadTables20() { byte[] BitLength = new byte[BC20]; byte[] Table = new byte[MC20*4]; @@ -273,8 +271,7 @@ bool ReadTables20() return true; } - -void ReadLastTables() + private void ReadLastTables() { if (ReadTop>=Inp.InAddr+5) if (UnpAudioBlock) @@ -287,8 +284,7 @@ void ReadLastTables() ReadTables20(); } - -void UnpInitData20(bool Solid) + private void UnpInitData20(bool Solid) { if (!Solid) { @@ -306,8 +302,7 @@ void UnpInitData20(bool Solid) } } - -byte DecodeAudio(int Delta) + private byte DecodeAudio(int Delta) { AudioVariables V=AudV[UnpCurChannel]; V.ByteCount++; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs index 53f0e713..d35aa3e4 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack50_cpp.cs @@ -17,7 +17,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { -void Unpack5(bool Solid) + private void Unpack5(bool Solid) { FileExtracted=true; @@ -173,8 +173,7 @@ void Unpack5(bool Solid) UnpWriteBuf(); } - -uint ReadFilterData(BitInput Inp) + private uint ReadFilterData(BitInput Inp) { uint ByteCount=(Inp.fgetbits()>>14)+1; Inp.addbits(2); @@ -188,8 +187,7 @@ uint ReadFilterData(BitInput Inp) return Data; } - -bool ReadFilter(BitInput Inp,UnpackFilter Filter) + private bool ReadFilter(BitInput Inp,UnpackFilter Filter) { if (!Inp.ExternalBuffer && Inp.InAddr>ReadTop-16) if (!UnpReadBuf()) @@ -212,8 +210,7 @@ bool ReadFilter(BitInput Inp,UnpackFilter Filter) return true; } - -bool AddFilter(UnpackFilter Filter) + private bool AddFilter(UnpackFilter Filter) { if (Filters.Count>=MAX_UNPACK_FILTERS) { @@ -232,8 +229,7 @@ bool AddFilter(UnpackFilter Filter) return true; } - -bool UnpReadBuf() + private bool UnpReadBuf() { int DataSize=ReadTop-Inp.InAddr; // Data left to process. if (DataSize<0) @@ -271,8 +267,7 @@ bool UnpReadBuf() return ReadCode!=-1; } - -void UnpWriteBuf() + private void UnpWriteBuf() { size_t WrittenBorder=WrPtr; size_t FullWriteSize=(UnpPtr-WrittenBorder)&MaxWinMask; @@ -425,8 +420,7 @@ void UnpWriteBuf() WriteBorder=WrPtr; } - -byte[] ApplyFilter(byte[] __d,uint DataSize,UnpackFilter Flt) + private byte[] ApplyFilter(byte[] __d,uint DataSize,UnpackFilter Flt) { int Data = 0; byte[] SrcData=__d; @@ -513,8 +507,7 @@ byte[] ApplyFilter(byte[] __d,uint DataSize,UnpackFilter Flt) return null; } - -void UnpWriteArea(size_t StartPtr,size_t EndPtr) + private void UnpWriteArea(size_t StartPtr,size_t EndPtr) { if (EndPtr!=StartPtr) UnpSomeRead=true; @@ -544,8 +537,7 @@ void UnpWriteArea(size_t StartPtr,size_t EndPtr) UnpWriteData(Window,StartPtr,EndPtr-StartPtr); } - -void UnpWriteData(byte[] Data, size_t offset, size_t Size) + private void UnpWriteData(byte[] Data, size_t offset, size_t Size) { if (WrittenFileSize>=DestUnpSize) return; @@ -557,15 +549,13 @@ void UnpWriteData(byte[] Data, size_t offset, size_t Size) WrittenFileSize+=Size; } - -void UnpInitData50(bool Solid) + private void UnpInitData50(bool Solid) { if (!Solid) TablesRead5=false; } - -bool ReadBlockHeader(BitInput Inp,ref UnpackBlockHeader Header) + private bool ReadBlockHeader(BitInput Inp,ref UnpackBlockHeader Header) { Header.HeaderSize=0; @@ -608,8 +598,7 @@ bool ReadBlockHeader(BitInput Inp,ref UnpackBlockHeader Header) return true; } - -bool ReadTables(BitInput Inp,ref UnpackBlockHeader Header, ref UnpackBlockTables Tables) + private bool ReadTables(BitInput Inp,ref UnpackBlockHeader Header, ref UnpackBlockTables Tables) { if (!Header.TablePresent) return true; @@ -713,8 +702,7 @@ bool ReadTables(BitInput Inp,ref UnpackBlockHeader Header, ref UnpackBlockTables return true; } - -void InitFilters() + private void InitFilters() { //Filters.SoftReset(); Filters.Clear(); diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index 44abffa3..21d3cd91 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -64,8 +64,7 @@ public Unpack(/* ComprDataIO *DataIO */) //#endif //} - -void Init(size_t WinSize,bool Solid) + private void Init(size_t WinSize,bool Solid) { // If 32-bit RAR unpacks an archive with 4 GB dictionary, the window size // will be 0 because of size_t overflow. Let's issue the memory error. @@ -145,8 +144,7 @@ void Init(size_t WinSize,bool Solid) MaxWinMask=MaxWinSize-1; } - -void DoUnpack(uint Method,bool Solid) + private void DoUnpack(uint Method,bool Solid) { // Methods <50 will crash in Fragmented mode when accessing NULL Window. // They cannot be called in such mode now, but we check it below anyway @@ -194,8 +192,7 @@ void DoUnpack(uint Method,bool Solid) } } - -void UnpInitData(bool Solid) + private void UnpInitData(bool Solid) { if (!Solid) { @@ -233,7 +230,7 @@ void UnpInitData(bool Solid) // LengthTable contains the length in bits for every element of alphabet. // Dec is the structure to decode Huffman code/ // Size is size of length table and DecodeNum field in Dec structure, -void MakeDecodeTables(byte[] LengthTable, int offset, DecodeTable Dec,uint Size) + private void MakeDecodeTables(byte[] LengthTable, int offset, DecodeTable Dec,uint Size) { // Size of alphabet and DecodePos array. Dec.MaxNum=Size; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs index cd2fa8f0..db705807 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpackinline_cpp.cs @@ -15,8 +15,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 { internal partial class Unpack { - -void InsertOldDist(uint Distance) + private void InsertOldDist(uint Distance) { OldDist[3]=OldDist[2]; OldDist[2]=OldDist[1]; @@ -28,7 +27,7 @@ void InsertOldDist(uint Distance) //#define FAST_MEMCPY //#endif -void CopyString(uint Length,uint Distance) + private void CopyString(uint Length,uint Distance) { size_t SrcPtr=UnpPtr-Distance; @@ -104,8 +103,7 @@ void CopyString(uint Length,uint Distance) } } - -uint DecodeNumber(BitInput Inp,DecodeTable Dec) + private uint DecodeNumber(BitInput Inp,DecodeTable Dec) { // Left aligned 15 bit length raw bit field. uint BitField=Inp.getbits() & 0xfffe; @@ -149,8 +147,7 @@ uint DecodeNumber(BitInput Inp,DecodeTable Dec) return Dec.DecodeNum[Pos]; } - -uint SlotToLength(BitInput Inp,uint Slot) + private uint SlotToLength(BitInput Inp,uint Slot) { uint LBits,Length=2; if (Slot<8) diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index 06520ab5..b5175d24 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -51,7 +51,7 @@ public const int UNPACK_MAX_WRITE =0x400000; } // Decode compressed bit fields to alphabet numbers. -sealed class DecodeTable + internal sealed class DecodeTable { // Real size of DecodeNum table. public uint MaxNum; @@ -92,8 +92,7 @@ sealed class DecodeTable public readonly ushort[] DecodeNum = new ushort[LARGEST_TABLE_SIZE]; }; - -struct UnpackBlockHeader + internal struct UnpackBlockHeader { public int BlockSize; public int BlockBitSize; @@ -103,8 +102,7 @@ struct UnpackBlockHeader public bool TablePresent; }; - -struct UnpackBlockTables + internal struct UnpackBlockTables { public DecodeTable LD; // Decode literals. public DecodeTable DD; // Decode distances. @@ -173,7 +171,7 @@ struct UnpackThreadData //struct UnpackFilter -class UnpackFilter + internal class UnpackFilter { public byte Type; public uint BlockStart; @@ -186,7 +184,7 @@ class UnpackFilter //struct UnpackFilter30 -class UnpackFilter30 + internal class UnpackFilter30 { public uint BlockStart; public uint BlockLength; @@ -201,8 +199,7 @@ class UnpackFilter30 #endif*/ }; - -class AudioVariables // For RAR 2.0 archives only. + internal class AudioVariables // For RAR 2.0 archives only. { public int K1,K2,K3,K4,K5; public int D1,D2,D3,D4; @@ -215,13 +212,13 @@ class AudioVariables // For RAR 2.0 archives only. // We can use the fragmented dictionary in case heap does not have the single // large enough memory block. It is slower than normal dictionary. -partial class FragmentedWindow + internal partial class FragmentedWindow { - const int MAX_MEM_BLOCKS=32; + private const int MAX_MEM_BLOCKS=32; //void Reset(); - readonly byte[][] Mem = new byte[MAX_MEM_BLOCKS][]; - readonly size_t[] MemSize = new size_t[MAX_MEM_BLOCKS]; + private readonly byte[][] Mem = new byte[MAX_MEM_BLOCKS][]; + private readonly size_t[] MemSize = new size_t[MAX_MEM_BLOCKS]; //FragmentedWindow(); //~FragmentedWindow(); @@ -261,7 +258,7 @@ internal partial class Unpack //ComprDataIO *UnpIO; //BitInput Inp; - BitInput Inp { get { return this; } } // hopefully this gets inlined + private BitInput Inp { get { return this; } } // hopefully this gets inlined #if RarV2017_RAR_SMP void InitMT(); @@ -274,48 +271,47 @@ internal partial class Unpack byte *ReadBufMT; #endif - byte[] FilterSrcMemory = new byte[0]; - byte[] FilterDstMemory = new byte[0]; + private byte[] FilterSrcMemory = new byte[0]; + private byte[] FilterDstMemory = new byte[0]; // Filters code, one entry per filter. - readonly List Filters = new List(); + private readonly List Filters = new List(); - readonly uint[] OldDist = new uint[4]; - uint OldDistPtr; - uint LastLength; + private readonly uint[] OldDist = new uint[4]; + private uint OldDistPtr; + private uint LastLength; // LastDist is necessary only for RAR2 and older with circular OldDist // array. In RAR3 last distance is always stored in OldDist[0]. - uint LastDist; + private uint LastDist; - size_t UnpPtr,WrPtr; + private size_t UnpPtr,WrPtr; // Top border of read packed data. - int ReadTop; + private int ReadTop; // Border to call UnpReadBuf. We use it instead of (ReadTop-C) // for optimization reasons. Ensures that we have C bytes in buffer // unless we are at the end of file. - int ReadBorder; + private int ReadBorder; - UnpackBlockHeader BlockHeader; - UnpackBlockTables BlockTables; + private UnpackBlockHeader BlockHeader; + private UnpackBlockTables BlockTables; - size_t WriteBorder; + private size_t WriteBorder; - byte[] Window; + private byte[] Window; - readonly FragmentedWindow FragWindow = new FragmentedWindow(); - bool Fragmented; + private readonly FragmentedWindow FragWindow = new FragmentedWindow(); + private bool Fragmented; - - int64 DestUnpSize; + private int64 DestUnpSize; //bool Suspended; - bool UnpAllBuf; - bool UnpSomeRead; - int64 WrittenFileSize; - bool FileExtracted; + private bool UnpAllBuf; + private bool UnpSomeRead; + private int64 WrittenFileSize; + private bool FileExtracted; /***************************** Unpack v 1.5 *********************************/ @@ -330,29 +326,31 @@ internal partial class Unpack //void CopyString15(uint Distance,uint Length); //uint DecodeNum(uint Num,uint StartPos,uint *DecTab,uint *PosTab); - readonly ushort[] ChSet = new ushort[256],ChSetA = new ushort[256],ChSetB = new ushort[256],ChSetC = new ushort[256]; - readonly byte[] NToPl = new byte[256],NToPlB = new byte[256],NToPlC = new byte[256]; - uint FlagBuf,AvrPlc,AvrPlcB,AvrLn1,AvrLn2,AvrLn3; - int Buf60,NumHuf,StMode,LCount,FlagsCnt; - uint Nhfb,Nlzb,MaxDist3; + private readonly ushort[] ChSet = new ushort[256],ChSetA = new ushort[256],ChSetB = new ushort[256],ChSetC = new ushort[256]; + private readonly byte[] NToPl = new byte[256],NToPlB = new byte[256],NToPlC = new byte[256]; + private uint FlagBuf,AvrPlc,AvrPlcB,AvrLn1,AvrLn2,AvrLn3; + private int Buf60,NumHuf,StMode,LCount,FlagsCnt; + + private uint Nhfb,Nlzb,MaxDist3; /***************************** Unpack v 1.5 *********************************/ /***************************** Unpack v 2.0 *********************************/ //void Unpack20(bool Solid); - DecodeTable[] MD = new DecodeTable[4]; // Decode multimedia data, up to 4 channels. + private DecodeTable[] MD = new DecodeTable[4]; // Decode multimedia data, up to 4 channels. - readonly byte[] UnpOldTable20 = new byte[MC20*4]; - bool UnpAudioBlock; - uint UnpChannels,UnpCurChannel; - int UnpChannelDelta; + private readonly byte[] UnpOldTable20 = new byte[MC20*4]; + private bool UnpAudioBlock; + private uint UnpChannels,UnpCurChannel; + + private int UnpChannelDelta; //void CopyString20(uint Length,uint Distance); //bool ReadTables20(); //void UnpWriteBuf20(); //void UnpInitData20(int Solid); //void ReadLastTables(); //byte DecodeAudio(int Delta); - AudioVariables[] AudV = new AudioVariables[4]; + private AudioVariables[] AudV = new AudioVariables[4]; /***************************** Unpack v 2.0 *********************************/ /***************************** Unpack v 3.0 *********************************/ @@ -372,22 +370,22 @@ internal partial class Unpack //void UnpWriteBuf30(); //void ExecuteCode(VM_PreparedProgram *Prg); - int PrevLowDist,LowDistRepCount; + private int PrevLowDist,LowDistRepCount; /*#if !RarV2017_RAR5ONLY ModelPPM PPM; #endif*/ - int PPMEscChar; + private int PPMEscChar; - readonly byte [] UnpOldTable = new byte[HUFF_TABLE_SIZE30]; - int UnpBlockType; + private readonly byte [] UnpOldTable = new byte[HUFF_TABLE_SIZE30]; + private int UnpBlockType; // If we already read decoding tables for Unpack v2,v3,v5. // We should not use a single variable for all algorithm versions, // because we can have a corrupt archive with one algorithm file // followed by another algorithm file with "solid" flag and we do not // want to reuse tables from one algorithm in another. - bool TablesRead2,TablesRead3,TablesRead5; + private bool TablesRead2,TablesRead3,TablesRead5; // Virtual machine to execute filters code. /*#if !RarV2017_RAR5ONLY @@ -396,29 +394,29 @@ internal partial class Unpack // Buffer to read VM filters code. We moved it here from AddVMCode // function to reduce time spent in BitInput constructor. - readonly BitInput VMCodeInp = new BitInput(true); + private readonly BitInput VMCodeInp = new BitInput(true); // Filters code, one entry per filter. - readonly List Filters30 = new List(); + private readonly List Filters30 = new List(); // Filters stack, several entrances of same filter are possible. - readonly List PrgStack = new List(); + private readonly List PrgStack = new List(); // Lengths of preceding data blocks, one length of one last block // for every filter. Used to reduce the size required to write // the data block length if lengths are repeating. - readonly List OldFilterLengths = new List(); + private readonly List OldFilterLengths = new List(); - int LastFilter; + private int LastFilter; /***************************** Unpack v 3.0 *********************************/ //Unpack(ComprDataIO *DataIO); //~Unpack(); //void Init(size_t WinSize,bool Solid); //void DoUnpack(uint Method,bool Solid); - bool IsFileExtracted() {return(FileExtracted);} - void SetDestSize(int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;} - void SetSuspended(bool Suspended) {this.Suspended=Suspended;} + private bool IsFileExtracted() {return(FileExtracted);} + private void SetDestSize(int64 DestSize) {DestUnpSize=DestSize;FileExtracted=false;} + private void SetSuspended(bool Suspended) {this.Suspended=Suspended;} #if RarV2017_RAR_SMP // More than 8 threads are unlikely to provide a noticeable gain @@ -428,10 +426,10 @@ internal partial class Unpack void UnpackDecode(UnpackThreadData &D); #endif - size_t MaxWinSize; - size_t MaxWinMask; + private size_t MaxWinSize; + private size_t MaxWinMask; - uint GetChar() + private uint GetChar() { if (Inp.InAddr>BitInput.MAX_SIZE-30) UnpReadBuf(); diff --git a/src/SharpCompress/Compressors/Xz/Crc32.cs b/src/SharpCompress/Compressors/Xz/Crc32.cs index cb65fec9..72c37ba1 100644 --- a/src/SharpCompress/Compressors/Xz/Crc32.cs +++ b/src/SharpCompress/Compressors/Xz/Crc32.cs @@ -8,7 +8,7 @@ namespace SharpCompress.Compressors.Xz public const UInt32 DefaultPolynomial = 0xedb88320u; public const UInt32 DefaultSeed = 0xffffffffu; - static UInt32[] defaultTable; + private static UInt32[] defaultTable; public static UInt32 Compute(byte[] buffer) { @@ -25,7 +25,7 @@ namespace SharpCompress.Compressors.Xz return ~CalculateHash(InitializeTable(polynomial), seed, buffer, 0, buffer.Length); } - static UInt32[] InitializeTable(UInt32 polynomial) + private static UInt32[] InitializeTable(UInt32 polynomial) { if (polynomial == DefaultPolynomial && defaultTable != null) return defaultTable; @@ -48,7 +48,7 @@ namespace SharpCompress.Compressors.Xz return createTable; } - static UInt32 CalculateHash(UInt32[] table, UInt32 seed, IList buffer, int start, int size) + private static UInt32 CalculateHash(UInt32[] table, UInt32 seed, IList buffer, int start, int size) { var crc = seed; for (var i = start; i < size - start; i++) diff --git a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs index e260a9cd..0f793dff 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs @@ -18,7 +18,7 @@ namespace SharpCompress.Compressors.Xz.Filters LZMA2 = 0x21, } - static Dictionary FilterMap = new Dictionary() + private static readonly Dictionary FilterMap = new Dictionary() { {FilterTypes.LZMA2, typeof(Lzma2Filter) } }; diff --git a/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs b/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs index 7d0020e8..970fafd3 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs @@ -9,7 +9,7 @@ namespace SharpCompress.Compressors.Xz.Filters public override bool AllowAsNonLast => false; public override bool ChangesDataSize => true; - byte _dictionarySize; + private byte _dictionarySize; public uint DictionarySize { get diff --git a/src/SharpCompress/Compressors/Xz/XZBlock.cs b/src/SharpCompress/Compressors/Xz/XZBlock.cs index 0848618b..96ecef17 100644 --- a/src/SharpCompress/Compressors/Xz/XZBlock.cs +++ b/src/SharpCompress/Compressors/Xz/XZBlock.cs @@ -13,7 +13,7 @@ namespace SharpCompress.Compressors.Xz public Stack Filters { get; private set; } = new Stack(); public bool HeaderIsLoaded { get; private set; } private CheckType _checkType; - private int _checkSize; + private readonly int _checkSize; private bool _streamConnected; private int _numFilters; private byte _blockHeaderSizeByte; diff --git a/src/SharpCompress/Compressors/Xz/XZIndex.cs b/src/SharpCompress/Compressors/Xz/XZIndex.cs index f1a66776..736f978a 100644 --- a/src/SharpCompress/Compressors/Xz/XZIndex.cs +++ b/src/SharpCompress/Compressors/Xz/XZIndex.cs @@ -15,7 +15,7 @@ namespace SharpCompress.Compressors.Xz public ulong NumberOfRecords { get; private set; } public List Records { get; } = new List(); - private bool _indexMarkerAlreadyVerified; + private readonly bool _indexMarkerAlreadyVerified; public XZIndex(BinaryReader reader, bool indexMarkerAlreadyVerified) { diff --git a/src/SharpCompress/Compressors/Xz/XZStream.cs b/src/SharpCompress/Compressors/Xz/XZStream.cs index d1de55a1..df755c26 100644 --- a/src/SharpCompress/Compressors/Xz/XZStream.cs +++ b/src/SharpCompress/Compressors/Xz/XZStream.cs @@ -40,7 +40,7 @@ namespace SharpCompress.Compressors.Xz public bool HeaderIsRead { get; private set; } private XZBlock _currentBlock; - bool _endOfStream; + private bool _endOfStream; public XZStream(Stream stream) : base(stream) { diff --git a/src/SharpCompress/Converters/DataConverter.cs b/src/SharpCompress/Converters/DataConverter.cs index 2ea5c5c9..6be04586 100644 --- a/src/SharpCompress/Converters/DataConverter.cs +++ b/src/SharpCompress/Converters/DataConverter.cs @@ -47,12 +47,12 @@ namespace SharpCompress.Converters public #endif - unsafe abstract class DataConverter + internal unsafe abstract class DataConverter { // Disables the warning: CLS compliance checking will not be performed on // `XXXX' because it is not visible from outside this assembly #pragma warning disable 3019 - static readonly DataConverter SwapConv = new SwapConverter(); + private static readonly DataConverter SwapConv = new SwapConverter(); public static readonly bool IsLittleEndian = BitConverter.IsLittleEndian; @@ -164,7 +164,7 @@ namespace SharpCompress.Converters } } - class CopyConverter : DataConverter + private class CopyConverter : DataConverter { public override double GetDouble(byte[] data, int index) { @@ -462,7 +462,7 @@ namespace SharpCompress.Converters } } - class SwapConverter : DataConverter + private class SwapConverter : DataConverter { public override double GetDouble(byte[] data, int index) { diff --git a/src/SharpCompress/Crypto/RijndaelEngine.cs b/src/SharpCompress/Crypto/RijndaelEngine.cs index 6fa73b4b..f90be37f 100644 --- a/src/SharpCompress/Crypto/RijndaelEngine.cs +++ b/src/SharpCompress/Crypto/RijndaelEngine.cs @@ -128,7 +128,7 @@ namespace Org.BouncyCastle.Crypto.Engines 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; - static readonly byte[][] shifts0 = + private static readonly byte[][] shifts0 = { new byte[] {0, 8, 16, 24}, new byte[] {0, 8, 16, 24}, @@ -137,7 +137,7 @@ namespace Org.BouncyCastle.Crypto.Engines new byte[] {0, 8, 24, 32} }; - static readonly byte[][] shifts1 = + private static readonly byte[][] shifts1 = { new byte[] {0, 24, 16, 8}, new byte[] {0, 32, 24, 16}, diff --git a/src/SharpCompress/Writers/Tar/TarWriter.cs b/src/SharpCompress/Writers/Tar/TarWriter.cs index f6f06c56..61e98ffd 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.cs @@ -12,7 +12,7 @@ namespace SharpCompress.Writers.Tar { public class TarWriter : AbstractWriter { - private bool finalizeArchiveOnClose; + private readonly bool finalizeArchiveOnClose; public TarWriter(Stream destination, TarWriterOptions options) : base(ArchiveType.Tar, options) diff --git a/tests/SharpCompress.Test/ArchiveTests.cs b/tests/SharpCompress.Test/ArchiveTests.cs index a5413891..beea1870 100644 --- a/tests/SharpCompress.Test/ArchiveTests.cs +++ b/tests/SharpCompress.Test/ArchiveTests.cs @@ -134,7 +134,7 @@ namespace SharpCompress.Test } } - void archive_CompressedBytesRead(object sender, CompressedBytesReadEventArgs e) + private void archive_CompressedBytesRead(object sender, CompressedBytesReadEventArgs e) { Console.WriteLine("Read Compressed File Part Bytes: {0} Percentage: {1}%", e.CurrentFilePartCompressedBytesRead, CreatePercentage(e.CurrentFilePartCompressedBytesRead, partTotal)); @@ -145,13 +145,13 @@ namespace SharpCompress.Test e.CompressedBytesRead, percentage); } - void archive_FilePartExtractionBegin(object sender, FilePartExtractionBeginEventArgs e) + private void archive_FilePartExtractionBegin(object sender, FilePartExtractionBeginEventArgs e) { partTotal = e.Size; Console.WriteLine("Initializing File Part Extraction: " + e.Name); } - void archive_EntryExtractionBegin(object sender, ArchiveExtractionEventArgs e) + private void archive_EntryExtractionBegin(object sender, ArchiveExtractionEventArgs e) { entryTotal = e.Item.Size; Console.WriteLine("Initializing File Entry Extraction: " + e.Item.Key); diff --git a/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs b/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs index b42c8788..c9594c1b 100644 --- a/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs +++ b/tests/SharpCompress.Test/Xz/Filters/Lzma2Tests.cs @@ -7,7 +7,7 @@ namespace SharpCompress.Test.Xz.Filters { public class Lzma2Tests : XZTestsBase { - Lzma2Filter filter; + private readonly Lzma2Filter filter; public Lzma2Tests() { diff --git a/tests/SharpCompress.Test/Zip/Zip64Tests.cs b/tests/SharpCompress.Test/Zip/Zip64Tests.cs index ffa2859b..d1e105a6 100644 --- a/tests/SharpCompress.Test/Zip/Zip64Tests.cs +++ b/tests/SharpCompress.Test/Zip/Zip64Tests.cs @@ -19,7 +19,7 @@ namespace SharpCompress.Test.Zip } // 4GiB + 1 - const long FOUR_GB_LIMIT = ((long)uint.MaxValue) + 1; + private const long FOUR_GB_LIMIT = ((long)uint.MaxValue) + 1; [Trait("format", "zip64")] public void Zip64_Single_Large_File() diff --git a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs index da17ccbf..20c76dfc 100644 --- a/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipArchiveTests.cs @@ -458,7 +458,7 @@ namespace SharpCompress.Test.Zip }); } - class NonSeekableMemoryStream : MemoryStream + private class NonSeekableMemoryStream : MemoryStream { public override bool CanSeek => false; } diff --git a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs index c1f7f701..e30c0eed 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -274,7 +274,7 @@ namespace SharpCompress.Test.Zip VerifyFiles(); } - class NonSeekableMemoryStream : MemoryStream + private class NonSeekableMemoryStream : MemoryStream { public override bool CanSeek => false; } From 173a0fe6598954964d03d86ebd8338f8442f7aee Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sat, 5 May 2018 19:35:58 +0100 Subject: [PATCH 110/124] Some naming clean up --- src/SharpCompress/Common/Entry.cs | 2 +- src/SharpCompress/Common/GZip/GZipFilePart.cs | 2 +- .../Common/Rar/Headers/AVHeader.cs | 11 +- .../Common/Rar/Headers/ArchiveCryptHeader.cs | 24 +-- .../Common/Rar/Headers/ArchiveHeader.cs | 14 +- .../Common/Rar/Headers/CommentHeader.cs | 4 +- .../Common/Rar/Headers/EndArchiveHeader.cs | 4 +- .../Common/Rar/Headers/FileHeader.cs | 44 +++--- src/SharpCompress/Common/Rar/Headers/Flags.cs | 148 +++++++++--------- .../Common/Rar/Headers/MarkHeader.cs | 8 +- .../Common/Rar/Headers/NewSubHeaderType.cs | 12 +- .../Common/Rar/Headers/RarHeader.cs | 22 +-- .../Common/Rar/Headers/RarHeaderFactory.cs | 26 +-- .../Common/Rar/RarCrcBinaryReader.cs | 26 +-- .../Common/Rar/RarCryptoBinaryReader.cs | 66 ++++---- .../Common/Rar/RarCryptoWrapper.cs | 54 +++---- src/SharpCompress/Common/Rar/RarRijndael.cs | 38 ++--- src/SharpCompress/Common/Rar/RarVolume.cs | 8 +- .../Common/SevenZip/SevenZipFilePart.cs | 4 +- src/SharpCompress/Common/Tar/TarFilePart.cs | 2 +- .../Common/Zip/SeekableZipFilePart.cs | 2 +- src/SharpCompress/Compressors/LZMA/Log.cs | 8 +- .../Compressors/LZMA/LzmaDecoder.cs | 78 ++++----- .../Compressors/LZMA/LzmaEncoder.cs | 6 +- .../Compressors/Rar/RarCrcStream.cs | 2 +- .../Compressors/Rar/RarStream.cs | 10 +- .../Compressors/Rar/UnpackV1/Unpack.cs | 12 +- .../Compressors/Rar/UnpackV1/Unpack50.cs | 34 ++-- .../Compressors/Rar/UnpackV2017/Unpack.cs | 18 +-- .../Rar/UnpackV2017/Unpack.unpack15_cpp.cs | 4 +- .../Rar/UnpackV2017/Unpack.unpack20_cpp.cs | 4 +- .../Rar/UnpackV2017/Unpack.unpack50_cpp.cs | 6 +- .../Rar/UnpackV2017/Unpack.unpack_cpp.cs | 2 +- .../Compressors/Rar/UnpackV2017/unpack_hpp.cs | 12 +- .../Compressors/Rar/VM/BitInput.cs | 4 +- .../Compressors/Xz/Filters/BlockFilter.cs | 8 +- .../Compressors/Xz/Filters/Lzma2Filter.cs | 3 +- src/SharpCompress/Compressors/Xz/XZFooter.cs | 4 +- src/SharpCompress/Compressors/Xz/XZHeader.cs | 2 +- .../Readers/Rar/MultiVolumeRarReader.cs | 2 +- .../SharpCompress.Test/Rar/RarArchiveTests.cs | 12 +- .../SharpCompress.Test/Rar/RarReaderTests.cs | 28 ++-- 42 files changed, 384 insertions(+), 396 deletions(-) diff --git a/src/SharpCompress/Common/Entry.cs b/src/SharpCompress/Common/Entry.cs index 9675bdbc..7855b217 100644 --- a/src/SharpCompress/Common/Entry.cs +++ b/src/SharpCompress/Common/Entry.cs @@ -68,7 +68,7 @@ namespace SharpCompress.Common /// public override string ToString() { - return this.Key; + return Key; } internal abstract IEnumerable Parts { get; } diff --git a/src/SharpCompress/Common/GZip/GZipFilePart.cs b/src/SharpCompress/Common/GZip/GZipFilePart.cs index 2aecd3ff..5859a242 100644 --- a/src/SharpCompress/Common/GZip/GZipFilePart.cs +++ b/src/SharpCompress/Common/GZip/GZipFilePart.cs @@ -19,7 +19,7 @@ namespace SharpCompress.Common.GZip { ReadAndValidateGzipHeader(stream); EntryStartPosition = stream.Position; - this._stream = stream; + _stream = stream; } internal long EntryStartPosition { get; } diff --git a/src/SharpCompress/Common/Rar/Headers/AVHeader.cs b/src/SharpCompress/Common/Rar/Headers/AVHeader.cs index 73c1f3bd..4b521ca8 100644 --- a/src/SharpCompress/Common/Rar/Headers/AVHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/AVHeader.cs @@ -7,23 +7,24 @@ namespace SharpCompress.Common.Rar.Headers public AVHeader(RarHeader header, RarCrcBinaryReader reader) : base(header, reader, HeaderType.Av) { - if (IsRar5) throw new InvalidFormatException("unexpected rar5 record"); + if (IsRar5) + throw new InvalidFormatException("unexpected rar5 record"); } protected override void ReadFinish(MarkingBinaryReader reader) { UnpackVersion = reader.ReadByte(); Method = reader.ReadByte(); - AVVersion = reader.ReadByte(); - AVInfoCRC = reader.ReadInt32(); + AvVersion = reader.ReadByte(); + AvInfoCrc = reader.ReadInt32(); } - internal int AVInfoCRC { get; private set; } + internal int AvInfoCrc { get; private set; } internal byte UnpackVersion { get; private set; } internal byte Method { get; private set; } - internal byte AVVersion { get; private set; } + internal byte AvVersion { get; private set; } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs index 0ffaed01..389a0697 100644 --- a/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveCryptHeader.cs @@ -15,11 +15,11 @@ namespace SharpCompress.Common.Rar.Headers private const int CRYPT5_KDF_LG2_COUNT_MAX = 24; // LOG2 of maximum accepted iteration count. - private bool UsePswCheck; - private uint Lg2Count; // Log2 of PBKDF2 repetition count. - private byte[] Salt; - private byte[] PswCheck; - private byte[] PswCheckCsm; + private bool _usePswCheck; + private uint _lg2Count; // Log2 of PBKDF2 repetition count. + private byte[] _salt; + private byte[] _pswCheck; + private byte[] _pswCheckCsm; public ArchiveCryptHeader(RarHeader header, RarCrcBinaryReader reader) : base(header, reader, HeaderType.Crypt) @@ -35,22 +35,22 @@ namespace SharpCompress.Common.Rar.Headers return; } var encryptionFlags = reader.ReadRarVIntUInt32(); - UsePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); - Lg2Count = reader.ReadRarVIntByte(1); + _usePswCheck = FlagUtility.HasFlag(encryptionFlags, EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); + _lg2Count = reader.ReadRarVIntByte(1); //UsePswCheck = HasHeaderFlag(EncryptionFlagsV5.CHFL_CRYPT_PSWCHECK); - if (Lg2Count > CRYPT5_KDF_LG2_COUNT_MAX) + if (_lg2Count > CRYPT5_KDF_LG2_COUNT_MAX) { //error? return; } - Salt = reader.ReadBytes(SIZE_SALT50); - if (UsePswCheck) + _salt = reader.ReadBytes(SIZE_SALT50); + if (_usePswCheck) { - PswCheck = reader.ReadBytes(SIZE_PSWCHECK); - PswCheckCsm = reader.ReadBytes(SIZE_PSWCHECK_CSUM); + _pswCheck = reader.ReadBytes(SIZE_PSWCHECK); + _pswCheckCsm = reader.ReadBytes(SIZE_PSWCHECK_CSUM); } } } diff --git a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs index bc4955d0..72b4bd4b 100644 --- a/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/ArchiveHeader.cs @@ -14,7 +14,7 @@ namespace SharpCompress.Common.Rar.Headers if (IsRar5) { Flags = reader.ReadRarVIntUInt16(); - if (HasFlag(ArchiveFlagsV5.HasVolumeNumber)) + if (HasFlag(ArchiveFlagsV5.HAS_VOLUME_NUMBER)) { VolumeNumber = (int)reader.ReadRarVIntUInt32(); } @@ -28,7 +28,7 @@ namespace SharpCompress.Common.Rar.Headers Flags = HeaderFlags; HighPosAv = reader.ReadInt16(); PosAv = reader.ReadInt32(); - if (HasFlag(ArchiveFlagsV4.EncryptVer)) + if (HasFlag(ArchiveFlagsV4.ENCRYPT_VER)) { EncryptionVersion = reader.ReadByte(); } @@ -67,15 +67,15 @@ namespace SharpCompress.Common.Rar.Headers private byte? EncryptionVersion { get; set; } - public bool? IsEncrypted => IsRar5 ? (bool?)null : HasFlag(ArchiveFlagsV4.Password); + public bool? IsEncrypted => IsRar5 ? (bool?)null : HasFlag(ArchiveFlagsV4.PASSWORD); - public bool OldNumberingFormat => !IsRar5 && !HasFlag(ArchiveFlagsV4.NewNumbering); + public bool OldNumberingFormat => !IsRar5 && !HasFlag(ArchiveFlagsV4.NEW_NUMBERING); - public bool IsVolume => HasFlag(IsRar5 ? ArchiveFlagsV5.Volume : ArchiveFlagsV4.Volume); + public bool IsVolume => HasFlag(IsRar5 ? ArchiveFlagsV5.VOLUME : ArchiveFlagsV4.VOLUME); // RAR5: Volume number field is present. True for all volumes except first. - public bool IsFirstVolume => IsRar5 ? VolumeNumber == null : HasFlag(ArchiveFlagsV4.FirstVolume); + public bool IsFirstVolume => IsRar5 ? VolumeNumber == null : HasFlag(ArchiveFlagsV4.FIRST_VOLUME); - public bool IsSolid => HasFlag(IsRar5 ? ArchiveFlagsV5.Solid : ArchiveFlagsV4.Solid); + public bool IsSolid => HasFlag(IsRar5 ? ArchiveFlagsV5.SOLID : ArchiveFlagsV4.SOLID); } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs b/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs index d15a1b8e..4845ce2b 100644 --- a/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/CommentHeader.cs @@ -15,7 +15,7 @@ namespace SharpCompress.Common.Rar.Headers UnpSize = reader.ReadInt16(); UnpVersion = reader.ReadByte(); UnpMethod = reader.ReadByte(); - CommCRC = reader.ReadInt16(); + CommCrc = reader.ReadInt16(); } internal short UnpSize { get; private set; } @@ -23,6 +23,6 @@ namespace SharpCompress.Common.Rar.Headers internal byte UnpVersion { get; private set; } internal byte UnpMethod { get; private set; } - internal short CommCRC { get; private set; } + internal short CommCrc { get; private set; } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs b/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs index 07d1d95e..926a8dfe 100644 --- a/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/EndArchiveHeader.cs @@ -18,11 +18,11 @@ namespace SharpCompress.Common.Rar.Headers else { Flags = HeaderFlags; - if (HasFlag(EndArchiveFlagsV4.DataCrc)) + if (HasFlag(EndArchiveFlagsV4.DATA_CRC)) { ArchiveCrc = reader.ReadInt32(); } - if (HasFlag(EndArchiveFlagsV4.VolumeNumber)) + if (HasFlag(EndArchiveFlagsV4.VOLUME_NUMBER)) { VolumeNumber = reader.ReadInt16(); } diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index 39b238ab..829083e9 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -17,7 +17,7 @@ namespace SharpCompress.Common.Rar.Headers { internal class FileHeader : RarHeader { - private uint fileCrc; + private uint _fileCrc; public FileHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) : base(header, reader, headerType) @@ -43,15 +43,15 @@ namespace SharpCompress.Common.Rar.Headers var lvalue = checked((long)reader.ReadRarVInt()); // long.MaxValue causes the unpack code to finish when the input stream is exhausted - UncompressedSize = HasFlag(FileFlagsV5.UnpackedSizeUnknown) ? long.MaxValue : lvalue; + UncompressedSize = HasFlag(FileFlagsV5.UNPACKED_SIZE_UNKNOWN) ? long.MaxValue : lvalue; FileAttributes = reader.ReadRarVIntUInt32(); - if (HasFlag(FileFlagsV5.HasModTime)) { + if (HasFlag(FileFlagsV5.HAS_MOD_TIME)) { FileLastModifiedTime = Utility.UnixTimeToDateTime(reader.ReadUInt32()); } - if (HasFlag(FileFlagsV5.HasCrc32)) { + if (HasFlag(FileFlagsV5.HAS_CRC32)) { FileCrc = reader.ReadUInt32(); } @@ -209,8 +209,8 @@ namespace SharpCompress.Common.Rar.Headers private void ReadFromReaderV4(MarkingBinaryReader reader) { Flags = HeaderFlags; - IsSolid = HasFlag(FileFlagsV4.Solid); - WindowSize = IsDirectory ? 0U : ((size_t)0x10000) << ((Flags & FileFlagsV4.WindowMask) >> 5); + IsSolid = HasFlag(FileFlagsV4.SOLID); + WindowSize = IsDirectory ? 0U : ((size_t)0x10000) << ((Flags & FileFlagsV4.WINDOW_MASK) >> 5); uint lowUncompressedSize = reader.ReadUInt32(); @@ -229,7 +229,7 @@ namespace SharpCompress.Common.Rar.Headers uint highCompressedSize = 0; uint highUncompressedkSize = 0; - if (HasFlag(FileFlagsV4.Large)) + if (HasFlag(FileFlagsV4.LARGE)) { highCompressedSize = reader.ReadUInt32(); highUncompressedkSize = reader.ReadUInt32(); @@ -254,9 +254,9 @@ namespace SharpCompress.Common.Rar.Headers switch (HeaderCode) { - case HeaderCodeV.Rar4FileHeader: + case HeaderCodeV.RAR4_FILE_HEADER: { - if (HasFlag(FileFlagsV4.Unicode)) + if (HasFlag(FileFlagsV4.UNICODE)) { int length = 0; while (length < fileNameBytes.Length @@ -281,10 +281,10 @@ namespace SharpCompress.Common.Rar.Headers FileName = ConvertPathV4(FileName); } break; - case HeaderCodeV.Rar4NewSubHeader: + case HeaderCodeV.RAR4_NEW_SUB_HEADER: { int datasize = HeaderSize - newLhdSize - nameSize; - if (HasFlag(FileFlagsV4.Salt)) + if (HasFlag(FileFlagsV4.SALT)) { datasize -= saltSize; } @@ -302,11 +302,11 @@ namespace SharpCompress.Common.Rar.Headers break; } - if (HasFlag(FileFlagsV4.Salt)) + if (HasFlag(FileFlagsV4.SALT)) { R4Salt = reader.ReadBytes(saltSize); } - if (HasFlag(FileFlagsV4.ExtTime)) + if (HasFlag(FileFlagsV4.EXT_TIME)) { // verify that the end of the header hasn't been reached before reading the Extended Time. // some tools incorrectly omit Extended Time despite specifying FileFlags.EXTTIME, which most parsers tolerate. @@ -337,8 +337,8 @@ namespace SharpCompress.Common.Rar.Headers } if (i != 0) { - uint DosTime = reader.ReadUInt32(); - time = Utility.DosDateToDateTime(DosTime); + uint dosTime = reader.ReadUInt32(); + time = Utility.DosDateToDateTime(dosTime); } if ((rmode & 4) == 0) { @@ -388,13 +388,13 @@ namespace SharpCompress.Common.Rar.Headers internal uint FileCrc { get { - if (IsRar5 && !HasFlag(FileFlagsV5.HasCrc32)) { + if (IsRar5 && !HasFlag(FileFlagsV5.HAS_CRC32)) { //!!! rar5: throw new InvalidOperationException("TODO rar5"); } - return this.fileCrc; + return _fileCrc; } - private set { this.fileCrc = value; } + private set => _fileCrc = value; } // 0 - storing @@ -404,7 +404,7 @@ namespace SharpCompress.Common.Rar.Headers // 4 - good compression // 5 - best compression internal byte CompressionMethod { get; private set; } - internal bool IsStored { get { return CompressionMethod == 0; } } + internal bool IsStored => CompressionMethod == 0; // eg (see DoUnpack()) //case 15: // rar 1.5 compression @@ -431,12 +431,12 @@ namespace SharpCompress.Common.Rar.Headers internal long DataStartPosition { get; set; } public Stream PackedStream { get; set; } - public bool IsSplitAfter => IsRar5 ? HasHeaderFlag(HeaderFlagsV5.SplitAfter) : HasFlag(FileFlagsV4.SplitAfter); + public bool IsSplitAfter => IsRar5 ? HasHeaderFlag(HeaderFlagsV5.SPLIT_AFTER) : HasFlag(FileFlagsV4.SPLIT_AFTER); - public bool IsDirectory => HasFlag(IsRar5 ? FileFlagsV5.Directory : FileFlagsV4.Directory); + public bool IsDirectory => HasFlag(IsRar5 ? FileFlagsV5.DIRECTORY : FileFlagsV4.DIRECTORY); //!!! TODO rar5 - public bool IsEncrypted => HasFlag(FileFlagsV4.Password); + public bool IsEncrypted => HasFlag(FileFlagsV4.PASSWORD); internal DateTime? FileLastModifiedTime { get; private set; } diff --git a/src/SharpCompress/Common/Rar/Headers/Flags.cs b/src/SharpCompress/Common/Rar/Headers/Flags.cs index 488b3c5b..0c19079b 100644 --- a/src/SharpCompress/Common/Rar/Headers/Flags.cs +++ b/src/SharpCompress/Common/Rar/Headers/Flags.cs @@ -18,27 +18,27 @@ namespace SharpCompress.Common.Rar.Headers internal static class HeaderCodeV { - public const byte Rar4MarkHeader = 0x72; - public const byte Rar4ArchiveHeader = 0x73; - public const byte Rar4FileHeader = 0x74; - public const byte Rar4CommentHeader = 0x75; - public const byte Rar4AvHeader = 0x76; - public const byte Rar4SubHeader = 0x77; - public const byte Rar4ProtectHeader = 0x78; - public const byte Rar4SignHeader = 0x79; - public const byte Rar4NewSubHeader = 0x7a; - public const byte Rar4EndArchiveHeader = 0x7b; + public const byte RAR4_MARK_HEADER = 0x72; + public const byte RAR4_ARCHIVE_HEADER = 0x73; + public const byte RAR4_FILE_HEADER = 0x74; + public const byte RAR4_COMMENT_HEADER = 0x75; + public const byte RAR4_AV_HEADER = 0x76; + public const byte RAR4_SUB_HEADER = 0x77; + public const byte RAR4_PROTECT_HEADER = 0x78; + public const byte RAR4_SIGN_HEADER = 0x79; + public const byte RAR4_NEW_SUB_HEADER = 0x7a; + public const byte RAR4_END_ARCHIVE_HEADER = 0x7b; - public const byte Rar5ArchiveHeader = 0x01; - public const byte Rar5FileHeader = 0x02; - public const byte Rar5ServiceHeader = 0x03; - public const byte Rar5ArchiveEncryptionHeader = 0x04; - public const byte Rar5EndArchiveHeader = 0x05; + public const byte RAR5_ARCHIVE_HEADER = 0x01; + public const byte RAR5_FILE_HEADER = 0x02; + public const byte RAR5_SERVICE_HEADER = 0x03; + public const byte RAR5_ARCHIVE_ENCRYPTION_HEADER = 0x04; + public const byte RAR5_END_ARCHIVE_HEADER = 0x05; } internal static class HeaderFlagsV4 { - public const ushort HasData = 0x8000; + public const ushort HAS_DATA = 0x8000; } internal static class EncryptionFlagsV5 @@ -52,98 +52,98 @@ namespace SharpCompress.Common.Rar.Headers internal static class HeaderFlagsV5 { - public const ushort HasExtra = 0x0001; - public const ushort HasData = 0x0002; - public const ushort Keep = 0x0004; // block must be kept during an update - public const ushort SplitBefore = 0x0008; - public const ushort SplitAfter = 0x0010; - public const ushort Child = 0x0020; // ??? Block depends on preceding file block. - public const ushort PreserveChild = 0x0040; // ???? Preserve a child block if host block is modified + public const ushort HAS_EXTRA = 0x0001; + public const ushort HAS_DATA = 0x0002; + public const ushort KEEP = 0x0004; // block must be kept during an update + public const ushort SPLIT_BEFORE = 0x0008; + public const ushort SPLIT_AFTER = 0x0010; + public const ushort CHILD = 0x0020; // ??? Block depends on preceding file block. + public const ushort PRESERVE_CHILD = 0x0040; // ???? Preserve a child block if host block is modified } internal static class ArchiveFlagsV4 { - public const ushort Volume = 0x0001; - public const ushort Comment = 0x0002; - public const ushort Lock = 0x0004; - public const ushort Solid = 0x0008; - public const ushort NewNumbering = 0x0010; + public const ushort VOLUME = 0x0001; + public const ushort COMMENT = 0x0002; + public const ushort LOCK = 0x0004; + public const ushort SOLID = 0x0008; + public const ushort NEW_NUMBERING = 0x0010; public const ushort AV = 0x0020; - public const ushort Protect = 0x0040; - public const ushort Password = 0x0080; - public const ushort FirstVolume = 0x0100; - public const ushort EncryptVer = 0x0200; + public const ushort PROTECT = 0x0040; + public const ushort PASSWORD = 0x0080; + public const ushort FIRST_VOLUME = 0x0100; + public const ushort ENCRYPT_VER = 0x0200; } internal static class ArchiveFlagsV5 { - public const ushort Volume = 0x0001; - public const ushort HasVolumeNumber = 0x0002; - public const ushort Solid = 0x0004; - public const ushort Protect = 0x0008; - public const ushort Lock = 0x0010; + public const ushort VOLUME = 0x0001; + public const ushort HAS_VOLUME_NUMBER = 0x0002; + public const ushort SOLID = 0x0004; + public const ushort PROTECT = 0x0008; + public const ushort LOCK = 0x0010; } internal static class HostOsV4 { - public const byte MsDos = 0; - public const byte Os2 = 1; - public const byte Win32 = 2; - public const byte Unix = 3; - public const byte MacOs = 4; - public const byte BeOs = 5; + public const byte MS_DOS = 0; + public const byte OS2 = 1; + public const byte WIN32 = 2; + public const byte UNIX = 3; + public const byte MAC_OS = 4; + public const byte BE_OS = 5; } internal static class HostOsV5 { - public const byte Windows = 0; - public const byte Unix = 1; + public const byte WINDOWS = 0; + public const byte UNIX = 1; } internal static class FileFlagsV4 { - public const ushort SplitBefore = 0x0001; - public const ushort SplitAfter = 0x0002; - public const ushort Password = 0x0004; - public const ushort Comment = 0x0008; - public const ushort Solid = 0x0010; + public const ushort SPLIT_BEFORE = 0x0001; + public const ushort SPLIT_AFTER = 0x0002; + public const ushort PASSWORD = 0x0004; + public const ushort COMMENT = 0x0008; + public const ushort SOLID = 0x0010; - public const ushort WindowMask = 0x00e0; - public const ushort Window64 = 0x0000; - public const ushort Window128 = 0x0020; - public const ushort Window256 = 0x0040; - public const ushort Window512 = 0x0060; - public const ushort Window1024 = 0x0080; - public const ushort Window2048 = 0x00a0; - public const ushort Window4096 = 0x00c0; - public const ushort Directory = 0x00e0; + public const ushort WINDOW_MASK = 0x00e0; + public const ushort WINDOW64 = 0x0000; + public const ushort WINDOW128 = 0x0020; + public const ushort WINDOW256 = 0x0040; + public const ushort WINDOW512 = 0x0060; + public const ushort WINDOW1024 = 0x0080; + public const ushort WINDOW2048 = 0x00a0; + public const ushort WINDOW4096 = 0x00c0; + public const ushort DIRECTORY = 0x00e0; - public const ushort Large = 0x0100; - public const ushort Unicode = 0x0200; - public const ushort Salt = 0x0400; - public const ushort Version = 0x0800; - public const ushort ExtTime = 0x1000; - public const ushort ExtFlags = 0x2000; + public const ushort LARGE = 0x0100; + public const ushort UNICODE = 0x0200; + public const ushort SALT = 0x0400; + public const ushort VERSION = 0x0800; + public const ushort EXT_TIME = 0x1000; + public const ushort EXT_FLAGS = 0x2000; } internal static class FileFlagsV5 { - public const ushort Directory = 0x0001; - public const ushort HasModTime = 0x0002; - public const ushort HasCrc32 = 0x0004; - public const ushort UnpackedSizeUnknown = 0x0008; + public const ushort DIRECTORY = 0x0001; + public const ushort HAS_MOD_TIME = 0x0002; + public const ushort HAS_CRC32 = 0x0004; + public const ushort UNPACKED_SIZE_UNKNOWN = 0x0008; } internal static class EndArchiveFlagsV4 { - public const ushort NextVolume = 0x0001; - public const ushort DataCrc = 0x0002; - public const ushort RevSpace = 0x0004; - public const ushort VolumeNumber = 0x0008; + public const ushort NEXT_VOLUME = 0x0001; + public const ushort DATA_CRC = 0x0002; + public const ushort REV_SPACE = 0x0004; + public const ushort VOLUME_NUMBER = 0x0008; } internal static class EndArchiveFlagsV5 { - public const ushort HasNextVolume = 0x0001; + public const ushort HAS_NEXT_VOLUME = 0x0001; } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs index c7b94149..3a27cf69 100644 --- a/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/MarkHeader.cs @@ -5,7 +5,7 @@ namespace SharpCompress.Common.Rar.Headers { internal class MarkHeader : IRarHeader { - private const int MaxSfxSize = 0x80000 - 16; //archive.cpp line 136 + private const int MAX_SFX_SIZE = 0x80000 - 16; //archive.cpp line 136 internal bool OldNumberingFormat { get; private set; } @@ -16,9 +16,7 @@ namespace SharpCompress.Common.Rar.Headers IsRar5 = isRar5; } - public HeaderType HeaderType { - get { return HeaderType.Mark; } - } + public HeaderType HeaderType => HeaderType.Mark; private static byte GetByte(Stream stream) { @@ -32,7 +30,7 @@ namespace SharpCompress.Common.Rar.Headers public static MarkHeader Read(Stream stream, bool leaveStreamOpen, bool lookForHeader) { - int maxScanIndex = lookForHeader ? MaxSfxSize : 0; + int maxScanIndex = lookForHeader ? MAX_SFX_SIZE : 0; try { int start = -1; diff --git a/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs b/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs index d4b2a77b..220c53d0 100644 --- a/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs +++ b/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs @@ -20,26 +20,26 @@ namespace SharpCompress.Common.Rar.Headers //internal static final NewSubHeaderType SUBHEAD_TYPE_BEOSEA = new NewSubHeaderType(new byte[]{'E','A','B','E'}); - private readonly byte[] bytes; + private readonly byte[] _bytes; private NewSubHeaderType(params char[] chars) { - bytes = new byte[chars.Length]; + _bytes = new byte[chars.Length]; for (int i = 0; i < chars.Length; ++i) { - bytes[i] = (byte)chars[i]; + _bytes[i] = (byte)chars[i]; } } internal bool Equals(byte[] bytes) { - if (this.bytes.Length != bytes.Length) + if (this._bytes.Length != bytes.Length) { return false; } for (int i = 0; i < bytes.Length; ++i) { - if (this.bytes[i] != bytes[i]) + if (this._bytes[i] != bytes[i]) { return false; } @@ -49,7 +49,7 @@ namespace SharpCompress.Common.Rar.Headers public bool Equals(NewSubHeaderType other) { - return Equals(other.bytes); + return Equals(other._bytes); } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index a3fe6a91..3171b539 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -8,8 +8,8 @@ namespace SharpCompress.Common.Rar.Headers // https://www.rarlab.com/technote.htm internal class RarHeader : IRarHeader { - private readonly HeaderType headerType; - private readonly bool isRar5; + private readonly HeaderType _headerType; + private readonly bool _isRar5; internal static RarHeader TryReadBase(RarCrcBinaryReader reader, bool isRar5, ArchiveEncoding archiveEncoding) { @@ -25,8 +25,8 @@ namespace SharpCompress.Common.Rar.Headers private RarHeader(RarCrcBinaryReader reader, bool isRar5, ArchiveEncoding archiveEncoding) { - this.headerType = HeaderType.Null; - this.isRar5 = isRar5; + _headerType = HeaderType.Null; + this._isRar5 = isRar5; ArchiveEncoding = archiveEncoding; if (IsRar5) { @@ -37,11 +37,11 @@ namespace SharpCompress.Common.Rar.Headers HeaderCode = reader.ReadRarVIntByte(); HeaderFlags = reader.ReadRarVIntUInt16(2); - if (HasHeaderFlag(HeaderFlagsV5.HasExtra)) + if (HasHeaderFlag(HeaderFlagsV5.HAS_EXTRA)) { ExtraSize = reader.ReadRarVIntUInt32(); } - if (HasHeaderFlag(HeaderFlagsV5.HasData)) + if (HasHeaderFlag(HeaderFlagsV5.HAS_DATA)) { AdditionalDataSize = (long)reader.ReadRarVInt(); } @@ -52,7 +52,7 @@ namespace SharpCompress.Common.Rar.Headers HeaderCode = reader.ReadByte(); HeaderFlags = reader.ReadUInt16(); HeaderSize = reader.ReadInt16(); - if (HasHeaderFlag(HeaderFlagsV4.HasData)) + if (HasHeaderFlag(HeaderFlagsV4.HAS_DATA)) { AdditionalDataSize = reader.ReadUInt32(); } @@ -60,8 +60,8 @@ namespace SharpCompress.Common.Rar.Headers } protected RarHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) { - this.headerType = headerType; - this.isRar5 = header.IsRar5; + this._headerType = headerType; + _isRar5 = header.IsRar5; HeaderCrc = header.HeaderCrc; HeaderCode = header.HeaderCode; HeaderFlags = header.HeaderFlags; @@ -98,9 +98,9 @@ namespace SharpCompress.Common.Rar.Headers } } - public HeaderType HeaderType { get { return this.headerType; } } + public HeaderType HeaderType => _headerType; - protected bool IsRar5 { get { return this.isRar5; } } + protected bool IsRar5 => _isRar5; protected uint HeaderCrc { get; } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index 0f562314..8c4f34f7 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -8,7 +8,7 @@ namespace SharpCompress.Common.Rar.Headers { internal class RarHeaderFactory { - private bool isRar5; + private bool _isRar5; internal RarHeaderFactory(StreamingMode mode, ReaderOptions options) { @@ -23,7 +23,7 @@ namespace SharpCompress.Common.Rar.Headers internal IEnumerable ReadHeaders(Stream stream) { var markHeader = MarkHeader.Read(stream, Options.LeaveStreamOpen, Options.LookForHeader); - this.isRar5 = markHeader.IsRar5; + _isRar5 = markHeader.IsRar5; yield return markHeader; RarHeader header; @@ -59,15 +59,15 @@ namespace SharpCompress.Common.Rar.Headers #endif } - var header = RarHeader.TryReadBase(reader, this.isRar5, Options.ArchiveEncoding); + var header = RarHeader.TryReadBase(reader, _isRar5, Options.ArchiveEncoding); if (header == null) { return null; } switch (header.HeaderCode) { - case HeaderCodeV.Rar5ArchiveHeader: - case HeaderCodeV.Rar4ArchiveHeader: + case HeaderCodeV.RAR5_ARCHIVE_HEADER: + case HeaderCodeV.RAR4_ARCHIVE_HEADER: { var ah = new ArchiveHeader(header, reader); if (ah.IsEncrypted == true) @@ -78,7 +78,7 @@ namespace SharpCompress.Common.Rar.Headers return ah; } - case HeaderCodeV.Rar4ProtectHeader: + case HeaderCodeV.RAR4_PROTECT_HEADER: { var ph = new ProtectHeader(header, reader); // skip the recovery record data, we do not use it. @@ -103,22 +103,22 @@ namespace SharpCompress.Common.Rar.Headers return ph; } - case HeaderCodeV.Rar5ServiceHeader: + case HeaderCodeV.RAR5_SERVICE_HEADER: { var fh = new FileHeader(header, reader, HeaderType.Service); SkipData(fh, reader); return fh; } - case HeaderCodeV.Rar4NewSubHeader: + case HeaderCodeV.RAR4_NEW_SUB_HEADER: { var fh = new FileHeader(header, reader, HeaderType.NewSub); SkipData(fh, reader); return fh; } - case HeaderCodeV.Rar5FileHeader: - case HeaderCodeV.Rar4FileHeader: + case HeaderCodeV.RAR5_FILE_HEADER: + case HeaderCodeV.RAR4_FILE_HEADER: { var fh = new FileHeader(header, reader, HeaderType.File); switch (StreamingMode) @@ -153,12 +153,12 @@ namespace SharpCompress.Common.Rar.Headers } return fh; } - case HeaderCodeV.Rar5EndArchiveHeader: - case HeaderCodeV.Rar4EndArchiveHeader: + case HeaderCodeV.RAR5_END_ARCHIVE_HEADER: + case HeaderCodeV.RAR4_END_ARCHIVE_HEADER: { return new EndArchiveHeader(header, reader); } - case HeaderCodeV.Rar5ArchiveEncryptionHeader: + case HeaderCodeV.RAR5_ARCHIVE_ENCRYPTION_HEADER: { var ch = new ArchiveCryptHeader(header, reader); IsEncrypted = true; diff --git a/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs index 6ab2923a..ddfd8b98 100644 --- a/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCrcBinaryReader.cs @@ -2,28 +2,30 @@ using System.IO; using SharpCompress.Compressors.Rar; using SharpCompress.IO; -namespace SharpCompress.Common.Rar { - internal class RarCrcBinaryReader : MarkingBinaryReader +namespace SharpCompress.Common.Rar +{ + internal class RarCrcBinaryReader : MarkingBinaryReader { - private uint currentCrc; + private uint _currentCrc; - public RarCrcBinaryReader(Stream stream) : base(stream) + public RarCrcBinaryReader(Stream stream) + : base(stream) { } - public uint GetCrc32() + public uint GetCrc32() { - return ~currentCrc; + return ~_currentCrc; } public void ResetCrc() { - currentCrc = 0xffffffff; + _currentCrc = 0xffffffff; } - protected void UpdateCrc(byte b) + protected void UpdateCrc(byte b) { - currentCrc = RarCRC.CheckCrc(currentCrc, b); + _currentCrc = RarCRC.CheckCrc(_currentCrc, b); } protected byte[] ReadBytesNoCrc(int count) @@ -31,17 +33,17 @@ namespace SharpCompress.Common.Rar { return base.ReadBytes(count); } - public override byte ReadByte() + public override byte ReadByte() { var b = base.ReadByte(); - currentCrc = RarCRC.CheckCrc(currentCrc, b); + _currentCrc = RarCRC.CheckCrc(_currentCrc, b); return b; } public override byte[] ReadBytes(int count) { var result = base.ReadBytes(count); - currentCrc = RarCRC.CheckCrc(currentCrc, result, 0, result.Length); + _currentCrc = RarCRC.CheckCrc(_currentCrc, result, 0, result.Length); return result; } } diff --git a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs index 9ea3cb2c..a9ed1ce0 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs @@ -1,5 +1,4 @@ - -#if !NO_CRYPTO +#if !NO_CRYPTO using System.Collections.Generic; using System.IO; @@ -7,16 +6,17 @@ namespace SharpCompress.Common.Rar { internal class RarCryptoBinaryReader : RarCrcBinaryReader { - private RarRijndael rijndael; - private byte[] salt; - private readonly string password; - private readonly Queue data = new Queue(); - private long readCount; + private RarRijndael _rijndael; + private byte[] _salt; + private readonly string _password; + private readonly Queue _data = new Queue(); + private long _readCount; - public RarCryptoBinaryReader(Stream stream, string password ) + public RarCryptoBinaryReader(Stream stream, string password) : base(stream) { - this.password = password; + this._password = password; + // coderb: not sure why this was being done at this logical point //SkipQueue(); byte[] salt = ReadBytes(8); @@ -24,38 +24,36 @@ namespace SharpCompress.Common.Rar } // track read count ourselves rather than using the underlying stream since we buffer - public override long CurrentReadByteCount { - get - { - return this.readCount; - } - protected set + public override long CurrentReadByteCount + { + get => _readCount; + protected set { // ignore } } - public override void Mark() { - this.readCount = 0; - } - - protected bool UseEncryption + public override void Mark() { - get { return salt != null; } + _readCount = 0; } + private bool UseEncryption => _salt != null; + internal void InitializeAes(byte[] salt) { - this.salt = salt; - rijndael = RarRijndael.InitializeFrom(password, salt); + this._salt = salt; + _rijndael = RarRijndael.InitializeFrom(_password, salt); } - public override byte ReadByte() { + public override byte ReadByte() + { if (UseEncryption) { return ReadAndDecryptBytes(1)[0]; } - this.readCount++; + + _readCount++; return base.ReadByte(); } @@ -65,13 +63,14 @@ namespace SharpCompress.Common.Rar { return ReadAndDecryptBytes(count); } - this.readCount += count; + + _readCount += count; return base.ReadBytes(count); } private byte[] ReadAndDecryptBytes(int count) { - int queueSize = data.Count; + int queueSize = _data.Count; int sizeToRead = count - queueSize; if (sizeToRead > 0) @@ -81,9 +80,9 @@ namespace SharpCompress.Common.Rar { //long ax = System.currentTimeMillis(); byte[] cipherText = ReadBytesNoCrc(16); - var readBytes = rijndael.ProcessBlock(cipherText); + var readBytes = _rijndael.ProcessBlock(cipherText); foreach (var readByte in readBytes) - data.Enqueue(readByte); + _data.Enqueue(readByte); } } @@ -91,23 +90,24 @@ namespace SharpCompress.Common.Rar for (int i = 0; i < count; i++) { - var b = data.Dequeue(); + var b = _data.Dequeue(); decryptedBytes[i] = b; UpdateCrc(b); } - this.readCount += count; + + _readCount += count; return decryptedBytes; } public void ClearQueue() { - data.Clear(); + _data.Clear(); } public void SkipQueue() { var position = BaseStream.Position; - BaseStream.Position = position + data.Count; + BaseStream.Position = position + _data.Count; ClearQueue(); } } diff --git a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs index 2a14ae1a..2fe47ff8 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs @@ -8,16 +8,16 @@ namespace SharpCompress.Common.Rar { internal class RarCryptoWrapper : Stream { - private readonly Stream actualStream; - private readonly byte[] salt; - private RarRijndael rijndael; - private readonly Queue data = new Queue(); + private readonly Stream _actualStream; + private readonly byte[] _salt; + private RarRijndael _rijndael; + private readonly Queue _data = new Queue(); public RarCryptoWrapper(Stream actualStream, string password, byte[] salt) { - this.actualStream = actualStream; - this.salt = salt; - rijndael = RarRijndael.InitializeFrom(password, salt); + this._actualStream = actualStream; + this._salt = salt; + _rijndael = RarRijndael.InitializeFrom(password, salt); } public override void Flush() @@ -37,16 +37,16 @@ namespace SharpCompress.Common.Rar public override int Read(byte[] buffer, int offset, int count) { - if (salt == null) + if (_salt == null) { - return actualStream.Read(buffer, offset, count); + return _actualStream.Read(buffer, offset, count); } return ReadAndDecrypt(buffer, offset, count); } public int ReadAndDecrypt(byte[] buffer, int offset, int count) { - int queueSize = data.Count; + int queueSize = _data.Count; int sizeToRead = count - queueSize; if (sizeToRead > 0) @@ -56,16 +56,16 @@ namespace SharpCompress.Common.Rar { //long ax = System.currentTimeMillis(); byte[] cipherText = new byte[RarRijndael.CRYPTO_BLOCK_SIZE]; - actualStream.Read(cipherText, 0, RarRijndael.CRYPTO_BLOCK_SIZE); + _actualStream.Read(cipherText, 0, RarRijndael.CRYPTO_BLOCK_SIZE); - var readBytes = rijndael.ProcessBlock(cipherText); + var readBytes = _rijndael.ProcessBlock(cipherText); foreach (var readByte in readBytes) - data.Enqueue(readByte); + _data.Enqueue(readByte); } for (int i = 0; i < count; i++) - buffer[offset + i] = data.Dequeue(); + buffer[offset + i] = _data.Dequeue(); } return count; } @@ -75,34 +75,22 @@ namespace SharpCompress.Common.Rar throw new NotSupportedException(); } - public override bool CanRead - { - get { return true; } - } + public override bool CanRead => true; - public override bool CanSeek - { - get { return false; } - } + public override bool CanSeek => false; - public override bool CanWrite - { - get { return false; } - } + public override bool CanWrite => false; - public override long Length - { - get { throw new NotSupportedException(); } - } + public override long Length => throw new NotSupportedException(); public override long Position { get; set; } protected override void Dispose(bool disposing) { - if (rijndael != null) + if (_rijndael != null) { - rijndael.Dispose(); - rijndael = null; + _rijndael.Dispose(); + _rijndael = null; } base.Dispose(disposing); } diff --git a/src/SharpCompress/Common/Rar/RarRijndael.cs b/src/SharpCompress/Common/Rar/RarRijndael.cs index b2ce22d6..55d870d6 100644 --- a/src/SharpCompress/Common/Rar/RarRijndael.cs +++ b/src/SharpCompress/Common/Rar/RarRijndael.cs @@ -13,15 +13,15 @@ namespace SharpCompress.Common.Rar { internal const int CRYPTO_BLOCK_SIZE = 16; - private readonly string password; - private readonly byte[] salt; - private byte[] aesInitializationVector; - private RijndaelEngine rijndael; + private readonly string _password; + private readonly byte[] _salt; + private byte[] _aesInitializationVector; + private RijndaelEngine _rijndael; private RarRijndael(string password, byte[] salt) { - this.password = password; - this.salt = salt; + this._password = password; + this._salt = salt; } private byte[] ComputeHash(byte[] input) @@ -33,19 +33,19 @@ namespace SharpCompress.Common.Rar private void Initialize() { - rijndael = new RijndaelEngine(); - aesInitializationVector = new byte[CRYPTO_BLOCK_SIZE]; - int rawLength = 2*password.Length; + _rijndael = new RijndaelEngine(); + _aesInitializationVector = new byte[CRYPTO_BLOCK_SIZE]; + int rawLength = 2*_password.Length; byte[] rawPassword = new byte[rawLength + 8]; - byte[] passwordBytes = Encoding.UTF8.GetBytes(password); - for (int i = 0; i < password.Length; i++) + byte[] passwordBytes = Encoding.UTF8.GetBytes(_password); + for (int i = 0; i < _password.Length; i++) { rawPassword[i*2] = passwordBytes[i]; rawPassword[i*2 + 1] = 0; } - for (int i = 0; i < salt.Length; i++) + for (int i = 0; i < _salt.Length; i++) { - rawPassword[i + rawLength] = salt[i]; + rawPassword[i + rawLength] = _salt[i]; } @@ -65,7 +65,7 @@ namespace SharpCompress.Common.Rar if (i%(noOfRounds/CRYPTO_BLOCK_SIZE) == 0) { digest = ComputeHash(bytes.ToArray()); - aesInitializationVector[i/(noOfRounds/CRYPTO_BLOCK_SIZE)] = digest[19]; + _aesInitializationVector[i/(noOfRounds/CRYPTO_BLOCK_SIZE)] = digest[19]; } } @@ -85,7 +85,7 @@ namespace SharpCompress.Common.Rar } } - rijndael.Init(false, new KeyParameter(aesKey)); + _rijndael.Init(false, new KeyParameter(aesKey)); } @@ -100,16 +100,16 @@ namespace SharpCompress.Common.Rar { var plainText = new byte[CRYPTO_BLOCK_SIZE]; var decryptedBytes = new List(); - rijndael.ProcessBlock(cipherText, 0, plainText, 0); + _rijndael.ProcessBlock(cipherText, 0, plainText, 0); for (int j = 0; j < plainText.Length; j++) { - decryptedBytes.Add((byte) (plainText[j] ^ aesInitializationVector[j%16])); //32:114, 33:101 + decryptedBytes.Add((byte) (plainText[j] ^ _aesInitializationVector[j%16])); //32:114, 33:101 } - for (int j = 0; j < aesInitializationVector.Length; j++) + for (int j = 0; j < _aesInitializationVector.Length; j++) { - aesInitializationVector[j] = cipherText[j]; + _aesInitializationVector[j] = cipherText[j]; } return decryptedBytes.ToArray(); } diff --git a/src/SharpCompress/Common/Rar/RarVolume.cs b/src/SharpCompress/Common/Rar/RarVolume.cs index 31bc7636..265db11c 100644 --- a/src/SharpCompress/Common/Rar/RarVolume.cs +++ b/src/SharpCompress/Common/Rar/RarVolume.cs @@ -13,17 +13,17 @@ namespace SharpCompress.Common.Rar /// public abstract class RarVolume : Volume { - private readonly RarHeaderFactory headerFactory; + private readonly RarHeaderFactory _headerFactory; internal RarVolume(StreamingMode mode, Stream stream, ReaderOptions options) : base(stream, options) { - headerFactory = new RarHeaderFactory(mode, options); + _headerFactory = new RarHeaderFactory(mode, options); } internal ArchiveHeader ArchiveHeader { get; private set; } - internal StreamingMode Mode => this.headerFactory.StreamingMode; + internal StreamingMode Mode => _headerFactory.StreamingMode; internal abstract IEnumerable ReadFileParts(); @@ -32,7 +32,7 @@ namespace SharpCompress.Common.Rar internal IEnumerable GetVolumeFileParts() { MarkHeader lastMarkHeader = null; - foreach (var header in this.headerFactory.ReadHeaders(Stream)) + foreach (var header in _headerFactory.ReadHeaders(Stream)) { switch (header.HeaderType) { diff --git a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs index 97c5e8ad..f62d9e32 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs @@ -14,8 +14,8 @@ namespace SharpCompress.Common.SevenZip internal SevenZipFilePart(Stream stream, ArchiveDatabase database, int index, CFileItem fileEntry, ArchiveEncoding archiveEncoding) : base(archiveEncoding) { - this._stream = stream; - this._database = database; + _stream = stream; + _database = database; Index = index; Header = fileEntry; if (Header.HasStream) diff --git a/src/SharpCompress/Common/Tar/TarFilePart.cs b/src/SharpCompress/Common/Tar/TarFilePart.cs index 2aec15f4..dafbb867 100644 --- a/src/SharpCompress/Common/Tar/TarFilePart.cs +++ b/src/SharpCompress/Common/Tar/TarFilePart.cs @@ -11,7 +11,7 @@ namespace SharpCompress.Common.Tar internal TarFilePart(TarHeader header, Stream seekableStream) : base(header.ArchiveEncoding) { - this._seekableStream = seekableStream; + _seekableStream = seekableStream; Header = header; } diff --git a/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs b/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs index 2364eb27..d06b34aa 100644 --- a/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/SeekableZipFilePart.cs @@ -11,7 +11,7 @@ namespace SharpCompress.Common.Zip internal SeekableZipFilePart(SeekableZipHeaderFactory headerFactory, DirectoryEntryHeader header, Stream stream) : base(header, stream) { - this._headerFactory = headerFactory; + _headerFactory = headerFactory; } internal override Stream GetCompressedStream() diff --git a/src/SharpCompress/Compressors/LZMA/Log.cs b/src/SharpCompress/Compressors/LZMA/Log.cs index ba461215..458abbcc 100644 --- a/src/SharpCompress/Compressors/LZMA/Log.cs +++ b/src/SharpCompress/Compressors/LZMA/Log.cs @@ -35,7 +35,7 @@ namespace SharpCompress.Compressors.LZMA { _needsIndent = false; #if !NO_FILE - System.Diagnostics.Debug.Write(_indent.Peek()); + Debug.Write(_indent.Peek()); #endif } } @@ -44,7 +44,7 @@ namespace SharpCompress.Compressors.LZMA { EnsureIndent(); #if !NO_FILE - System.Diagnostics.Debug.Write(value); + Debug.Write(value); #endif } @@ -52,7 +52,7 @@ namespace SharpCompress.Compressors.LZMA { EnsureIndent(); #if !NO_FILE - System.Diagnostics.Debug.Write(text); + Debug.Write(text); #endif } @@ -60,7 +60,7 @@ namespace SharpCompress.Compressors.LZMA { EnsureIndent(); #if !NO_FILE - System.Diagnostics.Debug.Write(string.Format(format, args)); + Debug.Write(string.Format(format, args)); #endif } diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index 6351a06a..dbea98c3 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -40,12 +40,12 @@ namespace SharpCompress.Compressors.LZMA public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState) { - if (m_Choice.Decode(rangeDecoder) == 0) + if (_Choice.Decode(rangeDecoder) == 0) { return _LowCoder[posState].Decode(rangeDecoder); } uint symbol = Base.kNumLowLenSymbols; - if (m_Choice2.Decode(rangeDecoder) == 0) + if (_Choice2.Decode(rangeDecoder) == 0) { symbol += _MidCoder[posState].Decode(rangeDecoder); } @@ -118,7 +118,7 @@ namespace SharpCompress.Compressors.LZMA public void Create(int numPosBits, int numPrevBits) { - if (m_Coders != null && _NumPrevBits == numPrevBits && + if (_Coders != null && _NumPrevBits == numPrevBits && _NumPosBits == numPosBits) { return; @@ -126,7 +126,7 @@ namespace SharpCompress.Compressors.LZMA _NumPosBits = numPosBits; _PosMask = ((uint)1 << numPosBits) - 1; _NumPrevBits = numPrevBits; - uint numStates = (uint)1 << (m_NumPrevBits + _NumPosBits); + uint numStates = (uint)1 << (_NumPrevBits + _NumPosBits); _Coders = new Decoder2[numStates]; for (uint i = 0; i < numStates; i++) { @@ -136,7 +136,7 @@ namespace SharpCompress.Compressors.LZMA public void Init() { - uint numStates = (uint)1 << (m_NumPrevBits + _NumPosBits); + uint numStates = (uint)1 << (_NumPrevBits + _NumPosBits); for (uint i = 0; i < numStates; i++) { _Coders[i].Init(); @@ -196,12 +196,12 @@ namespace SharpCompress.Compressors.LZMA private void CreateDictionary() { - if (m_DictionarySize < 0) + if (_DictionarySize < 0) { throw new InvalidParamException(); } _OutWindow = new OutWindow(); - int blockSize = Math.Max(m_DictionarySize, (1 << 12)); + int blockSize = Math.Max(_DictionarySize, (1 << 12)); _OutWindow.Create(blockSize); } @@ -250,18 +250,18 @@ namespace SharpCompress.Compressors.LZMA _LiteralDecoder.Init(); for (i = 0; i < Base.kNumLenToPosStates; i++) { - m_PosSlotDecoder[i].Init(); + _PosSlotDecoder[i].Init(); } - // m_PosSpecDecoder.Init(); + // _PosSpecDecoder.Init(); for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++) { - m_PosDecoders[i].Init(); + _PosDecoders[i].Init(); } - m_LenDecoder.Init(); - m_RepLenDecoder.Init(); - m_PosAlignDecoder.Init(); + _LenDecoder.Init(); + _RepLenDecoder.Init(); + _PosAlignDecoder.Init(); state.Init(); rep0 = 0; @@ -273,37 +273,37 @@ namespace SharpCompress.Compressors.LZMA public void Code(Stream inStream, Stream outStream, Int64 inSize, Int64 outSize, ICodeProgress progress) { - if (m_OutWindow == null) + if (_OutWindow == null) { CreateDictionary(); } - m_OutWindow.Init(outStream); + _OutWindow.Init(outStream); if (outSize > 0) { - m_OutWindow.SetLimit(outSize); + _OutWindow.SetLimit(outSize); } else { - m_OutWindow.SetLimit(Int64.MaxValue - m_OutWindow.Total); + _OutWindow.SetLimit(Int64.MaxValue - _OutWindow.Total); } RangeCoder.Decoder rangeDecoder = new RangeCoder.Decoder(); rangeDecoder.Init(inStream); - Code(m_DictionarySize, m_OutWindow, rangeDecoder); + Code(_DictionarySize, _OutWindow, rangeDecoder); - m_OutWindow.ReleaseStream(); + _OutWindow.ReleaseStream(); rangeDecoder.ReleaseStream(); if (!rangeDecoder.IsFinished || (inSize > 0 && rangeDecoder.Total != inSize)) { throw new DataErrorException(); } - if (m_OutWindow.HasPending) + if (_OutWindow.HasPending) { throw new DataErrorException(); } - m_OutWindow = null; + _OutWindow = null; } internal bool Code(int dictionarySize, OutWindow outWindow, RangeCoder.Decoder rangeDecoder) @@ -314,20 +314,20 @@ namespace SharpCompress.Compressors.LZMA while (outWindow.HasSpace) { - uint posState = (uint)outWindow.Total & m_PosStateMask; - if (m_IsMatchDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(rangeDecoder) == 0) + uint posState = (uint)outWindow.Total & _PosStateMask; + if (_IsMatchDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(rangeDecoder) == 0) { byte b; byte prevByte = outWindow.GetByte(0); if (!state.IsCharState()) { - b = m_LiteralDecoder.DecodeWithMatchByte(rangeDecoder, + b = _LiteralDecoder.DecodeWithMatchByte(rangeDecoder, (uint)outWindow.Total, prevByte, outWindow.GetByte((int)rep0)); } else { - b = m_LiteralDecoder.DecodeNormal(rangeDecoder, (uint)outWindow.Total, prevByte); + b = _LiteralDecoder.DecodeNormal(rangeDecoder, (uint)outWindow.Total, prevByte); } outWindow.PutByte(b); state.UpdateChar(); @@ -335,12 +335,12 @@ namespace SharpCompress.Compressors.LZMA else { uint len; - if (m_IsRepDecoders[state.Index].Decode(rangeDecoder) == 1) + if (_IsRepDecoders[state.Index].Decode(rangeDecoder) == 1) { - if (m_IsRepG0Decoders[state.Index].Decode(rangeDecoder) == 0) + if (_IsRepG0Decoders[state.Index].Decode(rangeDecoder) == 0) { if ( - m_IsRep0LongDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode( + _IsRep0LongDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode( rangeDecoder) == 0) { state.UpdateShortRep(); @@ -351,13 +351,13 @@ namespace SharpCompress.Compressors.LZMA else { UInt32 distance; - if (m_IsRepG1Decoders[state.Index].Decode(rangeDecoder) == 0) + if (_IsRepG1Decoders[state.Index].Decode(rangeDecoder) == 0) { distance = rep1; } else { - if (m_IsRepG2Decoders[state.Index].Decode(rangeDecoder) == 0) + if (_IsRepG2Decoders[state.Index].Decode(rangeDecoder) == 0) { distance = rep2; } @@ -371,7 +371,7 @@ namespace SharpCompress.Compressors.LZMA rep1 = rep0; rep0 = distance; } - len = m_RepLenDecoder.Decode(rangeDecoder, posState) + Base.kMatchMinLen; + len = _RepLenDecoder.Decode(rangeDecoder, posState) + Base.kMatchMinLen; state.UpdateRep(); } else @@ -379,23 +379,23 @@ namespace SharpCompress.Compressors.LZMA rep3 = rep2; rep2 = rep1; rep1 = rep0; - len = Base.kMatchMinLen + m_LenDecoder.Decode(rangeDecoder, posState); + len = Base.kMatchMinLen + _LenDecoder.Decode(rangeDecoder, posState); state.UpdateMatch(); - uint posSlot = m_PosSlotDecoder[Base.GetLenToPosState(len)].Decode(rangeDecoder); + uint posSlot = _PosSlotDecoder[Base.GetLenToPosState(len)].Decode(rangeDecoder); if (posSlot >= Base.kStartPosModelIndex) { int numDirectBits = (int)((posSlot >> 1) - 1); rep0 = ((2 | (posSlot & 1)) << numDirectBits); if (posSlot < Base.kEndPosModelIndex) { - rep0 += BitTreeDecoder.ReverseDecode(m_PosDecoders, + rep0 += BitTreeDecoder.ReverseDecode(_PosDecoders, rep0 - posSlot - 1, rangeDecoder, numDirectBits); } else { rep0 += (rangeDecoder.DecodeDirectBits( numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits); - rep0 += m_PosAlignDecoder.ReverseDecode(rangeDecoder); + rep0 += _PosAlignDecoder.ReverseDecode(rangeDecoder); } } else @@ -436,21 +436,21 @@ namespace SharpCompress.Compressors.LZMA Init(); if (properties.Length >= 5) { - m_DictionarySize = 0; + _DictionarySize = 0; for (int i = 0; i < 4; i++) { - m_DictionarySize += properties[1 + i] << (i * 8); + _DictionarySize += properties[1 + i] << (i * 8); } } } public void Train(Stream stream) { - if (m_OutWindow == null) + if (_OutWindow == null) { CreateDictionary(); } - m_OutWindow.Train(stream); + _OutWindow.Train(stream); } /* diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs index a4e76b72..666eb690 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs @@ -162,14 +162,14 @@ namespace SharpCompress.Compressors.LZMA public void Create(int numPosBits, int numPrevBits) { - if (m_Coders != null && _NumPrevBits == numPrevBits && _NumPosBits == numPosBits) + if (_Coders != null && _NumPrevBits == numPrevBits && _NumPosBits == numPosBits) { return; } _NumPosBits = numPosBits; _PosMask = ((uint)1 << numPosBits) - 1; _NumPrevBits = numPrevBits; - uint numStates = (uint)1 << (m_NumPrevBits + _NumPosBits); + uint numStates = (uint)1 << (_NumPrevBits + _NumPosBits); _Coders = new Encoder2[numStates]; for (uint i = 0; i < numStates; i++) { @@ -179,7 +179,7 @@ namespace SharpCompress.Compressors.LZMA public void Init() { - uint numStates = (uint)1 << (m_NumPrevBits + _NumPosBits); + uint numStates = (uint)1 << (_NumPrevBits + _NumPosBits); for (uint i = 0; i < numStates; i++) { _Coders[i].Init(); diff --git a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs index df7d85ff..329587e7 100644 --- a/src/SharpCompress/Compressors/Rar/RarCrcStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarCrcStream.cs @@ -33,7 +33,7 @@ namespace SharpCompress.Compressors.Rar { currentCrc = RarCRC.CheckCrc(currentCrc, buffer, offset, result); } - else if (GetCrc() != this.readStream.CurrentCrc && count != 0) + else if (GetCrc() != readStream.CurrentCrc && count != 0) { // NOTE: we use the last FileHeader in a multipart volume to check CRC throw new InvalidFormatException("file crc mismatch"); diff --git a/src/SharpCompress/Compressors/Rar/RarStream.cs b/src/SharpCompress/Compressors/Rar/RarStream.cs index 08240859..5d4f13fa 100644 --- a/src/SharpCompress/Compressors/Rar/RarStream.cs +++ b/src/SharpCompress/Compressors/Rar/RarStream.cs @@ -27,17 +27,17 @@ namespace SharpCompress.Compressors.Rar this.unpack = unpack; this.fileHeader = fileHeader; this.readStream = readStream; - this.fetch = true; + fetch = true; unpack.DoUnpack(fileHeader, readStream, this); - this.fetch = false; + fetch = false; } protected override void Dispose(bool disposing) { - if (!this.isDisposed) { - this.isDisposed = true; + if (!isDisposed) { + isDisposed = true; base.Dispose(disposing); - this.readStream.Dispose(); + readStream.Dispose(); } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs index 90c09c6d..96798e46 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs @@ -16,7 +16,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 public Unpack() { // to ease in porting Unpack50.cs - this.Inp = this; + Inp = this; } @@ -33,8 +33,8 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 } public bool Suspended { - get => this.suspended; - set => this.suspended = value; + get => suspended; + set => suspended = value; } public int Char @@ -105,7 +105,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) { - this.destUnpSize = fileHeader.UncompressedSize; + destUnpSize = fileHeader.UncompressedSize; this.fileHeader = fileHeader; this.readStream = readStream; this.writeStream = writeStream; @@ -113,7 +113,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 { Init(null); } - this.suspended = false; + suspended = false; DoUnpack(); } @@ -145,7 +145,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 break; default: - throw new InvalidFormatException("unknown rar compression version " + this.fileHeader.CompressionAlgorithm); + throw new InvalidFormatException("unknown rar compression version " + fileHeader.CompressionAlgorithm); } } diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs index ec3fbdc1..274692cf 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack50.cs @@ -110,19 +110,19 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 private const int MaxWinMask = PackDef.MAXWINMASK; // TODO: rename var - private int UnpPtr { get { return this.unpPtr; } set { this.unpPtr = value; } } - private int ReadBorder { get { return this.readBorder; } set { this.readBorder = value; } } - private long DestUnpSize { get { return this.destUnpSize; } set { this.destUnpSize = value; } } - private long WrittenFileSize { get { return this.writtenFileSize; } set { this.writtenFileSize = value; } } - private byte[] Window{ get { return this.window; } } - private uint LastLength { get { return (uint)this.lastLength; } set { this.lastLength = (int)value; } } - private uint OldDistN(int i) { return (uint)this.oldDist[i]; } - private void SetOldDistN(int i, uint value) { this.oldDist[i] = (int)value; } - private int WrPtr { get { return this.wrPtr; } set { this.wrPtr = value; } } + private int UnpPtr { get { return unpPtr; } set { unpPtr = value; } } + private int ReadBorder { get { return readBorder; } set { readBorder = value; } } + private long DestUnpSize { get { return destUnpSize; } set { destUnpSize = value; } } + private long WrittenFileSize { get { return writtenFileSize; } set { writtenFileSize = value; } } + private byte[] Window{ get { return window; } } + private uint LastLength { get { return (uint)lastLength; } set { lastLength = (int)value; } } + private uint OldDistN(int i) { return (uint)oldDist[i]; } + private void SetOldDistN(int i, uint value) { oldDist[i] = (int)value; } + private int WrPtr { get { return wrPtr; } set { wrPtr = value; } } private Unpack BlockHeader { get { return this; } } private Unpack Header { get { return this; } } - private int ReadTop { get { return this.readTop; } set { this.readTop = value; } } - private List Filters { get { return this.filters; } } + private int ReadTop { get { return readTop; } set { readTop = value; } } + private List Filters { get { return filters; } } // TODO: make sure these aren't already somewhere else public int BlockSize; @@ -325,13 +325,13 @@ public bool TablePresent; Filter.BlockLength=0; //Filter.Type=Inp.fgetbits()>>13; - Filter.Type=(byte)(this.Inp.fgetbits()>>13); + Filter.Type=(byte)(Inp.fgetbits()>>13); Inp.faddbits(3); if (Filter.Type==(byte)FilterType.FILTER_DELTA) { //Filter.Channels=(Inp.fgetbits()>>11)+1; - Filter.Channels=(byte)((this.Inp.fgetbits()>>11)+1); + Filter.Channels=(byte)((Inp.fgetbits()>>11)+1); Inp.faddbits(5); } @@ -363,7 +363,7 @@ public bool TablePresent; if (DataSize<0) return false; BlockHeader.BlockSize-=Inp.InAddr-BlockHeader.BlockStart; - if (Inp.InAddr>BitInput.MAX_SIZE/2) + if (Inp.InAddr>MAX_SIZE/2) { // If we already processed more than half of buffer, let's move // remaining data into beginning to free more space for new data @@ -383,9 +383,9 @@ public bool TablePresent; else DataSize=ReadTop; int ReadCode=0; - if (BitInput.MAX_SIZE!=DataSize) + if (MAX_SIZE!=DataSize) //ReadCode=UnpIO->UnpRead(Inp.InBuf+DataSize,BitInput.MAX_SIZE-DataSize); - ReadCode = readStream.Read(InBuf, DataSize, BitInput.MAX_SIZE-DataSize); + ReadCode = readStream.Read(InBuf, DataSize, MAX_SIZE-DataSize); if (ReadCode>0) // Can be also -1. ReadTop+=ReadCode; ReadBorder=ReadTop-30; @@ -685,7 +685,7 @@ public bool TablePresent; if (!UnpReadBuf()) return false; //Inp.faddbits((8-Inp.InBit)&7); - Inp.faddbits((uint)((8-this.Inp.InBit)&7)); + Inp.faddbits((uint)((8-Inp.InBit)&7)); byte BlockFlags=(byte)(Inp.fgetbits()>>8); Inp.faddbits(8); diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs index e8bc8c36..6eaab649 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.cs @@ -23,18 +23,18 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 private Stream writeStream; private void _UnpackCtor() { - for (int i = 0; i < this.AudV.Length; i++) { - this.AudV[i] = new AudioVariables(); + for (int i = 0; i < AudV.Length; i++) { + AudV[i] = new AudioVariables(); } } private int UnpIO_UnpRead(byte[] buf, int offset, int count) { // NOTE: caller has logic to check for -1 for error we throw instead. - return this.readStream.Read(buf, offset, count); + return readStream.Read(buf, offset, count); } private void UnpIO_UnpWrite(byte[] buf, size_t offset, uint count) { - this.writeStream.Write(buf, checked((int)offset), checked((int)count)); + writeStream.Write(buf, checked((int)offset), checked((int)count)); } public void DoUnpack(FileHeader fileHeader, Stream readStream, Stream writeStream) @@ -59,11 +59,11 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 public void DoUnpack() { - if (this.fileHeader.IsStored) + if (fileHeader.IsStored) { UnstoreFile(); } else { - DoUnpack(this.fileHeader.CompressionAlgorithm, fileHeader.IsSolid); + DoUnpack(fileHeader.CompressionAlgorithm, fileHeader.IsSolid); } } @@ -71,12 +71,12 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 { var b = new byte[0x10000]; do { - int n = this.readStream.Read(b, 0, (int)Math.Min(b.Length, DestUnpSize)); + int n = readStream.Read(b, 0, (int)Math.Min(b.Length, DestUnpSize)); if (n == 0) { break; } - this.writeStream.Write(b, 0, n); + writeStream.Write(b, 0, n); DestUnpSize -= n; } while (!Suspended); } @@ -98,7 +98,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 } } - public int PpmEscChar { get => this.PPMEscChar; set => this.PPMEscChar = value; } + public int PpmEscChar { get => PPMEscChar; set => PPMEscChar = value; } public static byte[] EnsureCapacity(byte[] array, int length) { return array.Length < length ? new byte[length] : array; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs index 8eeef6fe..2b71751e 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack15_cpp.cs @@ -125,9 +125,9 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 //#define GetShortLen1(pos) ((pos)==1 ? Buf60+3:ShortLen1[pos]) - private uint GetShortLen1(uint pos) { return ((pos)==1 ? (uint)(this.Buf60+3):ShortLen1[pos]); } + private uint GetShortLen1(uint pos) { return ((pos)==1 ? (uint)(Buf60+3):ShortLen1[pos]); } //#define GetShortLen2(pos) ((pos)==3 ? Buf60+3:ShortLen2[pos]) - private uint GetShortLen2(uint pos) { return ((pos)==3 ? (uint)(this.Buf60+3):ShortLen2[pos]); } + private uint GetShortLen2(uint pos) { return ((pos)==3 ? (uint)(Buf60+3):ShortLen2[pos]); } internal static class Unpack15Local { public static readonly uint[] ShortLen1={1,3,4,4,5,6,7,8,8,4,4,5,6,6,4,0}; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs index 72d1b529..1a78f9af 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack20_cpp.cs @@ -171,7 +171,7 @@ internal static class Unpack20Local { UnpSomeRead=true; if (UnpPtrBitInput.MAX_SIZE/2) + if (Inp.InAddr>MAX_SIZE/2) { // If we already processed more than half of buffer, let's move // remaining data into beginning to free more space for new data @@ -252,8 +252,8 @@ namespace SharpCompress.Compressors.Rar.UnpackV2017 else DataSize=ReadTop; int ReadCode=0; - if (BitInput.MAX_SIZE!=DataSize) - ReadCode=UnpIO_UnpRead(Inp.InBuf,DataSize,BitInput.MAX_SIZE-DataSize); + if (MAX_SIZE!=DataSize) + ReadCode=UnpIO_UnpRead(Inp.InBuf,DataSize,MAX_SIZE-DataSize); if (ReadCode>0) // Can be also -1. ReadTop+=ReadCode; ReadBorder=ReadTop-30; diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs index 21d3cd91..5b76ef4c 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/Unpack.unpack_cpp.cs @@ -196,7 +196,7 @@ public Unpack(/* ComprDataIO *DataIO */) { if (!Solid) { - Utility.Memset(this.OldDist, 0, this.OldDist.Length); + Utility.Memset(OldDist, 0, OldDist.Length); OldDistPtr=0; LastDist=LastLength=0; // memset(Window,0,MaxWinSize); diff --git a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs index b5175d24..8586c98b 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV2017/unpack_hpp.cs @@ -111,11 +111,11 @@ public const int UNPACK_MAX_WRITE =0x400000; public DecodeTable BD; // Decode bit lengths in Huffman table. public void Init() { - this.LD = new DecodeTable(); - this.DD = new DecodeTable(); - this.LDD = new DecodeTable(); - this.RD = new DecodeTable(); - this.BD = new DecodeTable(); + LD = new DecodeTable(); + DD = new DecodeTable(); + LDD = new DecodeTable(); + RD = new DecodeTable(); + BD = new DecodeTable(); } }; @@ -431,7 +431,7 @@ internal partial class Unpack private uint GetChar() { - if (Inp.InAddr>BitInput.MAX_SIZE-30) + if (Inp.InAddr>MAX_SIZE-30) UnpReadBuf(); return(Inp.InBuf[Inp.InAddr++]); } diff --git a/src/SharpCompress/Compressors/Rar/VM/BitInput.cs b/src/SharpCompress/Compressors/Rar/VM/BitInput.cs index 3d1fd979..82575912 100644 --- a/src/SharpCompress/Compressors/Rar/VM/BitInput.cs +++ b/src/SharpCompress/Compressors/Rar/VM/BitInput.cs @@ -9,8 +9,8 @@ namespace SharpCompress.Compressors.Rar.VM public int inBit; // TODO: rename var - public int InAddr { get { return this.inAddr; } set { this.inAddr = value; } } - public int InBit { get { return this.inBit; } set { this.inBit = value; } } + public int InAddr { get { return inAddr; } set { inAddr = value; } } + public int InBit { get { return inBit; } set { inBit = value; } } public bool ExternalBuffer; diff --git a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs index 0f793dff..8a13ec07 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/BlockFilter.cs @@ -15,11 +15,11 @@ namespace SharpCompress.Compressors.Xz.Filters ARCH_ARM_FILTER = 0x07, ARCH_ARMTHUMB_FILTER = 0x08, ARCH_SPARC_FILTER = 0x09, - LZMA2 = 0x21, + LZMA2 = 0x21 } - private static readonly Dictionary FilterMap = new Dictionary() - { + private static readonly Dictionary FilterMap = new Dictionary + { {FilterTypes.LZMA2, typeof(Lzma2Filter) } }; @@ -27,8 +27,6 @@ namespace SharpCompress.Compressors.Xz.Filters public abstract bool AllowAsNonLast { get; } public abstract bool ChangesDataSize { get; } - public BlockFilter() { } - public abstract void Init(byte[] properties); public abstract void ValidateFilter(); diff --git a/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs b/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs index 970fafd3..66d02e91 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using SharpCompress.Compressors.LZMA; namespace SharpCompress.Compressors.Xz.Filters { @@ -43,7 +44,7 @@ namespace SharpCompress.Compressors.Xz.Filters public override void SetBaseStream(Stream stream) { - BaseStream = new SharpCompress.Compressors.LZMA.LzmaStream(new[] { _dictionarySize }, stream); + BaseStream = new LzmaStream(new[] { _dictionarySize }, stream); } public override int Read(byte[] buffer, int offset, int count) diff --git a/src/SharpCompress/Compressors/Xz/XZFooter.cs b/src/SharpCompress/Compressors/Xz/XZFooter.cs index 73d89be4..8339bc65 100644 --- a/src/SharpCompress/Compressors/Xz/XZFooter.cs +++ b/src/SharpCompress/Compressors/Xz/XZFooter.cs @@ -8,7 +8,7 @@ namespace SharpCompress.Compressors.Xz public class XZFooter { private readonly BinaryReader _reader; - private readonly byte[] _magicBytes = new byte[] { 0x59, 0x5A }; + private readonly byte[] _magicBytes = { 0x59, 0x5A }; public long StreamStartPosition { get; private set; } public long BackwardSize { get; private set; } public byte[] StreamFlags { get; private set; } @@ -40,7 +40,7 @@ namespace SharpCompress.Compressors.Xz StreamFlags = reader.ReadBytes(2); } byte[] magBy = _reader.ReadBytes(2); - if (!Enumerable.SequenceEqual(magBy, _magicBytes)) + if (!magBy.SequenceEqual(_magicBytes)) { throw new InvalidDataException("Magic footer missing"); } diff --git a/src/SharpCompress/Compressors/Xz/XZHeader.cs b/src/SharpCompress/Compressors/Xz/XZHeader.cs index c74b888c..16588d6e 100644 --- a/src/SharpCompress/Compressors/Xz/XZHeader.cs +++ b/src/SharpCompress/Compressors/Xz/XZHeader.cs @@ -48,7 +48,7 @@ namespace SharpCompress.Compressors.Xz private void CheckMagicBytes(byte[] header) { - if (!Enumerable.SequenceEqual(header, MagicHeader)) + if (!header.SequenceEqual(MagicHeader)) throw new InvalidDataException("Invalid XZ Stream"); } } diff --git a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs index 558249e7..db30985a 100644 --- a/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs +++ b/src/SharpCompress/Readers/Rar/MultiVolumeRarReader.cs @@ -35,7 +35,7 @@ namespace SharpCompress.Readers.Rar { if (!base.NextEntryForCurrentStream()) { // if we're got another stream to try to process then do so - return this.streams.MoveNext() && LoadStreamForReading(this.streams.Current); + return streams.MoveNext() && LoadStreamForReading(streams.Current); } return true; } diff --git a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs index 72bd222e..7eeba813 100644 --- a/tests/SharpCompress.Test/Rar/RarArchiveTests.cs +++ b/tests/SharpCompress.Test/Rar/RarArchiveTests.cs @@ -186,14 +186,14 @@ namespace SharpCompress.Test.Rar private void DoRar_IsSolidArchiveCheck(string filename) { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(this.TEST_ARCHIVES_PATH, filename))) + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) { using (var archive = RarArchive.Open(stream)) { Assert.False(archive.IsSolid); foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - entry.WriteToDirectory(this.SCRATCH_FILES_PATH, + entry.WriteToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, @@ -254,12 +254,12 @@ namespace SharpCompress.Test.Rar private void DoRar_Multi_ArchiveStreamRead(string[] archives) { ResetScratch(); - using (var archive = RarArchive.Open(archives.Select(s => Path.Combine(this.TEST_ARCHIVES_PATH, s)) + using (var archive = RarArchive.Open(archives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) .Select(File.OpenRead))) { foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory)) { - entry.WriteToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() + entry.WriteToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true @@ -373,7 +373,7 @@ namespace SharpCompress.Test.Rar private void DoRar_IsFirstVolume_True(string firstFilename) { - using (var archive = RarArchive.Open(Path.Combine(this.TEST_ARCHIVES_PATH, firstFilename))) + using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, firstFilename))) { Assert.True(archive.IsMultipartVolume()); Assert.True(archive.IsFirstVolume()); @@ -392,7 +392,7 @@ namespace SharpCompress.Test.Rar private void DoRar_IsFirstVolume_False(string notFirstFilename) { - using (var archive = RarArchive.Open(Path.Combine(this.TEST_ARCHIVES_PATH, notFirstFilename))) + using (var archive = RarArchive.Open(Path.Combine(TEST_ARCHIVES_PATH, notFirstFilename))) { Assert.True(archive.IsMultipartVolume()); Assert.False(archive.IsFirstVolume()); diff --git a/tests/SharpCompress.Test/Rar/RarReaderTests.cs b/tests/SharpCompress.Test/Rar/RarReaderTests.cs index a4101c64..8caf8896 100644 --- a/tests/SharpCompress.Test/Rar/RarReaderTests.cs +++ b/tests/SharpCompress.Test/Rar/RarReaderTests.cs @@ -34,12 +34,12 @@ namespace SharpCompress.Test.Rar private void DoRar_Multi_Reader(string[] archives) { ResetScratch(); - using (var reader = RarReader.Open(archives.Select(s => Path.Combine(this.TEST_ARCHIVES_PATH, s)) + using (var reader = RarReader.Open(archives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) .Select(p => File.OpenRead(p)))) { while (reader.MoveToNextEntry()) { - reader.WriteEntryToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() + reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true @@ -65,7 +65,7 @@ namespace SharpCompress.Test.Rar Assert.Throws(() => { ResetScratch(); - using (var reader = RarReader.Open(archives.Select(s => Path.Combine(this.TEST_ARCHIVES_PATH, s)) + using (var reader = RarReader.Open(archives.Select(s => Path.Combine(TEST_ARCHIVES_PATH, s)) .Select(p => File.OpenRead(p)), new ReaderOptions() { @@ -74,7 +74,7 @@ namespace SharpCompress.Test.Rar { while (reader.MoveToNextEntry()) { - reader.WriteEntryToDirectory(this.SCRATCH_FILES_PATH, + reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, @@ -114,14 +114,14 @@ namespace SharpCompress.Test.Rar foreach (var file in archives) { - File.Copy(Path.Combine(this.TEST_ARCHIVES_PATH, file), Path.Combine(this.SCRATCH2_FILES_PATH, file)); + File.Copy(Path.Combine(TEST_ARCHIVES_PATH, file), Path.Combine(SCRATCH2_FILES_PATH, file)); } - var streams = archives.Select(s => Path.Combine(this.SCRATCH2_FILES_PATH, s)).Select(File.OpenRead).ToList(); + var streams = archives.Select(s => Path.Combine(SCRATCH2_FILES_PATH, s)).Select(File.OpenRead).ToList(); using (var reader = RarReader.Open(streams)) { while (reader.MoveToNextEntry()) { - reader.WriteEntryToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() + reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true @@ -134,7 +134,7 @@ namespace SharpCompress.Test.Rar } VerifyFiles(); - foreach (var file in archives.Select(s => Path.Combine(this.SCRATCH2_FILES_PATH, s))) + foreach (var file in archives.Select(s => Path.Combine(SCRATCH2_FILES_PATH, s))) { File.Delete(file); } @@ -238,7 +238,7 @@ namespace SharpCompress.Test.Rar private void DoRar_Entry_Stream(string filename) { ResetScratch(); - using (Stream stream = File.OpenRead(Path.Combine(this.TEST_ARCHIVES_PATH, filename))) + using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) using (var reader = RarReader.Open(stream)) { while (reader.MoveToNextEntry()) @@ -250,7 +250,7 @@ namespace SharpCompress.Test.Rar { string file = Path.GetFileName(reader.Entry.Key); string folder = Path.GetDirectoryName(reader.Entry.Key); - string destdir = Path.Combine(this.SCRATCH_FILES_PATH, folder); + string destdir = Path.Combine(SCRATCH_FILES_PATH, folder); if (!Directory.Exists(destdir)) { Directory.CreateDirectory(destdir); @@ -340,7 +340,7 @@ namespace SharpCompress.Test.Rar private void DoRar_Solid_Skip_Reader(string filename) { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(this.TEST_ARCHIVES_PATH, filename))) + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) using (var reader = RarReader.Open(stream, new ReaderOptions() { LookForHeader = true @@ -351,7 +351,7 @@ namespace SharpCompress.Test.Rar if (reader.Entry.Key.Contains("jpg")) { Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); - reader.WriteEntryToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() + reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true @@ -374,7 +374,7 @@ namespace SharpCompress.Test.Rar private void DoRar_Reader_Skip(string filename) { ResetScratch(); - using (var stream = File.OpenRead(Path.Combine(this.TEST_ARCHIVES_PATH, filename))) + using (var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, filename))) using (var reader = RarReader.Open(stream, new ReaderOptions() { LookForHeader = true @@ -385,7 +385,7 @@ namespace SharpCompress.Test.Rar if (reader.Entry.Key.Contains("jpg")) { Assert.Equal(CompressionType.Rar, reader.Entry.CompressionType); - reader.WriteEntryToDirectory(this.SCRATCH_FILES_PATH, new ExtractionOptions() + reader.WriteEntryToDirectory(SCRATCH_FILES_PATH, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true From c341c626a59ce9289105295ec797aae47ff67d90 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 6 May 2018 08:49:32 +0100 Subject: [PATCH 111/124] variable naming clean up --- .../Common/Rar/Headers/NewSubHeaderType.cs | 4 +- .../Common/Rar/Headers/RarHeader.cs | 4 +- .../Common/Rar/RarCryptoBinaryReader.cs | 4 +- .../Common/Rar/RarCryptoWrapper.cs | 4 +- src/SharpCompress/Common/Rar/RarRijndael.cs | 4 +- .../Common/SevenZip/ArchiveReader.cs | 22 +- src/SharpCompress/Compressors/ADC/ADCBase.cs | 30 +- .../Compressors/ADC/ADCStream.cs | 46 +- .../Compressors/LZMA/Bcj2DecoderStream.cs | 130 ++-- .../Compressors/LZMA/BitVector.cs | 18 +- src/SharpCompress/Compressors/LZMA/CRC.cs | 28 +- src/SharpCompress/Compressors/LZMA/ICoder.cs | 4 +- src/SharpCompress/Compressors/LZMA/LZ/CRC.cs | 68 -- .../Compressors/LZMA/LZ/LzBinTree.cs | 112 +-- .../Compressors/LZMA/LZ/LzOutWindow.cs | 30 +- src/SharpCompress/Compressors/LZMA/Log.cs | 26 +- .../Compressors/LZMA/LzmaBase.cs | 84 +- .../Compressors/LZMA/LzmaDecoder.cs | 306 ++++---- .../Compressors/LZMA/LzmaEncoder.cs | 730 +++++++++--------- .../Compressors/LZMA/LzmaEncoderProperties.cs | 24 +- .../Compressors/LZMA/LzmaStream.cs | 224 +++--- .../Compressors/LZMA/RangeCoder/RangeCoder.cs | 128 +-- .../LZMA/RangeCoder/RangeCoderBit.cs | 92 +-- .../LZMA/RangeCoder/RangeCoderBitTree.cs | 74 +- .../Compressors/LZMA/Registry.cs | 34 +- .../LZMA/Utilites/CrcBuilderStream.cs | 121 +-- .../LZMA/Utilites/CrcCheckStream.cs | 38 +- .../Compressors/LZMA/Utilites/Utils.cs | 2 +- .../Compressors/PPMd/H/FreqData.cs | 8 +- .../Compressors/PPMd/H/ModelPPM.cs | 516 ++++++------- .../Compressors/PPMd/H/PPMContext.cs | 362 ++++----- .../Compressors/PPMd/H/RangeCoder.cs | 72 +- .../Compressors/PPMd/H/RarMemBlock.cs | 38 +- .../Compressors/PPMd/H/RarNode.cs | 16 +- .../Compressors/PPMd/H/SEE2Context.cs | 50 +- src/SharpCompress/Compressors/PPMd/H/State.cs | 20 +- .../Compressors/PPMd/H/StateRef.cs | 20 +- .../Compressors/PPMd/H/SubAllocator.cs | 330 ++++---- .../Compressors/PPMd/I1/Allocator.cs | 240 +++--- .../Compressors/PPMd/I1/Coder.cs | 70 +- .../Compressors/PPMd/I1/MemoryNode.cs | 76 +- .../Compressors/PPMd/I1/Model.cs | 454 +++++------ .../Compressors/PPMd/I1/Pointer.cs | 92 +-- .../Compressors/PPMd/I1/PpmContext.cs | 374 ++++----- .../Compressors/PPMd/I1/PpmState.cs | 54 +- .../Compressors/PPMd/I1/See2Context.cs | 26 +- .../Compressors/PPMd/PpmdProperties.cs | 29 +- .../Compressors/PPMd/PpmdStream.cs | 76 +- .../Compressors/PPMd/PpmdVersion.cs | 2 +- .../Compressors/Rar/UnpackV1/Unpack.cs | 24 +- src/SharpCompress/SharpCompress.csproj | 4 +- 51 files changed, 2594 insertions(+), 2750 deletions(-) delete mode 100644 src/SharpCompress/Compressors/LZMA/LZ/CRC.cs diff --git a/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs b/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs index 220c53d0..84d50aee 100644 --- a/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs +++ b/src/SharpCompress/Common/Rar/Headers/NewSubHeaderType.cs @@ -33,13 +33,13 @@ namespace SharpCompress.Common.Rar.Headers internal bool Equals(byte[] bytes) { - if (this._bytes.Length != bytes.Length) + if (_bytes.Length != bytes.Length) { return false; } for (int i = 0; i < bytes.Length; ++i) { - if (this._bytes[i] != bytes[i]) + if (_bytes[i] != bytes[i]) { return false; } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index 3171b539..b15f6500 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -26,7 +26,7 @@ namespace SharpCompress.Common.Rar.Headers private RarHeader(RarCrcBinaryReader reader, bool isRar5, ArchiveEncoding archiveEncoding) { _headerType = HeaderType.Null; - this._isRar5 = isRar5; + _isRar5 = isRar5; ArchiveEncoding = archiveEncoding; if (IsRar5) { @@ -60,7 +60,7 @@ namespace SharpCompress.Common.Rar.Headers } protected RarHeader(RarHeader header, RarCrcBinaryReader reader, HeaderType headerType) { - this._headerType = headerType; + _headerType = headerType; _isRar5 = header.IsRar5; HeaderCrc = header.HeaderCrc; HeaderCode = header.HeaderCode; diff --git a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs index a9ed1ce0..66c0d606 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoBinaryReader.cs @@ -15,7 +15,7 @@ namespace SharpCompress.Common.Rar public RarCryptoBinaryReader(Stream stream, string password) : base(stream) { - this._password = password; + _password = password; // coderb: not sure why this was being done at this logical point //SkipQueue(); @@ -42,7 +42,7 @@ namespace SharpCompress.Common.Rar internal void InitializeAes(byte[] salt) { - this._salt = salt; + _salt = salt; _rijndael = RarRijndael.InitializeFrom(_password, salt); } diff --git a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs index 2fe47ff8..50e1520c 100644 --- a/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs +++ b/src/SharpCompress/Common/Rar/RarCryptoWrapper.cs @@ -15,8 +15,8 @@ namespace SharpCompress.Common.Rar public RarCryptoWrapper(Stream actualStream, string password, byte[] salt) { - this._actualStream = actualStream; - this._salt = salt; + _actualStream = actualStream; + _salt = salt; _rijndael = RarRijndael.InitializeFrom(password, salt); } diff --git a/src/SharpCompress/Common/Rar/RarRijndael.cs b/src/SharpCompress/Common/Rar/RarRijndael.cs index 55d870d6..83fd4958 100644 --- a/src/SharpCompress/Common/Rar/RarRijndael.cs +++ b/src/SharpCompress/Common/Rar/RarRijndael.cs @@ -20,8 +20,8 @@ namespace SharpCompress.Common.Rar private RarRijndael(string password, byte[] salt) { - this._password = password; - this._salt = salt; + _password = password; + _salt = salt; } private byte[] ComputeHash(byte[] input) diff --git a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs index 3e0155f0..2041fd3f 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs @@ -457,7 +457,7 @@ namespace SharpCompress.Common.SevenZip { break; } - if (type == BlockType.CRC) + if (type == BlockType.Crc) { packCRCs = ReadHashDigests(numPackStreams); continue; @@ -545,7 +545,7 @@ namespace SharpCompress.Common.SevenZip return; } - if (type == BlockType.CRC) + if (type == BlockType.Crc) { List crcs = ReadHashDigests(numFolders); for (int i = 0; i < numFolders; i++) @@ -600,7 +600,7 @@ namespace SharpCompress.Common.SevenZip #endif continue; } - if (type == BlockType.CRC || type == BlockType.Size) + if (type == BlockType.Crc || type == BlockType.Size) { break; } @@ -672,7 +672,7 @@ namespace SharpCompress.Common.SevenZip for (;;) { - if (type == BlockType.CRC) + if (type == BlockType.Crc) { digests = new List(numDigestsTotal); @@ -835,7 +835,7 @@ namespace SharpCompress.Common.SevenZip if (folder.UnpackCRCDefined) { - if (CRC.Finish(CRC.Update(CRC.kInitCRC, data, 0, unpackSize)) != folder.UnpackCRC) + if (Crc.Finish(Crc.Update(Crc.INIT_CRC, data, 0, unpackSize)) != folder.UnpackCRC) { throw new InvalidOperationException("Decoded stream does not match expected CRC."); } @@ -1227,11 +1227,11 @@ namespace SharpCompress.Common.SevenZip long nextHeaderSize = (long)DataReader.Get64(_header, 0x14); uint nextHeaderCrc = DataReader.Get32(_header, 0x1C); - uint crc = CRC.kInitCRC; - crc = CRC.Update(crc, nextHeaderOffset); - crc = CRC.Update(crc, nextHeaderSize); - crc = CRC.Update(crc, nextHeaderCrc); - crc = CRC.Finish(crc); + uint crc = Crc.INIT_CRC; + crc = Crc.Update(crc, nextHeaderOffset); + crc = Crc.Update(crc, nextHeaderSize); + crc = Crc.Update(crc, nextHeaderCrc); + crc = Crc.Finish(crc); if (crc != crcFromArchive) { @@ -1262,7 +1262,7 @@ namespace SharpCompress.Common.SevenZip byte[] header = new byte[nextHeaderSize]; _stream.ReadExact(header, 0, header.Length); - if (CRC.Finish(CRC.Update(CRC.kInitCRC, header, 0, header.Length)) != nextHeaderCrc) + if (Crc.Finish(Crc.Update(Crc.INIT_CRC, header, 0, header.Length)) != nextHeaderCrc) { throw new InvalidOperationException(); } diff --git a/src/SharpCompress/Compressors/ADC/ADCBase.cs b/src/SharpCompress/Compressors/ADC/ADCBase.cs index 83250cee..dfca000e 100644 --- a/src/SharpCompress/Compressors/ADC/ADCBase.cs +++ b/src/SharpCompress/Compressors/ADC/ADCBase.cs @@ -33,32 +33,32 @@ namespace SharpCompress.Compressors.ADC /// public static class ADCBase { - private const int Plain = 1; - private const int TwoByte = 2; - private const int ThreeByte = 3; + private const int PLAIN = 1; + private const int TWO_BYTE = 2; + private const int THREE_BYTE = 3; private static int GetChunkType(byte byt) { if ((byt & 0x80) == 0x80) { - return Plain; + return PLAIN; } if ((byt & 0x40) == 0x40) { - return ThreeByte; + return THREE_BYTE; } - return TwoByte; + return TWO_BYTE; } private static int GetChunkSize(byte byt) { switch (GetChunkType(byt)) { - case Plain: + case PLAIN: return (byt & 0x7F) + 1; - case TwoByte: + case TWO_BYTE: return ((byt & 0x3F) >> 2) + 3; - case ThreeByte: + case THREE_BYTE: return (byt & 0x3F) + 4; default: return -1; @@ -69,11 +69,11 @@ namespace SharpCompress.Compressors.ADC { switch (GetChunkType(chunk[position])) { - case Plain: + case PLAIN: return 0; - case TwoByte: + case TWO_BYTE: return ((chunk[position] & 0x03) << 8) + chunk[position + 1]; - case ThreeByte: + case THREE_BYTE: return (chunk[position + 1] << 8) + chunk[position + 2]; default: return -1; @@ -130,7 +130,7 @@ namespace SharpCompress.Compressors.ADC switch (chunkType) { - case Plain: + case PLAIN: chunkSize = GetChunkSize((byte)readByte); if (outPosition + chunkSize > bufferSize) { @@ -141,7 +141,7 @@ namespace SharpCompress.Compressors.ADC outPosition += chunkSize; position += chunkSize + 1; break; - case TwoByte: + case TWO_BYTE: tempMs = new MemoryStream(); chunkSize = GetChunkSize((byte)readByte); tempMs.WriteByte((byte)readByte); @@ -172,7 +172,7 @@ namespace SharpCompress.Compressors.ADC position += 2; } break; - case ThreeByte: + case THREE_BYTE: tempMs = new MemoryStream(); chunkSize = GetChunkSize((byte)readByte); tempMs.WriteByte((byte)readByte); diff --git a/src/SharpCompress/Compressors/ADC/ADCStream.cs b/src/SharpCompress/Compressors/ADC/ADCStream.cs index 0db58080..d8799fdd 100644 --- a/src/SharpCompress/Compressors/ADC/ADCStream.cs +++ b/src/SharpCompress/Compressors/ADC/ADCStream.cs @@ -36,27 +36,27 @@ namespace SharpCompress.Compressors.ADC /// /// This stream holds the compressed data /// - private readonly Stream stream; + private readonly Stream _stream; /// /// Is this instance disposed? /// - private bool isDisposed; + private bool _isDisposed; /// /// Position in decompressed data /// - private long position; + private long _position; /// /// Buffer with currently used chunk of decompressed data /// - private byte[] outBuffer; + private byte[] _outBuffer; /// /// Position in buffer of decompressed data /// - private int outPosition; + private int _outPosition; /// /// Initializates a stream that decompresses ADC data on the fly @@ -70,10 +70,10 @@ namespace SharpCompress.Compressors.ADC throw new NotSupportedException(); } - this.stream = stream; + _stream = stream; } - public override bool CanRead => stream.CanRead; + public override bool CanRead => _stream.CanRead; public override bool CanSeek => false; @@ -81,7 +81,7 @@ namespace SharpCompress.Compressors.ADC public override long Length => throw new NotSupportedException(); - public override long Position { get => position; set => throw new NotSupportedException(); } + public override long Position { get => _position; set => throw new NotSupportedException(); } public override void Flush() { @@ -89,11 +89,11 @@ namespace SharpCompress.Compressors.ADC protected override void Dispose(bool disposing) { - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; + _isDisposed = true; base.Dispose(disposing); } @@ -122,35 +122,35 @@ namespace SharpCompress.Compressors.ADC int size = -1; - if (outBuffer == null) + if (_outBuffer == null) { - size = ADCBase.Decompress(stream, out outBuffer); - outPosition = 0; + size = ADCBase.Decompress(_stream, out _outBuffer); + _outPosition = 0; } int inPosition = offset; int toCopy = count; int copied = 0; - while (outPosition + toCopy >= outBuffer.Length) + while (_outPosition + toCopy >= _outBuffer.Length) { - int piece = outBuffer.Length - outPosition; - Array.Copy(outBuffer, outPosition, buffer, inPosition, piece); + int piece = _outBuffer.Length - _outPosition; + Array.Copy(_outBuffer, _outPosition, buffer, inPosition, piece); inPosition += piece; copied += piece; - position += piece; + _position += piece; toCopy -= piece; - size = ADCBase.Decompress(stream, out outBuffer); - outPosition = 0; - if (size == 0 || outBuffer == null || outBuffer.Length == 0) + size = ADCBase.Decompress(_stream, out _outBuffer); + _outPosition = 0; + if (size == 0 || _outBuffer == null || _outBuffer.Length == 0) { return copied; } } - Array.Copy(outBuffer, outPosition, buffer, inPosition, toCopy); - outPosition += toCopy; - position += toCopy; + Array.Copy(_outBuffer, _outPosition, buffer, inPosition, toCopy); + _outPosition += toCopy; + _position += toCopy; copied += toCopy; return copied; } diff --git a/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs b/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs index 6b37c53c..3dc78c3a 100644 --- a/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs @@ -6,28 +6,28 @@ namespace SharpCompress.Compressors.LZMA { internal class Bcj2DecoderStream : DecoderStream2 { - private const int kNumTopBits = 24; - private const uint kTopValue = (1 << kNumTopBits); + private const int K_NUM_TOP_BITS = 24; + private const uint K_TOP_VALUE = (1 << K_NUM_TOP_BITS); private class RangeDecoder { - internal readonly Stream mStream; - internal uint Range; - internal uint Code; + internal readonly Stream _mStream; + internal uint _range; + internal uint _code; public RangeDecoder(Stream stream) { - mStream = stream; - Range = 0xFFFFFFFF; + _mStream = stream; + _range = 0xFFFFFFFF; for (int i = 0; i < 5; i++) { - Code = (Code << 8) | ReadByte(); + _code = (_code << 8) | ReadByte(); } } public byte ReadByte() { - int bt = mStream.ReadByte(); + int bt = _mStream.ReadByte(); if (bt < 0) { throw new EndOfStreamException(); @@ -38,22 +38,22 @@ namespace SharpCompress.Compressors.LZMA public void Dispose() { - mStream.Dispose(); + _mStream.Dispose(); } } private class StatusDecoder { - private const int numMoveBits = 5; + private const int NUM_MOVE_BITS = 5; - private const int kNumBitModelTotalBits = 11; - private const uint kBitModelTotal = 1u << kNumBitModelTotalBits; + private const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; + private const uint K_BIT_MODEL_TOTAL = 1u << K_NUM_BIT_MODEL_TOTAL_BITS; - private uint Prob; + private uint _prob; public StatusDecoder() { - Prob = kBitModelTotal / 2; + _prob = K_BIT_MODEL_TOTAL / 2; } private void UpdateModel(uint symbol) @@ -64,49 +64,49 @@ namespace SharpCompress.Compressors.LZMA */ if (symbol == 0) { - Prob += (kBitModelTotal - Prob) >> numMoveBits; + _prob += (K_BIT_MODEL_TOTAL - _prob) >> NUM_MOVE_BITS; } else { - Prob -= (Prob) >> numMoveBits; + _prob -= (_prob) >> NUM_MOVE_BITS; } } public uint Decode(RangeDecoder decoder) { - uint newBound = (decoder.Range >> kNumBitModelTotalBits) * Prob; - if (decoder.Code < newBound) + uint newBound = (decoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob; + if (decoder._code < newBound) { - decoder.Range = newBound; - Prob += (kBitModelTotal - Prob) >> numMoveBits; - if (decoder.Range < kTopValue) + decoder._range = newBound; + _prob += (K_BIT_MODEL_TOTAL - _prob) >> NUM_MOVE_BITS; + if (decoder._range < K_TOP_VALUE) { - decoder.Code = (decoder.Code << 8) | decoder.ReadByte(); - decoder.Range <<= 8; + decoder._code = (decoder._code << 8) | decoder.ReadByte(); + decoder._range <<= 8; } return 0; } - decoder.Range -= newBound; - decoder.Code -= newBound; - Prob -= Prob >> numMoveBits; - if (decoder.Range < kTopValue) + decoder._range -= newBound; + decoder._code -= newBound; + _prob -= _prob >> NUM_MOVE_BITS; + if (decoder._range < K_TOP_VALUE) { - decoder.Code = (decoder.Code << 8) | decoder.ReadByte(); - decoder.Range <<= 8; + decoder._code = (decoder._code << 8) | decoder.ReadByte(); + decoder._range <<= 8; } return 1; } } - private readonly Stream mMainStream; - private readonly Stream mCallStream; - private readonly Stream mJumpStream; - private readonly RangeDecoder mRangeDecoder; - private readonly StatusDecoder[] mStatusDecoder; - private long mWritten; - private readonly IEnumerator mIter; - private bool mFinished; - private bool isDisposed; + private readonly Stream _mMainStream; + private readonly Stream _mCallStream; + private readonly Stream _mJumpStream; + private readonly RangeDecoder _mRangeDecoder; + private readonly StatusDecoder[] _mStatusDecoder; + private long _mWritten; + private readonly IEnumerator _mIter; + private bool _mFinished; + private bool _isDisposed; public Bcj2DecoderStream(Stream[] streams, byte[] info, long limit) { @@ -120,31 +120,31 @@ namespace SharpCompress.Compressors.LZMA throw new NotSupportedException(); } - mMainStream = streams[0]; - mCallStream = streams[1]; - mJumpStream = streams[2]; - mRangeDecoder = new RangeDecoder(streams[3]); + _mMainStream = streams[0]; + _mCallStream = streams[1]; + _mJumpStream = streams[2]; + _mRangeDecoder = new RangeDecoder(streams[3]); - mStatusDecoder = new StatusDecoder[256 + 2]; - for (int i = 0; i < mStatusDecoder.Length; i++) + _mStatusDecoder = new StatusDecoder[256 + 2]; + for (int i = 0; i < _mStatusDecoder.Length; i++) { - mStatusDecoder[i] = new StatusDecoder(); + _mStatusDecoder[i] = new StatusDecoder(); } - mIter = Run().GetEnumerator(); + _mIter = Run().GetEnumerator(); } protected override void Dispose(bool disposing) { - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; + _isDisposed = true; base.Dispose(disposing); - mMainStream.Dispose(); - mCallStream.Dispose(); - mJumpStream.Dispose(); + _mMainStream.Dispose(); + _mCallStream.Dispose(); + _mJumpStream.Dispose(); } private static bool IsJcc(byte b0, byte b1) @@ -174,20 +174,20 @@ namespace SharpCompress.Compressors.LZMA public override int Read(byte[] buffer, int offset, int count) { - if (count == 0 || mFinished) + if (count == 0 || _mFinished) { return 0; } for (int i = 0; i < count; i++) { - if (!mIter.MoveNext()) + if (!_mIter.MoveNext()) { - mFinished = true; + _mFinished = true; return i; } - buffer[offset + i] = mIter.Current; + buffer[offset + i] = _mIter.Current; } return count; @@ -205,14 +205,14 @@ namespace SharpCompress.Compressors.LZMA uint i; for (i = 0; i < kBurstSize; i++) { - int tmp = mMainStream.ReadByte(); + int tmp = _mMainStream.ReadByte(); if (tmp < 0) { yield break; } b = (byte)tmp; - mWritten++; + _mWritten++; yield return b; if (IsJ(prevByte, b)) { @@ -228,9 +228,9 @@ namespace SharpCompress.Compressors.LZMA continue; } - if (mStatusDecoder[GetIndex(prevByte, b)].Decode(mRangeDecoder) == 1) + if (_mStatusDecoder[GetIndex(prevByte, b)].Decode(_mRangeDecoder) == 1) { - Stream s = (b == 0xE8) ? mCallStream : mJumpStream; + Stream s = (b == 0xE8) ? _mCallStream : _mJumpStream; uint src = 0; for (i = 0; i < 4; i++) @@ -245,14 +245,14 @@ namespace SharpCompress.Compressors.LZMA src |= (uint)b0; } - uint dest = src - (uint)(mWritten + 4); - mWritten++; + uint dest = src - (uint)(_mWritten + 4); + _mWritten++; yield return (byte)dest; - mWritten++; + _mWritten++; yield return (byte)(dest >> 8); - mWritten++; + _mWritten++; yield return (byte)(dest >> 16); - mWritten++; + _mWritten++; yield return (byte)(dest >> 24); prevByte = (byte)(dest >> 24); processedBytes += 4; diff --git a/src/SharpCompress/Compressors/LZMA/BitVector.cs b/src/SharpCompress/Compressors/LZMA/BitVector.cs index f3934c4e..9f267f72 100644 --- a/src/SharpCompress/Compressors/LZMA/BitVector.cs +++ b/src/SharpCompress/Compressors/LZMA/BitVector.cs @@ -6,24 +6,24 @@ namespace SharpCompress.Compressors.LZMA { internal class BitVector { - private readonly uint[] mBits; + private readonly uint[] _mBits; public BitVector(int length) { Length = length; - mBits = new uint[(length + 31) >> 5]; + _mBits = new uint[(length + 31) >> 5]; } public BitVector(int length, bool initValue) { Length = length; - mBits = new uint[(length + 31) >> 5]; + _mBits = new uint[(length + 31) >> 5]; if (initValue) { - for (int i = 0; i < mBits.Length; i++) + for (int i = 0; i < _mBits.Length; i++) { - mBits[i] = ~0u; + _mBits[i] = ~0u; } } } @@ -61,7 +61,7 @@ namespace SharpCompress.Compressors.LZMA throw new ArgumentOutOfRangeException(nameof(index)); } - return (mBits[index >> 5] & (1u << (index & 31))) != 0; + return (_mBits[index >> 5] & (1u << (index & 31))) != 0; } } @@ -72,7 +72,7 @@ namespace SharpCompress.Compressors.LZMA throw new ArgumentOutOfRangeException(nameof(index)); } - mBits[index >> 5] |= 1u << (index & 31); + _mBits[index >> 5] |= 1u << (index & 31); } internal bool GetAndSet(int index) @@ -82,9 +82,9 @@ namespace SharpCompress.Compressors.LZMA throw new ArgumentOutOfRangeException(nameof(index)); } - uint bits = mBits[index >> 5]; + uint bits = _mBits[index >> 5]; uint mask = 1u << (index & 31); - mBits[index >> 5] |= mask; + _mBits[index >> 5] |= mask; return (bits & mask) != 0; } diff --git a/src/SharpCompress/Compressors/LZMA/CRC.cs b/src/SharpCompress/Compressors/LZMA/CRC.cs index 92e5851d..2ee0600a 100644 --- a/src/SharpCompress/Compressors/LZMA/CRC.cs +++ b/src/SharpCompress/Compressors/LZMA/CRC.cs @@ -3,12 +3,12 @@ using System.IO; namespace SharpCompress.Compressors.LZMA { - internal static class CRC + internal static class Crc { - public const uint kInitCRC = 0xFFFFFFFF; - private static readonly uint[] kTable = new uint[4 * 256]; + internal const uint INIT_CRC = 0xFFFFFFFF; + internal static readonly uint[] TABLE = new uint[4 * 256]; - static CRC() + static Crc() { const uint kCrcPoly = 0xEDB88320; @@ -20,19 +20,19 @@ namespace SharpCompress.Compressors.LZMA r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); } - kTable[i] = r; + TABLE[i] = r; } - for (uint i = 256; i < kTable.Length; i++) + for (uint i = 256; i < TABLE.Length; i++) { - uint r = kTable[i - 256]; - kTable[i] = kTable[r & 0xFF] ^ (r >> 8); + uint r = TABLE[i - 256]; + TABLE[i] = TABLE[r & 0xFF] ^ (r >> 8); } } public static uint From(Stream stream, long length) { - uint crc = kInitCRC; + uint crc = INIT_CRC; byte[] buffer = new byte[Math.Min(length, 4 << 10)]; while (length > 0) { @@ -54,16 +54,16 @@ namespace SharpCompress.Compressors.LZMA public static uint Update(uint crc, byte bt) { - return kTable[(crc & 0xFF) ^ bt] ^ (crc >> 8); + return TABLE[(crc & 0xFF) ^ bt] ^ (crc >> 8); } public static uint Update(uint crc, uint value) { crc ^= value; - return kTable[0x300 + (crc & 0xFF)] - ^ kTable[0x200 + ((crc >> 8) & 0xFF)] - ^ kTable[0x100 + ((crc >> 16) & 0xFF)] - ^ kTable[0x000 + (crc >> 24)]; + return TABLE[0x300 + (crc & 0xFF)] + ^ TABLE[0x200 + ((crc >> 8) & 0xFF)] + ^ TABLE[0x100 + ((crc >> 16) & 0xFF)] + ^ TABLE[0x000 + (crc >> 24)]; } public static uint Update(uint crc, ulong value) diff --git a/src/SharpCompress/Compressors/LZMA/ICoder.cs b/src/SharpCompress/Compressors/LZMA/ICoder.cs index 6e00503c..f5049dd7 100644 --- a/src/SharpCompress/Compressors/LZMA/ICoder.cs +++ b/src/SharpCompress/Compressors/LZMA/ICoder.cs @@ -77,7 +77,7 @@ namespace SharpCompress.Compressors.LZMA /// /// Provides the fields that represent properties idenitifiers for compressing. /// - internal enum CoderPropID + internal enum CoderPropId { /// /// Specifies default property. @@ -157,7 +157,7 @@ namespace SharpCompress.Compressors.LZMA internal interface ISetCoderProperties { - void SetCoderProperties(CoderPropID[] propIDs, object[] properties); + void SetCoderProperties(CoderPropId[] propIDs, object[] properties); } internal interface IWriteCoderProperties diff --git a/src/SharpCompress/Compressors/LZMA/LZ/CRC.cs b/src/SharpCompress/Compressors/LZMA/LZ/CRC.cs deleted file mode 100644 index a6c560d3..00000000 --- a/src/SharpCompress/Compressors/LZMA/LZ/CRC.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace SharpCompress.Compressors.LZMA.LZ -{ - internal class CRC - { - public static readonly uint[] Table; - - static CRC() - { - Table = new uint[256]; - const uint kPoly = 0xEDB88320; - for (uint i = 0; i < 256; i++) - { - uint r = i; - for (int j = 0; j < 8; j++) - { - if ((r & 1) != 0) - { - r = (r >> 1) ^ kPoly; - } - else - { - r >>= 1; - } - } - Table[i] = r; - } - } - - private uint _value = 0xFFFFFFFF; - - public void Init() - { - _value = 0xFFFFFFFF; - } - - public void UpdateByte(byte b) - { - _value = Table[(((byte)(_value)) ^ b)] ^ (_value >> 8); - } - - public void Update(byte[] data, uint offset, uint size) - { - for (uint i = 0; i < size; i++) - { - _value = Table[(((byte)(_value)) ^ data[offset + i])] ^ (_value >> 8); - } - } - - public uint GetDigest() - { - return _value ^ 0xFFFFFFFF; - } - - private static uint CalculateDigest(byte[] data, uint offset, uint size) - { - CRC crc = new CRC(); - - // crc.Init(); - crc.Update(data, offset, size); - return crc.GetDigest(); - } - - private static bool VerifyDigest(uint digest, byte[] data, uint offset, uint size) - { - return (CalculateDigest(data, offset, size) == digest); - } - } -} \ No newline at end of file diff --git a/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs b/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs index 79ecceb8..bfa34b84 100644 --- a/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs +++ b/src/SharpCompress/Compressors/LZMA/LZ/LzBinTree.cs @@ -16,34 +16,34 @@ namespace SharpCompress.Compressors.LZMA.LZ private UInt32 _hashMask; private UInt32 _hashSizeSum; - private bool HASH_ARRAY = true; + private bool _hashArray = true; - private const UInt32 kHash2Size = 1 << 10; - private const UInt32 kHash3Size = 1 << 16; - private const UInt32 kBT2HashSize = 1 << 16; - private const UInt32 kStartMaxLen = 1; - private const UInt32 kHash3Offset = kHash2Size; - private const UInt32 kEmptyHashValue = 0; - private const UInt32 kMaxValForNormalize = ((UInt32)1 << 31) - 1; + private const UInt32 K_HASH2_SIZE = 1 << 10; + private const UInt32 K_HASH3_SIZE = 1 << 16; + private const UInt32 K_BT2_HASH_SIZE = 1 << 16; + private const UInt32 K_START_MAX_LEN = 1; + private const UInt32 K_HASH3_OFFSET = K_HASH2_SIZE; + private const UInt32 K_EMPTY_HASH_VALUE = 0; + private const UInt32 K_MAX_VAL_FOR_NORMALIZE = ((UInt32)1 << 31) - 1; - private UInt32 kNumHashDirectBytes; - private UInt32 kMinMatchCheck = 4; - private UInt32 kFixHashSize = kHash2Size + kHash3Size; + private UInt32 _kNumHashDirectBytes; + private UInt32 _kMinMatchCheck = 4; + private UInt32 _kFixHashSize = K_HASH2_SIZE + K_HASH3_SIZE; public void SetType(int numHashBytes) { - HASH_ARRAY = (numHashBytes > 2); - if (HASH_ARRAY) + _hashArray = (numHashBytes > 2); + if (_hashArray) { - kNumHashDirectBytes = 0; - kMinMatchCheck = 4; - kFixHashSize = kHash2Size + kHash3Size; + _kNumHashDirectBytes = 0; + _kMinMatchCheck = 4; + _kFixHashSize = K_HASH2_SIZE + K_HASH3_SIZE; } else { - kNumHashDirectBytes = 2; - kMinMatchCheck = 2 + 1; - kFixHashSize = 0; + _kNumHashDirectBytes = 2; + _kMinMatchCheck = 2 + 1; + _kFixHashSize = 0; } } @@ -62,7 +62,7 @@ namespace SharpCompress.Compressors.LZMA.LZ base.Init(); for (UInt32 i = 0; i < _hashSizeSum; i++) { - _hash[i] = kEmptyHashValue; + _hash[i] = K_EMPTY_HASH_VALUE; } _cyclicBufferPos = 0; ReduceOffsets(-1); @@ -75,7 +75,7 @@ namespace SharpCompress.Compressors.LZMA.LZ _cyclicBufferPos = 0; } base.MovePos(); - if (_pos == kMaxValForNormalize) + if (_pos == K_MAX_VAL_FOR_NORMALIZE) { Normalize(); } @@ -99,7 +99,7 @@ namespace SharpCompress.Compressors.LZMA.LZ public void Create(UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter) { - if (historySize > kMaxValForNormalize - 256) + if (historySize > K_MAX_VAL_FOR_NORMALIZE - 256) { throw new Exception(); } @@ -118,9 +118,9 @@ namespace SharpCompress.Compressors.LZMA.LZ _son = new UInt32[(_cyclicBufferSize = cyclicBufferSize) * 2]; } - UInt32 hs = kBT2HashSize; + UInt32 hs = K_BT2_HASH_SIZE; - if (HASH_ARRAY) + if (_hashArray) { hs = historySize - 1; hs |= (hs >> 1); @@ -135,7 +135,7 @@ namespace SharpCompress.Compressors.LZMA.LZ } _hashMask = hs; hs++; - hs += kFixHashSize; + hs += _kFixHashSize; } if (hs != _hashSizeSum) { @@ -153,7 +153,7 @@ namespace SharpCompress.Compressors.LZMA.LZ else { lenLimit = _streamPos - _pos; - if (lenLimit < kMinMatchCheck) + if (lenLimit < _kMinMatchCheck) { MovePos(); return 0; @@ -163,29 +163,29 @@ namespace SharpCompress.Compressors.LZMA.LZ UInt32 offset = 0; UInt32 matchMinPos = (_pos > _cyclicBufferSize) ? (_pos - _cyclicBufferSize) : 0; UInt32 cur = _bufferOffset + _pos; - UInt32 maxLen = kStartMaxLen; // to avoid items for len < hashSize; + UInt32 maxLen = K_START_MAX_LEN; // to avoid items for len < hashSize; UInt32 hashValue, hash2Value = 0, hash3Value = 0; - if (HASH_ARRAY) + if (_hashArray) { - UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1]; - hash2Value = temp & (kHash2Size - 1); + UInt32 temp = Crc.TABLE[_bufferBase[cur]] ^ _bufferBase[cur + 1]; + hash2Value = temp & (K_HASH2_SIZE - 1); temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8); - hash3Value = temp & (kHash3Size - 1); - hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask; + hash3Value = temp & (K_HASH3_SIZE - 1); + hashValue = (temp ^ (Crc.TABLE[_bufferBase[cur + 3]] << 5)) & _hashMask; } else { hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8); } - UInt32 curMatch = _hash[kFixHashSize + hashValue]; - if (HASH_ARRAY) + UInt32 curMatch = _hash[_kFixHashSize + hashValue]; + if (_hashArray) { UInt32 curMatch2 = _hash[hash2Value]; - UInt32 curMatch3 = _hash[kHash3Offset + hash3Value]; + UInt32 curMatch3 = _hash[K_HASH3_OFFSET + hash3Value]; _hash[hash2Value] = _pos; - _hash[kHash3Offset + hash3Value] = _pos; + _hash[K_HASH3_OFFSET + hash3Value] = _pos; if (curMatch2 > matchMinPos) { if (_bufferBase[_bufferOffset + curMatch2] == _bufferBase[cur]) @@ -210,26 +210,26 @@ namespace SharpCompress.Compressors.LZMA.LZ if (offset != 0 && curMatch2 == curMatch) { offset -= 2; - maxLen = kStartMaxLen; + maxLen = K_START_MAX_LEN; } } - _hash[kFixHashSize + hashValue] = _pos; + _hash[_kFixHashSize + hashValue] = _pos; UInt32 ptr0 = (_cyclicBufferPos << 1) + 1; UInt32 ptr1 = (_cyclicBufferPos << 1); UInt32 len0, len1; - len0 = len1 = kNumHashDirectBytes; + len0 = len1 = _kNumHashDirectBytes; - if (kNumHashDirectBytes != 0) + if (_kNumHashDirectBytes != 0) { if (curMatch > matchMinPos) { - if (_bufferBase[_bufferOffset + curMatch + kNumHashDirectBytes] != - _bufferBase[cur + kNumHashDirectBytes]) + if (_bufferBase[_bufferOffset + curMatch + _kNumHashDirectBytes] != + _bufferBase[cur + _kNumHashDirectBytes]) { - distances[offset++] = maxLen = kNumHashDirectBytes; + distances[offset++] = maxLen = _kNumHashDirectBytes; distances[offset++] = _pos - curMatch - 1; } } @@ -241,7 +241,7 @@ namespace SharpCompress.Compressors.LZMA.LZ { if (curMatch <= matchMinPos || count-- == 0) { - _son[ptr0] = _son[ptr1] = kEmptyHashValue; + _son[ptr0] = _son[ptr1] = K_EMPTY_HASH_VALUE; break; } UInt32 delta = _pos - curMatch; @@ -303,7 +303,7 @@ namespace SharpCompress.Compressors.LZMA.LZ else { lenLimit = _streamPos - _pos; - if (lenLimit < kMinMatchCheck) + if (lenLimit < _kMinMatchCheck) { MovePos(); continue; @@ -315,36 +315,36 @@ namespace SharpCompress.Compressors.LZMA.LZ UInt32 hashValue; - if (HASH_ARRAY) + if (_hashArray) { - UInt32 temp = CRC.Table[_bufferBase[cur]] ^ _bufferBase[cur + 1]; - UInt32 hash2Value = temp & (kHash2Size - 1); + UInt32 temp = Crc.TABLE[_bufferBase[cur]] ^ _bufferBase[cur + 1]; + UInt32 hash2Value = temp & (K_HASH2_SIZE - 1); _hash[hash2Value] = _pos; temp ^= ((UInt32)(_bufferBase[cur + 2]) << 8); - UInt32 hash3Value = temp & (kHash3Size - 1); - _hash[kHash3Offset + hash3Value] = _pos; - hashValue = (temp ^ (CRC.Table[_bufferBase[cur + 3]] << 5)) & _hashMask; + UInt32 hash3Value = temp & (K_HASH3_SIZE - 1); + _hash[K_HASH3_OFFSET + hash3Value] = _pos; + hashValue = (temp ^ (Crc.TABLE[_bufferBase[cur + 3]] << 5)) & _hashMask; } else { hashValue = _bufferBase[cur] ^ ((UInt32)(_bufferBase[cur + 1]) << 8); } - UInt32 curMatch = _hash[kFixHashSize + hashValue]; - _hash[kFixHashSize + hashValue] = _pos; + UInt32 curMatch = _hash[_kFixHashSize + hashValue]; + _hash[_kFixHashSize + hashValue] = _pos; UInt32 ptr0 = (_cyclicBufferPos << 1) + 1; UInt32 ptr1 = (_cyclicBufferPos << 1); UInt32 len0, len1; - len0 = len1 = kNumHashDirectBytes; + len0 = len1 = _kNumHashDirectBytes; UInt32 count = _cutValue; while (true) { if (curMatch <= matchMinPos || count-- == 0) { - _son[ptr0] = _son[ptr1] = kEmptyHashValue; + _son[ptr0] = _son[ptr1] = K_EMPTY_HASH_VALUE; break; } @@ -398,7 +398,7 @@ namespace SharpCompress.Compressors.LZMA.LZ UInt32 value = items[i]; if (value <= subValue) { - value = kEmptyHashValue; + value = K_EMPTY_HASH_VALUE; } else { diff --git a/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs b/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs index 1b230873..6736dc1a 100644 --- a/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs +++ b/src/SharpCompress/Compressors/LZMA/LZ/LzOutWindow.cs @@ -13,8 +13,8 @@ namespace SharpCompress.Compressors.LZMA.LZ private int _pendingDist; private Stream _stream; - public long Total; - public long Limit; + public long _total; + public long _limit; public void Create(int windowSize) { @@ -30,8 +30,8 @@ namespace SharpCompress.Compressors.LZMA.LZ _pos = 0; _streamPos = 0; _pendingLen = 0; - Total = 0; - Limit = 0; + _total = 0; + _limit = 0; } public void Reset() @@ -50,8 +50,8 @@ namespace SharpCompress.Compressors.LZMA.LZ long len = stream.Length; int size = (len < _windowSize) ? (int)len : _windowSize; stream.Position = len - size; - Total = 0; - Limit = size; + _total = 0; + _limit = size; _pos = _windowSize - size; CopyStream(stream, size); if (_pos == _windowSize) @@ -94,14 +94,14 @@ namespace SharpCompress.Compressors.LZMA.LZ { pos += _windowSize; } - for (; size > 0 && _pos < _windowSize && Total < Limit; size--) + for (; size > 0 && _pos < _windowSize && _total < _limit; size--) { if (pos >= _windowSize) { pos = 0; } _buffer[_pos++] = _buffer[pos++]; - Total++; + _total++; if (_pos >= _windowSize) { Flush(); @@ -114,7 +114,7 @@ namespace SharpCompress.Compressors.LZMA.LZ public void PutByte(byte b) { _buffer[_pos++] = b; - Total++; + _total++; if (_pos >= _windowSize) { Flush(); @@ -134,12 +134,12 @@ namespace SharpCompress.Compressors.LZMA.LZ public int CopyStream(Stream stream, int len) { int size = len; - while (size > 0 && _pos < _windowSize && Total < Limit) + while (size > 0 && _pos < _windowSize && _total < _limit) { int curSize = _windowSize - _pos; - if (curSize > Limit - Total) + if (curSize > _limit - _total) { - curSize = (int)(Limit - Total); + curSize = (int)(_limit - _total); } if (curSize > size) { @@ -152,7 +152,7 @@ namespace SharpCompress.Compressors.LZMA.LZ } size -= numReadBytes; _pos += numReadBytes; - Total += numReadBytes; + _total += numReadBytes; if (_pos >= _windowSize) { Flush(); @@ -163,10 +163,10 @@ namespace SharpCompress.Compressors.LZMA.LZ public void SetLimit(long size) { - Limit = Total + size; + _limit = _total + size; } - public bool HasSpace => _pos < _windowSize && Total < Limit; + public bool HasSpace => _pos < _windowSize && _total < _limit; public bool HasPending => _pendingLen > 0; diff --git a/src/SharpCompress/Compressors/LZMA/Log.cs b/src/SharpCompress/Compressors/LZMA/Log.cs index 458abbcc..f3b4eab5 100644 --- a/src/SharpCompress/Compressors/LZMA/Log.cs +++ b/src/SharpCompress/Compressors/LZMA/Log.cs @@ -6,36 +6,36 @@ namespace SharpCompress.Compressors.LZMA { internal static class Log { - private static readonly Stack _indent = new Stack(); - private static bool _needsIndent = true; + private static readonly Stack INDENT = new Stack(); + private static bool NEEDS_INDENT = true; static Log() { - _indent.Push(""); + INDENT.Push(""); } public static void PushIndent(string indent = " ") { - _indent.Push(_indent.Peek() + indent); + INDENT.Push(INDENT.Peek() + indent); } public static void PopIndent() { - if (_indent.Count == 1) + if (INDENT.Count == 1) { throw new InvalidOperationException(); } - _indent.Pop(); + INDENT.Pop(); } private static void EnsureIndent() { - if (_needsIndent) + if (NEEDS_INDENT) { - _needsIndent = false; + NEEDS_INDENT = false; #if !NO_FILE - Debug.Write(_indent.Peek()); + Debug.Write(INDENT.Peek()); #endif } } @@ -67,28 +67,28 @@ namespace SharpCompress.Compressors.LZMA public static void WriteLine() { Debug.WriteLine(""); - _needsIndent = true; + NEEDS_INDENT = true; } public static void WriteLine(object value) { EnsureIndent(); Debug.WriteLine(value); - _needsIndent = true; + NEEDS_INDENT = true; } public static void WriteLine(string text) { EnsureIndent(); Debug.WriteLine(text); - _needsIndent = true; + NEEDS_INDENT = true; } public static void WriteLine(string format, params object[] args) { EnsureIndent(); Debug.WriteLine(string.Format(format, args)); - _needsIndent = true; + NEEDS_INDENT = true; } } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/LZMA/LzmaBase.cs b/src/SharpCompress/Compressors/LZMA/LzmaBase.cs index e272607f..6e2b8a5d 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaBase.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaBase.cs @@ -2,8 +2,8 @@ namespace SharpCompress.Compressors.LZMA { internal abstract class Base { - public const uint kNumRepDistances = 4; - public const uint kNumStates = 12; + public const uint K_NUM_REP_DISTANCES = 4; + public const uint K_NUM_STATES = 12; // static byte []kLiteralNextStates = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; // static byte []kMatchNextStates = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; @@ -12,98 +12,98 @@ namespace SharpCompress.Compressors.LZMA public struct State { - public uint Index; + public uint _index; public void Init() { - Index = 0; + _index = 0; } public void UpdateChar() { - if (Index < 4) + if (_index < 4) { - Index = 0; + _index = 0; } - else if (Index < 10) + else if (_index < 10) { - Index -= 3; + _index -= 3; } else { - Index -= 6; + _index -= 6; } } public void UpdateMatch() { - Index = (uint)(Index < 7 ? 7 : 10); + _index = (uint)(_index < 7 ? 7 : 10); } public void UpdateRep() { - Index = (uint)(Index < 7 ? 8 : 11); + _index = (uint)(_index < 7 ? 8 : 11); } public void UpdateShortRep() { - Index = (uint)(Index < 7 ? 9 : 11); + _index = (uint)(_index < 7 ? 9 : 11); } public bool IsCharState() { - return Index < 7; + return _index < 7; } } - public const int kNumPosSlotBits = 6; - public const int kDicLogSizeMin = 0; + public const int K_NUM_POS_SLOT_BITS = 6; + public const int K_DIC_LOG_SIZE_MIN = 0; // public const int kDicLogSizeMax = 30; // public const uint kDistTableSizeMax = kDicLogSizeMax * 2; - public const int kNumLenToPosStatesBits = 2; // it's for speed optimization - public const uint kNumLenToPosStates = 1 << kNumLenToPosStatesBits; + public const int K_NUM_LEN_TO_POS_STATES_BITS = 2; // it's for speed optimization + public const uint K_NUM_LEN_TO_POS_STATES = 1 << K_NUM_LEN_TO_POS_STATES_BITS; - public const uint kMatchMinLen = 2; + public const uint K_MATCH_MIN_LEN = 2; public static uint GetLenToPosState(uint len) { - len -= kMatchMinLen; - if (len < kNumLenToPosStates) + len -= K_MATCH_MIN_LEN; + if (len < K_NUM_LEN_TO_POS_STATES) { return len; } - return kNumLenToPosStates - 1; + return K_NUM_LEN_TO_POS_STATES - 1; } - public const int kNumAlignBits = 4; - public const uint kAlignTableSize = 1 << kNumAlignBits; - public const uint kAlignMask = (kAlignTableSize - 1); + public const int K_NUM_ALIGN_BITS = 4; + public const uint K_ALIGN_TABLE_SIZE = 1 << K_NUM_ALIGN_BITS; + public const uint K_ALIGN_MASK = (K_ALIGN_TABLE_SIZE - 1); - public const uint kStartPosModelIndex = 4; - public const uint kEndPosModelIndex = 14; - public const uint kNumPosModels = kEndPosModelIndex - kStartPosModelIndex; + public const uint K_START_POS_MODEL_INDEX = 4; + public const uint K_END_POS_MODEL_INDEX = 14; + public const uint K_NUM_POS_MODELS = K_END_POS_MODEL_INDEX - K_START_POS_MODEL_INDEX; - public const uint kNumFullDistances = 1 << ((int)kEndPosModelIndex / 2); + public const uint K_NUM_FULL_DISTANCES = 1 << ((int)K_END_POS_MODEL_INDEX / 2); - public const uint kNumLitPosStatesBitsEncodingMax = 4; - public const uint kNumLitContextBitsMax = 8; + public const uint K_NUM_LIT_POS_STATES_BITS_ENCODING_MAX = 4; + public const uint K_NUM_LIT_CONTEXT_BITS_MAX = 8; - public const int kNumPosStatesBitsMax = 4; - public const uint kNumPosStatesMax = (1 << kNumPosStatesBitsMax); - public const int kNumPosStatesBitsEncodingMax = 4; - public const uint kNumPosStatesEncodingMax = (1 << kNumPosStatesBitsEncodingMax); + public const int K_NUM_POS_STATES_BITS_MAX = 4; + public const uint K_NUM_POS_STATES_MAX = (1 << K_NUM_POS_STATES_BITS_MAX); + public const int K_NUM_POS_STATES_BITS_ENCODING_MAX = 4; + public const uint K_NUM_POS_STATES_ENCODING_MAX = (1 << K_NUM_POS_STATES_BITS_ENCODING_MAX); - public const int kNumLowLenBits = 3; - public const int kNumMidLenBits = 3; - public const int kNumHighLenBits = 8; - public const uint kNumLowLenSymbols = 1 << kNumLowLenBits; - public const uint kNumMidLenSymbols = 1 << kNumMidLenBits; + public const int K_NUM_LOW_LEN_BITS = 3; + public const int K_NUM_MID_LEN_BITS = 3; + public const int K_NUM_HIGH_LEN_BITS = 8; + public const uint K_NUM_LOW_LEN_SYMBOLS = 1 << K_NUM_LOW_LEN_BITS; + public const uint K_NUM_MID_LEN_SYMBOLS = 1 << K_NUM_MID_LEN_BITS; - public const uint kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols + - (1 << kNumHighLenBits); + public const uint K_NUM_LEN_SYMBOLS = K_NUM_LOW_LEN_SYMBOLS + K_NUM_MID_LEN_SYMBOLS + + (1 << K_NUM_HIGH_LEN_BITS); - public const uint kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1; + public const uint K_MATCH_MAX_LEN = K_MATCH_MIN_LEN + K_NUM_LEN_SYMBOLS - 1; } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs index dbea98c3..896e92cf 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaDecoder.cs @@ -9,50 +9,50 @@ namespace SharpCompress.Compressors.LZMA { private class LenDecoder { - private BitDecoder _Choice = new BitDecoder(); - private BitDecoder _Choice2 = new BitDecoder(); - private readonly BitTreeDecoder[] _LowCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; - private readonly BitTreeDecoder[] _MidCoder = new BitTreeDecoder[Base.kNumPosStatesMax]; - private BitTreeDecoder _HighCoder = new BitTreeDecoder(Base.kNumHighLenBits); - private uint _NumPosStates; + private BitDecoder _choice = new BitDecoder(); + private BitDecoder _choice2 = new BitDecoder(); + private readonly BitTreeDecoder[] _lowCoder = new BitTreeDecoder[Base.K_NUM_POS_STATES_MAX]; + private readonly BitTreeDecoder[] _midCoder = new BitTreeDecoder[Base.K_NUM_POS_STATES_MAX]; + private BitTreeDecoder _highCoder = new BitTreeDecoder(Base.K_NUM_HIGH_LEN_BITS); + private uint _numPosStates; public void Create(uint numPosStates) { - for (uint posState = _NumPosStates; posState < numPosStates; posState++) + for (uint posState = _numPosStates; posState < numPosStates; posState++) { - _LowCoder[posState] = new BitTreeDecoder(Base.kNumLowLenBits); - _MidCoder[posState] = new BitTreeDecoder(Base.kNumMidLenBits); + _lowCoder[posState] = new BitTreeDecoder(Base.K_NUM_LOW_LEN_BITS); + _midCoder[posState] = new BitTreeDecoder(Base.K_NUM_MID_LEN_BITS); } - _NumPosStates = numPosStates; + _numPosStates = numPosStates; } public void Init() { - _Choice.Init(); - for (uint posState = 0; posState < _NumPosStates; posState++) + _choice.Init(); + for (uint posState = 0; posState < _numPosStates; posState++) { - _LowCoder[posState].Init(); - _MidCoder[posState].Init(); + _lowCoder[posState].Init(); + _midCoder[posState].Init(); } - _Choice2.Init(); - _HighCoder.Init(); + _choice2.Init(); + _highCoder.Init(); } public uint Decode(RangeCoder.Decoder rangeDecoder, uint posState) { - if (_Choice.Decode(rangeDecoder) == 0) + if (_choice.Decode(rangeDecoder) == 0) { - return _LowCoder[posState].Decode(rangeDecoder); + return _lowCoder[posState].Decode(rangeDecoder); } - uint symbol = Base.kNumLowLenSymbols; - if (_Choice2.Decode(rangeDecoder) == 0) + uint symbol = Base.K_NUM_LOW_LEN_SYMBOLS; + if (_choice2.Decode(rangeDecoder) == 0) { - symbol += _MidCoder[posState].Decode(rangeDecoder); + symbol += _midCoder[posState].Decode(rangeDecoder); } else { - symbol += Base.kNumMidLenSymbols; - symbol += _HighCoder.Decode(rangeDecoder); + symbol += Base.K_NUM_MID_LEN_SYMBOLS; + symbol += _highCoder.Decode(rangeDecoder); } return symbol; } @@ -62,18 +62,18 @@ namespace SharpCompress.Compressors.LZMA { private struct Decoder2 { - private BitDecoder[] _Decoders; + private BitDecoder[] _decoders; public void Create() { - _Decoders = new BitDecoder[0x300]; + _decoders = new BitDecoder[0x300]; } public void Init() { for (int i = 0; i < 0x300; i++) { - _Decoders[i].Init(); + _decoders[i].Init(); } } @@ -82,7 +82,7 @@ namespace SharpCompress.Compressors.LZMA uint symbol = 1; do { - symbol = (symbol << 1) | _Decoders[symbol].Decode(rangeDecoder); + symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder); } while (symbol < 0x100); return (byte)symbol; @@ -95,13 +95,13 @@ namespace SharpCompress.Compressors.LZMA { uint matchBit = (uint)(matchByte >> 7) & 1; matchByte <<= 1; - uint bit = _Decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); + uint bit = _decoders[((1 + matchBit) << 8) + symbol].Decode(rangeDecoder); symbol = (symbol << 1) | bit; if (matchBit != bit) { while (symbol < 0x100) { - symbol = (symbol << 1) | _Decoders[symbol].Decode(rangeDecoder); + symbol = (symbol << 1) | _decoders[symbol].Decode(rangeDecoder); } break; } @@ -111,98 +111,98 @@ namespace SharpCompress.Compressors.LZMA } } - private Decoder2[] _Coders; - private int _NumPrevBits; - private int _NumPosBits; - private uint _PosMask; + private Decoder2[] _coders; + private int _numPrevBits; + private int _numPosBits; + private uint _posMask; public void Create(int numPosBits, int numPrevBits) { - if (_Coders != null && _NumPrevBits == numPrevBits && - _NumPosBits == numPosBits) + if (_coders != null && _numPrevBits == numPrevBits && + _numPosBits == numPosBits) { return; } - _NumPosBits = numPosBits; - _PosMask = ((uint)1 << numPosBits) - 1; - _NumPrevBits = numPrevBits; - uint numStates = (uint)1 << (_NumPrevBits + _NumPosBits); - _Coders = new Decoder2[numStates]; + _numPosBits = numPosBits; + _posMask = ((uint)1 << numPosBits) - 1; + _numPrevBits = numPrevBits; + uint numStates = (uint)1 << (_numPrevBits + _numPosBits); + _coders = new Decoder2[numStates]; for (uint i = 0; i < numStates; i++) { - _Coders[i].Create(); + _coders[i].Create(); } } public void Init() { - uint numStates = (uint)1 << (_NumPrevBits + _NumPosBits); + uint numStates = (uint)1 << (_numPrevBits + _numPosBits); for (uint i = 0; i < numStates; i++) { - _Coders[i].Init(); + _coders[i].Init(); } } private uint GetState(uint pos, byte prevByte) { - return ((pos & _PosMask) << _NumPrevBits) + (uint)(prevByte >> (8 - _NumPrevBits)); + return ((pos & _posMask) << _numPrevBits) + (uint)(prevByte >> (8 - _numPrevBits)); } public byte DecodeNormal(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte) { - return _Coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); + return _coders[GetState(pos, prevByte)].DecodeNormal(rangeDecoder); } public byte DecodeWithMatchByte(RangeCoder.Decoder rangeDecoder, uint pos, byte prevByte, byte matchByte) { - return _Coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); + return _coders[GetState(pos, prevByte)].DecodeWithMatchByte(rangeDecoder, matchByte); } } - private OutWindow _OutWindow; + private OutWindow _outWindow; - private readonly BitDecoder[] _IsMatchDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; - private readonly BitDecoder[] _IsRepDecoders = new BitDecoder[Base.kNumStates]; - private readonly BitDecoder[] _IsRepG0Decoders = new BitDecoder[Base.kNumStates]; - private readonly BitDecoder[] _IsRepG1Decoders = new BitDecoder[Base.kNumStates]; - private readonly BitDecoder[] _IsRepG2Decoders = new BitDecoder[Base.kNumStates]; - private readonly BitDecoder[] _IsRep0LongDecoders = new BitDecoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + private readonly BitDecoder[] _isMatchDecoders = new BitDecoder[Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX]; + private readonly BitDecoder[] _isRepDecoders = new BitDecoder[Base.K_NUM_STATES]; + private readonly BitDecoder[] _isRepG0Decoders = new BitDecoder[Base.K_NUM_STATES]; + private readonly BitDecoder[] _isRepG1Decoders = new BitDecoder[Base.K_NUM_STATES]; + private readonly BitDecoder[] _isRepG2Decoders = new BitDecoder[Base.K_NUM_STATES]; + private readonly BitDecoder[] _isRep0LongDecoders = new BitDecoder[Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX]; - private readonly BitTreeDecoder[] _PosSlotDecoder = new BitTreeDecoder[Base.kNumLenToPosStates]; - private readonly BitDecoder[] _PosDecoders = new BitDecoder[Base.kNumFullDistances - Base.kEndPosModelIndex]; + private readonly BitTreeDecoder[] _posSlotDecoder = new BitTreeDecoder[Base.K_NUM_LEN_TO_POS_STATES]; + private readonly BitDecoder[] _posDecoders = new BitDecoder[Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX]; - private BitTreeDecoder _PosAlignDecoder = new BitTreeDecoder(Base.kNumAlignBits); + private BitTreeDecoder _posAlignDecoder = new BitTreeDecoder(Base.K_NUM_ALIGN_BITS); - private readonly LenDecoder _LenDecoder = new LenDecoder(); - private readonly LenDecoder _RepLenDecoder = new LenDecoder(); + private readonly LenDecoder _lenDecoder = new LenDecoder(); + private readonly LenDecoder _repLenDecoder = new LenDecoder(); - private readonly LiteralDecoder _LiteralDecoder = new LiteralDecoder(); + private readonly LiteralDecoder _literalDecoder = new LiteralDecoder(); - private int _DictionarySize; + private int _dictionarySize; - private uint _PosStateMask; + private uint _posStateMask; - private Base.State state = new Base.State(); - private uint rep0, rep1, rep2, rep3; + private Base.State _state = new Base.State(); + private uint _rep0, _rep1, _rep2, _rep3; public Decoder() { - _DictionarySize = -1; - for (int i = 0; i < Base.kNumLenToPosStates; i++) + _dictionarySize = -1; + for (int i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) { - _PosSlotDecoder[i] = new BitTreeDecoder(Base.kNumPosSlotBits); + _posSlotDecoder[i] = new BitTreeDecoder(Base.K_NUM_POS_SLOT_BITS); } } private void CreateDictionary() { - if (_DictionarySize < 0) + if (_dictionarySize < 0) { throw new InvalidParamException(); } - _OutWindow = new OutWindow(); - int blockSize = Math.Max(_DictionarySize, (1 << 12)); - _OutWindow.Create(blockSize); + _outWindow = new OutWindow(); + int blockSize = Math.Max(_dictionarySize, (1 << 12)); + _outWindow.Create(blockSize); } private void SetLiteralProperties(int lp, int lc) @@ -215,95 +215,95 @@ namespace SharpCompress.Compressors.LZMA { throw new InvalidParamException(); } - _LiteralDecoder.Create(lp, lc); + _literalDecoder.Create(lp, lc); } private void SetPosBitsProperties(int pb) { - if (pb > Base.kNumPosStatesBitsMax) + if (pb > Base.K_NUM_POS_STATES_BITS_MAX) { throw new InvalidParamException(); } uint numPosStates = (uint)1 << pb; - _LenDecoder.Create(numPosStates); - _RepLenDecoder.Create(numPosStates); - _PosStateMask = numPosStates - 1; + _lenDecoder.Create(numPosStates); + _repLenDecoder.Create(numPosStates); + _posStateMask = numPosStates - 1; } private void Init() { uint i; - for (i = 0; i < Base.kNumStates; i++) + for (i = 0; i < Base.K_NUM_STATES; i++) { - for (uint j = 0; j <= _PosStateMask; j++) + for (uint j = 0; j <= _posStateMask; j++) { - uint index = (i << Base.kNumPosStatesBitsMax) + j; - _IsMatchDecoders[index].Init(); - _IsRep0LongDecoders[index].Init(); + uint index = (i << Base.K_NUM_POS_STATES_BITS_MAX) + j; + _isMatchDecoders[index].Init(); + _isRep0LongDecoders[index].Init(); } - _IsRepDecoders[i].Init(); - _IsRepG0Decoders[i].Init(); - _IsRepG1Decoders[i].Init(); - _IsRepG2Decoders[i].Init(); + _isRepDecoders[i].Init(); + _isRepG0Decoders[i].Init(); + _isRepG1Decoders[i].Init(); + _isRepG2Decoders[i].Init(); } - _LiteralDecoder.Init(); - for (i = 0; i < Base.kNumLenToPosStates; i++) + _literalDecoder.Init(); + for (i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) { - _PosSlotDecoder[i].Init(); + _posSlotDecoder[i].Init(); } // _PosSpecDecoder.Init(); - for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++) + for (i = 0; i < Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX; i++) { - _PosDecoders[i].Init(); + _posDecoders[i].Init(); } - _LenDecoder.Init(); - _RepLenDecoder.Init(); - _PosAlignDecoder.Init(); + _lenDecoder.Init(); + _repLenDecoder.Init(); + _posAlignDecoder.Init(); - state.Init(); - rep0 = 0; - rep1 = 0; - rep2 = 0; - rep3 = 0; + _state.Init(); + _rep0 = 0; + _rep1 = 0; + _rep2 = 0; + _rep3 = 0; } public void Code(Stream inStream, Stream outStream, Int64 inSize, Int64 outSize, ICodeProgress progress) { - if (_OutWindow == null) + if (_outWindow == null) { CreateDictionary(); } - _OutWindow.Init(outStream); + _outWindow.Init(outStream); if (outSize > 0) { - _OutWindow.SetLimit(outSize); + _outWindow.SetLimit(outSize); } else { - _OutWindow.SetLimit(Int64.MaxValue - _OutWindow.Total); + _outWindow.SetLimit(Int64.MaxValue - _outWindow._total); } RangeCoder.Decoder rangeDecoder = new RangeCoder.Decoder(); rangeDecoder.Init(inStream); - Code(_DictionarySize, _OutWindow, rangeDecoder); + Code(_dictionarySize, _outWindow, rangeDecoder); - _OutWindow.ReleaseStream(); + _outWindow.ReleaseStream(); rangeDecoder.ReleaseStream(); - if (!rangeDecoder.IsFinished || (inSize > 0 && rangeDecoder.Total != inSize)) + if (!rangeDecoder.IsFinished || (inSize > 0 && rangeDecoder._total != inSize)) { throw new DataErrorException(); } - if (_OutWindow.HasPending) + if (_outWindow.HasPending) { throw new DataErrorException(); } - _OutWindow = null; + _outWindow = null; } internal bool Code(int dictionarySize, OutWindow outWindow, RangeCoder.Decoder rangeDecoder) @@ -314,104 +314,104 @@ namespace SharpCompress.Compressors.LZMA while (outWindow.HasSpace) { - uint posState = (uint)outWindow.Total & _PosStateMask; - if (_IsMatchDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode(rangeDecoder) == 0) + uint posState = (uint)outWindow._total & _posStateMask; + if (_isMatchDecoders[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Decode(rangeDecoder) == 0) { byte b; byte prevByte = outWindow.GetByte(0); - if (!state.IsCharState()) + if (!_state.IsCharState()) { - b = _LiteralDecoder.DecodeWithMatchByte(rangeDecoder, - (uint)outWindow.Total, prevByte, - outWindow.GetByte((int)rep0)); + b = _literalDecoder.DecodeWithMatchByte(rangeDecoder, + (uint)outWindow._total, prevByte, + outWindow.GetByte((int)_rep0)); } else { - b = _LiteralDecoder.DecodeNormal(rangeDecoder, (uint)outWindow.Total, prevByte); + b = _literalDecoder.DecodeNormal(rangeDecoder, (uint)outWindow._total, prevByte); } outWindow.PutByte(b); - state.UpdateChar(); + _state.UpdateChar(); } else { uint len; - if (_IsRepDecoders[state.Index].Decode(rangeDecoder) == 1) + if (_isRepDecoders[_state._index].Decode(rangeDecoder) == 1) { - if (_IsRepG0Decoders[state.Index].Decode(rangeDecoder) == 0) + if (_isRepG0Decoders[_state._index].Decode(rangeDecoder) == 0) { if ( - _IsRep0LongDecoders[(state.Index << Base.kNumPosStatesBitsMax) + posState].Decode( + _isRep0LongDecoders[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Decode( rangeDecoder) == 0) { - state.UpdateShortRep(); - outWindow.PutByte(outWindow.GetByte((int)rep0)); + _state.UpdateShortRep(); + outWindow.PutByte(outWindow.GetByte((int)_rep0)); continue; } } else { UInt32 distance; - if (_IsRepG1Decoders[state.Index].Decode(rangeDecoder) == 0) + if (_isRepG1Decoders[_state._index].Decode(rangeDecoder) == 0) { - distance = rep1; + distance = _rep1; } else { - if (_IsRepG2Decoders[state.Index].Decode(rangeDecoder) == 0) + if (_isRepG2Decoders[_state._index].Decode(rangeDecoder) == 0) { - distance = rep2; + distance = _rep2; } else { - distance = rep3; - rep3 = rep2; + distance = _rep3; + _rep3 = _rep2; } - rep2 = rep1; + _rep2 = _rep1; } - rep1 = rep0; - rep0 = distance; + _rep1 = _rep0; + _rep0 = distance; } - len = _RepLenDecoder.Decode(rangeDecoder, posState) + Base.kMatchMinLen; - state.UpdateRep(); + len = _repLenDecoder.Decode(rangeDecoder, posState) + Base.K_MATCH_MIN_LEN; + _state.UpdateRep(); } else { - rep3 = rep2; - rep2 = rep1; - rep1 = rep0; - len = Base.kMatchMinLen + _LenDecoder.Decode(rangeDecoder, posState); - state.UpdateMatch(); - uint posSlot = _PosSlotDecoder[Base.GetLenToPosState(len)].Decode(rangeDecoder); - if (posSlot >= Base.kStartPosModelIndex) + _rep3 = _rep2; + _rep2 = _rep1; + _rep1 = _rep0; + len = Base.K_MATCH_MIN_LEN + _lenDecoder.Decode(rangeDecoder, posState); + _state.UpdateMatch(); + uint posSlot = _posSlotDecoder[Base.GetLenToPosState(len)].Decode(rangeDecoder); + if (posSlot >= Base.K_START_POS_MODEL_INDEX) { int numDirectBits = (int)((posSlot >> 1) - 1); - rep0 = ((2 | (posSlot & 1)) << numDirectBits); - if (posSlot < Base.kEndPosModelIndex) + _rep0 = ((2 | (posSlot & 1)) << numDirectBits); + if (posSlot < Base.K_END_POS_MODEL_INDEX) { - rep0 += BitTreeDecoder.ReverseDecode(_PosDecoders, - rep0 - posSlot - 1, rangeDecoder, numDirectBits); + _rep0 += BitTreeDecoder.ReverseDecode(_posDecoders, + _rep0 - posSlot - 1, rangeDecoder, numDirectBits); } else { - rep0 += (rangeDecoder.DecodeDirectBits( - numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits); - rep0 += _PosAlignDecoder.ReverseDecode(rangeDecoder); + _rep0 += (rangeDecoder.DecodeDirectBits( + numDirectBits - Base.K_NUM_ALIGN_BITS) << Base.K_NUM_ALIGN_BITS); + _rep0 += _posAlignDecoder.ReverseDecode(rangeDecoder); } } else { - rep0 = posSlot; + _rep0 = posSlot; } } - if (rep0 >= outWindow.Total || rep0 >= dictionarySizeCheck) + if (_rep0 >= outWindow._total || _rep0 >= dictionarySizeCheck) { - if (rep0 == 0xFFFFFFFF) + if (_rep0 == 0xFFFFFFFF) { return true; } throw new DataErrorException(); } - outWindow.CopyBlock((int)rep0, (int)len); + outWindow.CopyBlock((int)_rep0, (int)len); } } return false; @@ -427,7 +427,7 @@ namespace SharpCompress.Compressors.LZMA int remainder = properties[0] / 9; int lp = remainder % 5; int pb = remainder / 5; - if (pb > Base.kNumPosStatesBitsMax) + if (pb > Base.K_NUM_POS_STATES_BITS_MAX) { throw new InvalidParamException(); } @@ -436,21 +436,21 @@ namespace SharpCompress.Compressors.LZMA Init(); if (properties.Length >= 5) { - _DictionarySize = 0; + _dictionarySize = 0; for (int i = 0; i < 4; i++) { - _DictionarySize += properties[1 + i] << (i * 8); + _dictionarySize += properties[1 + i] << (i * 8); } } } public void Train(Stream stream) { - if (_OutWindow == null) + if (_outWindow == null) { CreateDictionary(); } - _OutWindow.Train(stream); + _outWindow.Train(stream); } /* diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs index 666eb690..be604d32 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoder.cs @@ -9,26 +9,26 @@ namespace SharpCompress.Compressors.LZMA { private enum EMatchFinderType { - BT2, - BT4 + Bt2, + Bt4 } - private const UInt32 kIfinityPrice = 0xFFFFFFF; + private const UInt32 K_IFINITY_PRICE = 0xFFFFFFF; - private static readonly Byte[] g_FastPos = new Byte[1 << 11]; + private static readonly Byte[] G_FAST_POS = new Byte[1 << 11]; static Encoder() { const Byte kFastSlots = 22; int c = 2; - g_FastPos[0] = 0; - g_FastPos[1] = 1; + G_FAST_POS[0] = 0; + G_FAST_POS[1] = 1; for (Byte slotFast = 2; slotFast < kFastSlots; slotFast++) { UInt32 k = ((UInt32)1 << ((slotFast >> 1) - 1)); for (UInt32 j = 0; j < k; j++, c++) { - g_FastPos[c] = slotFast; + G_FAST_POS[c] = slotFast; } } } @@ -37,61 +37,61 @@ namespace SharpCompress.Compressors.LZMA { if (pos < (1 << 11)) { - return g_FastPos[pos]; + return G_FAST_POS[pos]; } if (pos < (1 << 21)) { - return (UInt32)(g_FastPos[pos >> 10] + 20); + return (UInt32)(G_FAST_POS[pos >> 10] + 20); } - return (UInt32)(g_FastPos[pos >> 20] + 40); + return (UInt32)(G_FAST_POS[pos >> 20] + 40); } private static UInt32 GetPosSlot2(UInt32 pos) { if (pos < (1 << 17)) { - return (UInt32)(g_FastPos[pos >> 6] + 12); + return (UInt32)(G_FAST_POS[pos >> 6] + 12); } if (pos < (1 << 27)) { - return (UInt32)(g_FastPos[pos >> 16] + 32); + return (UInt32)(G_FAST_POS[pos >> 16] + 32); } - return (UInt32)(g_FastPos[pos >> 26] + 52); + return (UInt32)(G_FAST_POS[pos >> 26] + 52); } private Base.State _state = new Base.State(); private Byte _previousByte; - private readonly UInt32[] _repDistances = new UInt32[Base.kNumRepDistances]; + private readonly UInt32[] _repDistances = new UInt32[Base.K_NUM_REP_DISTANCES]; private void BaseInit() { _state.Init(); _previousByte = 0; - for (UInt32 i = 0; i < Base.kNumRepDistances; i++) + for (UInt32 i = 0; i < Base.K_NUM_REP_DISTANCES; i++) { _repDistances[i] = 0; } } - private const int kDefaultDictionaryLogSize = 22; - private const UInt32 kNumFastBytesDefault = 0x20; + private const int K_DEFAULT_DICTIONARY_LOG_SIZE = 22; + private const UInt32 K_NUM_FAST_BYTES_DEFAULT = 0x20; private class LiteralEncoder { public struct Encoder2 { - private BitEncoder[] _Encoders; + private BitEncoder[] _encoders; public void Create() { - _Encoders = new BitEncoder[0x300]; + _encoders = new BitEncoder[0x300]; } public void Init() { for (int i = 0; i < 0x300; i++) { - _Encoders[i].Init(); + _encoders[i].Init(); } } @@ -101,7 +101,7 @@ namespace SharpCompress.Compressors.LZMA for (int i = 7; i >= 0; i--) { uint bit = (uint)((symbol >> i) & 1); - _Encoders[context].Encode(rangeEncoder, bit); + _encoders[context].Encode(rangeEncoder, bit); context = (context << 1) | bit; } } @@ -120,7 +120,7 @@ namespace SharpCompress.Compressors.LZMA state += ((1 + matchBit) << 8); same = (matchBit == bit); } - _Encoders[state].Encode(rangeEncoder, bit); + _encoders[state].Encode(rangeEncoder, bit); context = (context << 1) | bit; } } @@ -136,7 +136,7 @@ namespace SharpCompress.Compressors.LZMA { uint matchBit = (uint)(matchByte >> i) & 1; uint bit = (uint)(symbol >> i) & 1; - price += _Encoders[((1 + matchBit) << 8) + context].GetPrice(bit); + price += _encoders[((1 + matchBit) << 8) + context].GetPrice(bit); context = (context << 1) | bit; if (matchBit != bit) { @@ -148,47 +148,47 @@ namespace SharpCompress.Compressors.LZMA for (; i >= 0; i--) { uint bit = (uint)(symbol >> i) & 1; - price += _Encoders[context].GetPrice(bit); + price += _encoders[context].GetPrice(bit); context = (context << 1) | bit; } return price; } } - private Encoder2[] _Coders; - private int _NumPrevBits; - private int _NumPosBits; - private uint _PosMask; + private Encoder2[] _coders; + private int _numPrevBits; + private int _numPosBits; + private uint _posMask; public void Create(int numPosBits, int numPrevBits) { - if (_Coders != null && _NumPrevBits == numPrevBits && _NumPosBits == numPosBits) + if (_coders != null && _numPrevBits == numPrevBits && _numPosBits == numPosBits) { return; } - _NumPosBits = numPosBits; - _PosMask = ((uint)1 << numPosBits) - 1; - _NumPrevBits = numPrevBits; - uint numStates = (uint)1 << (_NumPrevBits + _NumPosBits); - _Coders = new Encoder2[numStates]; + _numPosBits = numPosBits; + _posMask = ((uint)1 << numPosBits) - 1; + _numPrevBits = numPrevBits; + uint numStates = (uint)1 << (_numPrevBits + _numPosBits); + _coders = new Encoder2[numStates]; for (uint i = 0; i < numStates; i++) { - _Coders[i].Create(); + _coders[i].Create(); } } public void Init() { - uint numStates = (uint)1 << (_NumPrevBits + _NumPosBits); + uint numStates = (uint)1 << (_numPrevBits + _numPosBits); for (uint i = 0; i < numStates; i++) { - _Coders[i].Init(); + _coders[i].Init(); } } public Encoder2 GetSubCoder(UInt32 pos, Byte prevByte) { - return _Coders[((pos & _PosMask) << _NumPrevBits) + (uint)(prevByte >> (8 - _NumPrevBits))]; + return _coders[((pos & _posMask) << _numPrevBits) + (uint)(prevByte >> (8 - _numPrevBits))]; } } @@ -196,16 +196,16 @@ namespace SharpCompress.Compressors.LZMA { private BitEncoder _choice = new BitEncoder(); private BitEncoder _choice2 = new BitEncoder(); - private readonly BitTreeEncoder[] _lowCoder = new BitTreeEncoder[Base.kNumPosStatesEncodingMax]; - private readonly BitTreeEncoder[] _midCoder = new BitTreeEncoder[Base.kNumPosStatesEncodingMax]; - private BitTreeEncoder _highCoder = new BitTreeEncoder(Base.kNumHighLenBits); + private readonly BitTreeEncoder[] _lowCoder = new BitTreeEncoder[Base.K_NUM_POS_STATES_ENCODING_MAX]; + private readonly BitTreeEncoder[] _midCoder = new BitTreeEncoder[Base.K_NUM_POS_STATES_ENCODING_MAX]; + private BitTreeEncoder _highCoder = new BitTreeEncoder(Base.K_NUM_HIGH_LEN_BITS); public LenEncoder() { - for (UInt32 posState = 0; posState < Base.kNumPosStatesEncodingMax; posState++) + for (UInt32 posState = 0; posState < Base.K_NUM_POS_STATES_ENCODING_MAX; posState++) { - _lowCoder[posState] = new BitTreeEncoder(Base.kNumLowLenBits); - _midCoder[posState] = new BitTreeEncoder(Base.kNumMidLenBits); + _lowCoder[posState] = new BitTreeEncoder(Base.K_NUM_LOW_LEN_BITS); + _midCoder[posState] = new BitTreeEncoder(Base.K_NUM_MID_LEN_BITS); } } @@ -223,16 +223,16 @@ namespace SharpCompress.Compressors.LZMA public void Encode(RangeCoder.Encoder rangeEncoder, UInt32 symbol, UInt32 posState) { - if (symbol < Base.kNumLowLenSymbols) + if (symbol < Base.K_NUM_LOW_LEN_SYMBOLS) { _choice.Encode(rangeEncoder, 0); _lowCoder[posState].Encode(rangeEncoder, symbol); } else { - symbol -= Base.kNumLowLenSymbols; + symbol -= Base.K_NUM_LOW_LEN_SYMBOLS; _choice.Encode(rangeEncoder, 1); - if (symbol < Base.kNumMidLenSymbols) + if (symbol < Base.K_NUM_MID_LEN_SYMBOLS) { _choice2.Encode(rangeEncoder, 0); _midCoder[posState].Encode(rangeEncoder, symbol); @@ -240,7 +240,7 @@ namespace SharpCompress.Compressors.LZMA else { _choice2.Encode(rangeEncoder, 1); - _highCoder.Encode(rangeEncoder, symbol - Base.kNumMidLenSymbols); + _highCoder.Encode(rangeEncoder, symbol - Base.K_NUM_MID_LEN_SYMBOLS); } } } @@ -252,7 +252,7 @@ namespace SharpCompress.Compressors.LZMA UInt32 b0 = a1 + _choice2.GetPrice0(); UInt32 b1 = a1 + _choice2.GetPrice1(); UInt32 i = 0; - for (i = 0; i < Base.kNumLowLenSymbols; i++) + for (i = 0; i < Base.K_NUM_LOW_LEN_SYMBOLS; i++) { if (i >= numSymbols) { @@ -260,28 +260,28 @@ namespace SharpCompress.Compressors.LZMA } prices[st + i] = a0 + _lowCoder[posState].GetPrice(i); } - for (; i < Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; i++) + for (; i < Base.K_NUM_LOW_LEN_SYMBOLS + Base.K_NUM_MID_LEN_SYMBOLS; i++) { if (i >= numSymbols) { return; } - prices[st + i] = b0 + _midCoder[posState].GetPrice(i - Base.kNumLowLenSymbols); + prices[st + i] = b0 + _midCoder[posState].GetPrice(i - Base.K_NUM_LOW_LEN_SYMBOLS); } for (; i < numSymbols; i++) { - prices[st + i] = b1 + _highCoder.GetPrice(i - Base.kNumLowLenSymbols - Base.kNumMidLenSymbols); + prices[st + i] = b1 + _highCoder.GetPrice(i - Base.K_NUM_LOW_LEN_SYMBOLS - Base.K_NUM_MID_LEN_SYMBOLS); } } } - private const UInt32 kNumLenSpecSymbols = Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; + private const UInt32 K_NUM_LEN_SPEC_SYMBOLS = Base.K_NUM_LOW_LEN_SYMBOLS + Base.K_NUM_MID_LEN_SYMBOLS; private class LenPriceTableEncoder : LenEncoder { - private readonly UInt32[] _prices = new UInt32[Base.kNumLenSymbols << Base.kNumPosStatesBitsEncodingMax]; + private readonly UInt32[] _prices = new UInt32[Base.K_NUM_LEN_SYMBOLS << Base.K_NUM_POS_STATES_BITS_ENCODING_MAX]; private UInt32 _tableSize; - private readonly UInt32[] _counters = new UInt32[Base.kNumPosStatesEncodingMax]; + private readonly UInt32[] _counters = new UInt32[Base.K_NUM_POS_STATES_ENCODING_MAX]; public void SetTableSize(UInt32 tableSize) { @@ -290,12 +290,12 @@ namespace SharpCompress.Compressors.LZMA public UInt32 GetPrice(UInt32 symbol, UInt32 posState) { - return _prices[posState * Base.kNumLenSymbols + symbol]; + return _prices[posState * Base.K_NUM_LEN_SYMBOLS + symbol]; } private void UpdateTable(UInt32 posState) { - SetPrices(posState, _tableSize, _prices, posState * Base.kNumLenSymbols); + SetPrices(posState, _tableSize, _prices, posState * Base.K_NUM_LEN_SYMBOLS); _counters[posState] = _tableSize; } @@ -317,76 +317,76 @@ namespace SharpCompress.Compressors.LZMA } } - private const UInt32 kNumOpts = 1 << 12; + private const UInt32 K_NUM_OPTS = 1 << 12; private class Optimal { - public Base.State State; + public Base.State _state; - public bool Prev1IsChar; - public bool Prev2; + public bool _prev1IsChar; + public bool _prev2; - public UInt32 PosPrev2; - public UInt32 BackPrev2; + public UInt32 _posPrev2; + public UInt32 _backPrev2; - public UInt32 Price; - public UInt32 PosPrev; - public UInt32 BackPrev; + public UInt32 _price; + public UInt32 _posPrev; + public UInt32 _backPrev; - public UInt32 Backs0; - public UInt32 Backs1; - public UInt32 Backs2; - public UInt32 Backs3; + public UInt32 _backs0; + public UInt32 _backs1; + public UInt32 _backs2; + public UInt32 _backs3; public void MakeAsChar() { - BackPrev = 0xFFFFFFFF; - Prev1IsChar = false; + _backPrev = 0xFFFFFFFF; + _prev1IsChar = false; } public void MakeAsShortRep() { - BackPrev = 0; + _backPrev = 0; ; - Prev1IsChar = false; + _prev1IsChar = false; } public bool IsShortRep() { - return (BackPrev == 0); + return (_backPrev == 0); } } - private readonly Optimal[] _optimum = new Optimal[kNumOpts]; + private readonly Optimal[] _optimum = new Optimal[K_NUM_OPTS]; private BinTree _matchFinder; private readonly RangeCoder.Encoder _rangeEncoder = new RangeCoder.Encoder(); private readonly BitEncoder[] _isMatch = - new BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + new BitEncoder[Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX]; - private readonly BitEncoder[] _isRep = new BitEncoder[Base.kNumStates]; - private readonly BitEncoder[] _isRepG0 = new BitEncoder[Base.kNumStates]; - private readonly BitEncoder[] _isRepG1 = new BitEncoder[Base.kNumStates]; - private readonly BitEncoder[] _isRepG2 = new BitEncoder[Base.kNumStates]; + private readonly BitEncoder[] _isRep = new BitEncoder[Base.K_NUM_STATES]; + private readonly BitEncoder[] _isRepG0 = new BitEncoder[Base.K_NUM_STATES]; + private readonly BitEncoder[] _isRepG1 = new BitEncoder[Base.K_NUM_STATES]; + private readonly BitEncoder[] _isRepG2 = new BitEncoder[Base.K_NUM_STATES]; private readonly BitEncoder[] _isRep0Long = - new BitEncoder[Base.kNumStates << Base.kNumPosStatesBitsMax]; + new BitEncoder[Base.K_NUM_STATES << Base.K_NUM_POS_STATES_BITS_MAX]; - private readonly BitTreeEncoder[] _posSlotEncoder = new BitTreeEncoder[Base.kNumLenToPosStates]; + private readonly BitTreeEncoder[] _posSlotEncoder = new BitTreeEncoder[Base.K_NUM_LEN_TO_POS_STATES]; private readonly BitEncoder[] _posEncoders = - new BitEncoder[Base.kNumFullDistances - Base.kEndPosModelIndex]; + new BitEncoder[Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX]; - private BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(Base.kNumAlignBits); + private BitTreeEncoder _posAlignEncoder = new BitTreeEncoder(Base.K_NUM_ALIGN_BITS); private readonly LenPriceTableEncoder _lenEncoder = new LenPriceTableEncoder(); private readonly LenPriceTableEncoder _repMatchLenEncoder = new LenPriceTableEncoder(); private readonly LiteralEncoder _literalEncoder = new LiteralEncoder(); - private readonly UInt32[] _matchDistances = new UInt32[Base.kMatchMaxLen * 2 + 2]; + private readonly UInt32[] _matchDistances = new UInt32[Base.K_MATCH_MAX_LEN * 2 + 2]; - private UInt32 _numFastBytes = kNumFastBytesDefault; + private UInt32 _numFastBytes = K_NUM_FAST_BYTES_DEFAULT; private UInt32 _longestMatchLength; private UInt32 _numDistancePairs; @@ -397,30 +397,30 @@ namespace SharpCompress.Compressors.LZMA private bool _longestMatchWasFound; - private readonly UInt32[] _posSlotPrices = new UInt32[1 << (Base.kNumPosSlotBits + Base.kNumLenToPosStatesBits)]; - private readonly UInt32[] _distancesPrices = new UInt32[Base.kNumFullDistances << Base.kNumLenToPosStatesBits]; - private readonly UInt32[] _alignPrices = new UInt32[Base.kAlignTableSize]; + private readonly UInt32[] _posSlotPrices = new UInt32[1 << (Base.K_NUM_POS_SLOT_BITS + Base.K_NUM_LEN_TO_POS_STATES_BITS)]; + private readonly UInt32[] _distancesPrices = new UInt32[Base.K_NUM_FULL_DISTANCES << Base.K_NUM_LEN_TO_POS_STATES_BITS]; + private readonly UInt32[] _alignPrices = new UInt32[Base.K_ALIGN_TABLE_SIZE]; private UInt32 _alignPriceCount; - private UInt32 _distTableSize = (kDefaultDictionaryLogSize * 2); + private UInt32 _distTableSize = (K_DEFAULT_DICTIONARY_LOG_SIZE * 2); private int _posStateBits = 2; private UInt32 _posStateMask = (4 - 1); private int _numLiteralPosStateBits; private int _numLiteralContextBits = 3; - private UInt32 _dictionarySize = (1 << kDefaultDictionaryLogSize); + private UInt32 _dictionarySize = (1 << K_DEFAULT_DICTIONARY_LOG_SIZE); private UInt32 _dictionarySizePrev = 0xFFFFFFFF; private UInt32 _numFastBytesPrev = 0xFFFFFFFF; - private Int64 nowPos64; + private Int64 _nowPos64; private bool _finished; private Stream _inStream; - private EMatchFinderType _matchFinderType = EMatchFinderType.BT4; + private EMatchFinderType _matchFinderType = EMatchFinderType.Bt4; private bool _writeEndMark; - private bool _needReleaseMFStream; + private bool _needReleaseMfStream; private bool _processingMode; private void Create() @@ -429,7 +429,7 @@ namespace SharpCompress.Compressors.LZMA { BinTree bt = new BinTree(); int numHashBytes = 4; - if (_matchFinderType == EMatchFinderType.BT2) + if (_matchFinderType == EMatchFinderType.Bt2) { numHashBytes = 2; } @@ -442,20 +442,20 @@ namespace SharpCompress.Compressors.LZMA { return; } - _matchFinder.Create(_dictionarySize, kNumOpts, _numFastBytes, Base.kMatchMaxLen + 1 + kNumOpts); + _matchFinder.Create(_dictionarySize, K_NUM_OPTS, _numFastBytes, Base.K_MATCH_MAX_LEN + 1 + K_NUM_OPTS); _dictionarySizePrev = _dictionarySize; _numFastBytesPrev = _numFastBytes; } public Encoder() { - for (int i = 0; i < kNumOpts; i++) + for (int i = 0; i < K_NUM_OPTS; i++) { _optimum[i] = new Optimal(); } - for (int i = 0; i < Base.kNumLenToPosStates; i++) + for (int i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) { - _posSlotEncoder[i] = new BitTreeEncoder(Base.kNumPosSlotBits); + _posSlotEncoder[i] = new BitTreeEncoder(Base.K_NUM_POS_SLOT_BITS); } } @@ -470,11 +470,11 @@ namespace SharpCompress.Compressors.LZMA _rangeEncoder.Init(); uint i; - for (i = 0; i < Base.kNumStates; i++) + for (i = 0; i < Base.K_NUM_STATES; i++) { for (uint j = 0; j <= _posStateMask; j++) { - uint complexState = (i << Base.kNumPosStatesBitsMax) + j; + uint complexState = (i << Base.K_NUM_POS_STATES_BITS_MAX) + j; _isMatch[complexState].Init(); _isRep0Long[complexState].Init(); } @@ -484,11 +484,11 @@ namespace SharpCompress.Compressors.LZMA _isRepG2[i].Init(); } _literalEncoder.Init(); - for (i = 0; i < Base.kNumLenToPosStates; i++) + for (i = 0; i < Base.K_NUM_LEN_TO_POS_STATES; i++) { _posSlotEncoder[i].Init(); } - for (i = 0; i < Base.kNumFullDistances - Base.kEndPosModelIndex; i++) + for (i = 0; i < Base.K_NUM_FULL_DISTANCES - Base.K_END_POS_MODEL_INDEX; i++) { _posEncoders[i].Init(); } @@ -514,7 +514,7 @@ namespace SharpCompress.Compressors.LZMA if (lenRes == _numFastBytes) { lenRes += _matchFinder.GetMatchLen((int)lenRes - 1, _matchDistances[numDistancePairs - 1], - Base.kMatchMaxLen - lenRes); + Base.K_MATCH_MAX_LEN - lenRes); } } _additionalOffset++; @@ -531,8 +531,8 @@ namespace SharpCompress.Compressors.LZMA private UInt32 GetRepLen1Price(Base.State state, UInt32 posState) { - return _isRepG0[state.Index].GetPrice0() + - _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0(); + return _isRepG0[state._index].GetPrice0() + + _isRep0Long[(state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].GetPrice0(); } private UInt32 GetPureRepPrice(UInt32 repIndex, Base.State state, UInt32 posState) @@ -540,20 +540,20 @@ namespace SharpCompress.Compressors.LZMA UInt32 price; if (repIndex == 0) { - price = _isRepG0[state.Index].GetPrice0(); - price += _isRep0Long[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); + price = _isRepG0[state._index].GetPrice0(); + price += _isRep0Long[(state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].GetPrice1(); } else { - price = _isRepG0[state.Index].GetPrice1(); + price = _isRepG0[state._index].GetPrice1(); if (repIndex == 1) { - price += _isRepG1[state.Index].GetPrice0(); + price += _isRepG1[state._index].GetPrice0(); } else { - price += _isRepG1[state.Index].GetPrice1(); - price += _isRepG2[state.Index].GetPrice(repIndex - 2); + price += _isRepG1[state._index].GetPrice1(); + price += _isRepG2[state._index].GetPrice(repIndex - 2); } } return price; @@ -561,7 +561,7 @@ namespace SharpCompress.Compressors.LZMA private UInt32 GetRepPrice(UInt32 repIndex, UInt32 len, Base.State state, UInt32 posState) { - UInt32 price = _repMatchLenEncoder.GetPrice(len - Base.kMatchMinLen, posState); + UInt32 price = _repMatchLenEncoder.GetPrice(len - Base.K_MATCH_MIN_LEN, posState); return price + GetPureRepPrice(repIndex, state, posState); } @@ -569,62 +569,62 @@ namespace SharpCompress.Compressors.LZMA { UInt32 price; UInt32 lenToPosState = Base.GetLenToPosState(len); - if (pos < Base.kNumFullDistances) + if (pos < Base.K_NUM_FULL_DISTANCES) { - price = _distancesPrices[(lenToPosState * Base.kNumFullDistances) + pos]; + price = _distancesPrices[(lenToPosState * Base.K_NUM_FULL_DISTANCES) + pos]; } else { - price = _posSlotPrices[(lenToPosState << Base.kNumPosSlotBits) + GetPosSlot2(pos)] + - _alignPrices[pos & Base.kAlignMask]; + price = _posSlotPrices[(lenToPosState << Base.K_NUM_POS_SLOT_BITS) + GetPosSlot2(pos)] + + _alignPrices[pos & Base.K_ALIGN_MASK]; } - return price + _lenEncoder.GetPrice(len - Base.kMatchMinLen, posState); + return price + _lenEncoder.GetPrice(len - Base.K_MATCH_MIN_LEN, posState); } private UInt32 Backward(out UInt32 backRes, UInt32 cur) { _optimumEndIndex = cur; - UInt32 posMem = _optimum[cur].PosPrev; - UInt32 backMem = _optimum[cur].BackPrev; + UInt32 posMem = _optimum[cur]._posPrev; + UInt32 backMem = _optimum[cur]._backPrev; do { - if (_optimum[cur].Prev1IsChar) + if (_optimum[cur]._prev1IsChar) { _optimum[posMem].MakeAsChar(); - _optimum[posMem].PosPrev = posMem - 1; - if (_optimum[cur].Prev2) + _optimum[posMem]._posPrev = posMem - 1; + if (_optimum[cur]._prev2) { - _optimum[posMem - 1].Prev1IsChar = false; - _optimum[posMem - 1].PosPrev = _optimum[cur].PosPrev2; - _optimum[posMem - 1].BackPrev = _optimum[cur].BackPrev2; + _optimum[posMem - 1]._prev1IsChar = false; + _optimum[posMem - 1]._posPrev = _optimum[cur]._posPrev2; + _optimum[posMem - 1]._backPrev = _optimum[cur]._backPrev2; } } UInt32 posPrev = posMem; UInt32 backCur = backMem; - backMem = _optimum[posPrev].BackPrev; - posMem = _optimum[posPrev].PosPrev; + backMem = _optimum[posPrev]._backPrev; + posMem = _optimum[posPrev]._posPrev; - _optimum[posPrev].BackPrev = backCur; - _optimum[posPrev].PosPrev = cur; + _optimum[posPrev]._backPrev = backCur; + _optimum[posPrev]._posPrev = cur; cur = posPrev; } while (cur > 0); - backRes = _optimum[0].BackPrev; - _optimumCurrentIndex = _optimum[0].PosPrev; + backRes = _optimum[0]._backPrev; + _optimumCurrentIndex = _optimum[0]._posPrev; return _optimumCurrentIndex; } - private readonly UInt32[] reps = new UInt32[Base.kNumRepDistances]; - private readonly UInt32[] repLens = new UInt32[Base.kNumRepDistances]; + private readonly UInt32[] _reps = new UInt32[Base.K_NUM_REP_DISTANCES]; + private readonly UInt32[] _repLens = new UInt32[Base.K_NUM_REP_DISTANCES]; private UInt32 GetOptimum(UInt32 position, out UInt32 backRes) { if (_optimumEndIndex != _optimumCurrentIndex) { - UInt32 lenRes = _optimum[_optimumCurrentIndex].PosPrev - _optimumCurrentIndex; - backRes = _optimum[_optimumCurrentIndex].BackPrev; - _optimumCurrentIndex = _optimum[_optimumCurrentIndex].PosPrev; + UInt32 lenRes = _optimum[_optimumCurrentIndex]._posPrev - _optimumCurrentIndex; + backRes = _optimum[_optimumCurrentIndex]._backPrev; + _optimumCurrentIndex = _optimum[_optimumCurrentIndex]._posPrev; return lenRes; } _optimumCurrentIndex = _optimumEndIndex = 0; @@ -647,33 +647,33 @@ namespace SharpCompress.Compressors.LZMA backRes = 0xFFFFFFFF; return 1; } - if (numAvailableBytes > Base.kMatchMaxLen) + if (numAvailableBytes > Base.K_MATCH_MAX_LEN) { - numAvailableBytes = Base.kMatchMaxLen; + numAvailableBytes = Base.K_MATCH_MAX_LEN; } UInt32 repMaxIndex = 0; UInt32 i; - for (i = 0; i < Base.kNumRepDistances; i++) + for (i = 0; i < Base.K_NUM_REP_DISTANCES; i++) { - reps[i] = _repDistances[i]; - repLens[i] = _matchFinder.GetMatchLen(0 - 1, reps[i], Base.kMatchMaxLen); - if (repLens[i] > repLens[repMaxIndex]) + _reps[i] = _repDistances[i]; + _repLens[i] = _matchFinder.GetMatchLen(0 - 1, _reps[i], Base.K_MATCH_MAX_LEN); + if (_repLens[i] > _repLens[repMaxIndex]) { repMaxIndex = i; } } - if (repLens[repMaxIndex] >= _numFastBytes) + if (_repLens[repMaxIndex] >= _numFastBytes) { backRes = repMaxIndex; - UInt32 lenRes = repLens[repMaxIndex]; + UInt32 lenRes = _repLens[repMaxIndex]; MovePos(lenRes - 1); return lenRes; } if (lenMain >= _numFastBytes) { - backRes = _matchDistances[numDistancePairs - 1] + Base.kNumRepDistances; + backRes = _matchDistances[numDistancePairs - 1] + Base.K_NUM_REP_DISTANCES; MovePos(lenMain - 1); return lenMain; } @@ -681,59 +681,59 @@ namespace SharpCompress.Compressors.LZMA Byte currentByte = _matchFinder.GetIndexByte(0 - 1); Byte matchByte = _matchFinder.GetIndexByte((Int32)(0 - _repDistances[0] - 1 - 1)); - if (lenMain < 2 && currentByte != matchByte && repLens[repMaxIndex] < 2) + if (lenMain < 2 && currentByte != matchByte && _repLens[repMaxIndex] < 2) { backRes = 0xFFFFFFFF; return 1; } - _optimum[0].State = _state; + _optimum[0]._state = _state; UInt32 posState = (position & _posStateMask); - _optimum[1].Price = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() + + _optimum[1]._price = _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].GetPrice0() + _literalEncoder.GetSubCoder(position, _previousByte) .GetPrice(!_state.IsCharState(), matchByte, currentByte); _optimum[1].MakeAsChar(); - UInt32 matchPrice = _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); - UInt32 repMatchPrice = matchPrice + _isRep[_state.Index].GetPrice1(); + UInt32 matchPrice = _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].GetPrice1(); + UInt32 repMatchPrice = matchPrice + _isRep[_state._index].GetPrice1(); if (matchByte == currentByte) { UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(_state, posState); - if (shortRepPrice < _optimum[1].Price) + if (shortRepPrice < _optimum[1]._price) { - _optimum[1].Price = shortRepPrice; + _optimum[1]._price = shortRepPrice; _optimum[1].MakeAsShortRep(); } } - UInt32 lenEnd = ((lenMain >= repLens[repMaxIndex]) ? lenMain : repLens[repMaxIndex]); + UInt32 lenEnd = ((lenMain >= _repLens[repMaxIndex]) ? lenMain : _repLens[repMaxIndex]); if (lenEnd < 2) { - backRes = _optimum[1].BackPrev; + backRes = _optimum[1]._backPrev; return 1; } - _optimum[1].PosPrev = 0; + _optimum[1]._posPrev = 0; - _optimum[0].Backs0 = reps[0]; - _optimum[0].Backs1 = reps[1]; - _optimum[0].Backs2 = reps[2]; - _optimum[0].Backs3 = reps[3]; + _optimum[0]._backs0 = _reps[0]; + _optimum[0]._backs1 = _reps[1]; + _optimum[0]._backs2 = _reps[2]; + _optimum[0]._backs3 = _reps[3]; UInt32 len = lenEnd; do { - _optimum[len--].Price = kIfinityPrice; + _optimum[len--]._price = K_IFINITY_PRICE; } while (len >= 2); - for (i = 0; i < Base.kNumRepDistances; i++) + for (i = 0; i < Base.K_NUM_REP_DISTANCES; i++) { - UInt32 repLen = repLens[i]; + UInt32 repLen = _repLens[i]; if (repLen < 2) { continue; @@ -743,20 +743,20 @@ namespace SharpCompress.Compressors.LZMA { UInt32 curAndLenPrice = price + _repMatchLenEncoder.GetPrice(repLen - 2, posState); Optimal optimum = _optimum[repLen]; - if (curAndLenPrice < optimum.Price) + if (curAndLenPrice < optimum._price) { - optimum.Price = curAndLenPrice; - optimum.PosPrev = 0; - optimum.BackPrev = i; - optimum.Prev1IsChar = false; + optimum._price = curAndLenPrice; + optimum._posPrev = 0; + optimum._backPrev = i; + optimum._prev1IsChar = false; } } while (--repLen >= 2); } - UInt32 normalMatchPrice = matchPrice + _isRep[_state.Index].GetPrice0(); + UInt32 normalMatchPrice = matchPrice + _isRep[_state._index].GetPrice0(); - len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + len = ((_repLens[0] >= 2) ? _repLens[0] + 1 : 2); if (len <= lenMain) { UInt32 offs = 0; @@ -769,12 +769,12 @@ namespace SharpCompress.Compressors.LZMA UInt32 distance = _matchDistances[offs + 1]; UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(distance, len, posState); Optimal optimum = _optimum[len]; - if (curAndLenPrice < optimum.Price) + if (curAndLenPrice < optimum._price) { - optimum.Price = curAndLenPrice; - optimum.PosPrev = 0; - optimum.BackPrev = distance + Base.kNumRepDistances; - optimum.Prev1IsChar = false; + optimum._price = curAndLenPrice; + optimum._posPrev = 0; + optimum._backPrev = distance + Base.K_NUM_REP_DISTANCES; + optimum._prev1IsChar = false; } if (len == _matchDistances[offs]) { @@ -806,15 +806,15 @@ namespace SharpCompress.Compressors.LZMA return Backward(out backRes, cur); } position++; - UInt32 posPrev = _optimum[cur].PosPrev; + UInt32 posPrev = _optimum[cur]._posPrev; Base.State state; - if (_optimum[cur].Prev1IsChar) + if (_optimum[cur]._prev1IsChar) { posPrev--; - if (_optimum[cur].Prev2) + if (_optimum[cur]._prev2) { - state = _optimum[_optimum[cur].PosPrev2].State; - if (_optimum[cur].BackPrev2 < Base.kNumRepDistances) + state = _optimum[_optimum[cur]._posPrev2]._state; + if (_optimum[cur]._backPrev2 < Base.K_NUM_REP_DISTANCES) { state.UpdateRep(); } @@ -825,13 +825,13 @@ namespace SharpCompress.Compressors.LZMA } else { - state = _optimum[posPrev].State; + state = _optimum[posPrev]._state; } state.UpdateChar(); } else { - state = _optimum[posPrev].State; + state = _optimum[posPrev]._state; } if (posPrev == cur - 1) { @@ -847,16 +847,16 @@ namespace SharpCompress.Compressors.LZMA else { UInt32 pos; - if (_optimum[cur].Prev1IsChar && _optimum[cur].Prev2) + if (_optimum[cur]._prev1IsChar && _optimum[cur]._prev2) { - posPrev = _optimum[cur].PosPrev2; - pos = _optimum[cur].BackPrev2; + posPrev = _optimum[cur]._posPrev2; + pos = _optimum[cur]._backPrev2; state.UpdateRep(); } else { - pos = _optimum[cur].BackPrev; - if (pos < Base.kNumRepDistances) + pos = _optimum[cur]._backPrev; + if (pos < Base.K_NUM_REP_DISTANCES) { state.UpdateRep(); } @@ -866,91 +866,91 @@ namespace SharpCompress.Compressors.LZMA } } Optimal opt = _optimum[posPrev]; - if (pos < Base.kNumRepDistances) + if (pos < Base.K_NUM_REP_DISTANCES) { if (pos == 0) { - reps[0] = opt.Backs0; - reps[1] = opt.Backs1; - reps[2] = opt.Backs2; - reps[3] = opt.Backs3; + _reps[0] = opt._backs0; + _reps[1] = opt._backs1; + _reps[2] = opt._backs2; + _reps[3] = opt._backs3; } else if (pos == 1) { - reps[0] = opt.Backs1; - reps[1] = opt.Backs0; - reps[2] = opt.Backs2; - reps[3] = opt.Backs3; + _reps[0] = opt._backs1; + _reps[1] = opt._backs0; + _reps[2] = opt._backs2; + _reps[3] = opt._backs3; } else if (pos == 2) { - reps[0] = opt.Backs2; - reps[1] = opt.Backs0; - reps[2] = opt.Backs1; - reps[3] = opt.Backs3; + _reps[0] = opt._backs2; + _reps[1] = opt._backs0; + _reps[2] = opt._backs1; + _reps[3] = opt._backs3; } else { - reps[0] = opt.Backs3; - reps[1] = opt.Backs0; - reps[2] = opt.Backs1; - reps[3] = opt.Backs2; + _reps[0] = opt._backs3; + _reps[1] = opt._backs0; + _reps[2] = opt._backs1; + _reps[3] = opt._backs2; } } else { - reps[0] = (pos - Base.kNumRepDistances); - reps[1] = opt.Backs0; - reps[2] = opt.Backs1; - reps[3] = opt.Backs2; + _reps[0] = (pos - Base.K_NUM_REP_DISTANCES); + _reps[1] = opt._backs0; + _reps[2] = opt._backs1; + _reps[3] = opt._backs2; } } - _optimum[cur].State = state; - _optimum[cur].Backs0 = reps[0]; - _optimum[cur].Backs1 = reps[1]; - _optimum[cur].Backs2 = reps[2]; - _optimum[cur].Backs3 = reps[3]; - UInt32 curPrice = _optimum[cur].Price; + _optimum[cur]._state = state; + _optimum[cur]._backs0 = _reps[0]; + _optimum[cur]._backs1 = _reps[1]; + _optimum[cur]._backs2 = _reps[2]; + _optimum[cur]._backs3 = _reps[3]; + UInt32 curPrice = _optimum[cur]._price; currentByte = _matchFinder.GetIndexByte(0 - 1); - matchByte = _matchFinder.GetIndexByte((Int32)(0 - reps[0] - 1 - 1)); + matchByte = _matchFinder.GetIndexByte((Int32)(0 - _reps[0] - 1 - 1)); posState = (position & _posStateMask); UInt32 curAnd1Price = curPrice + - _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice0() + + _isMatch[(state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].GetPrice0() + _literalEncoder.GetSubCoder(position, _matchFinder.GetIndexByte(0 - 2)). GetPrice(!state.IsCharState(), matchByte, currentByte); Optimal nextOptimum = _optimum[cur + 1]; bool nextIsChar = false; - if (curAnd1Price < nextOptimum.Price) + if (curAnd1Price < nextOptimum._price) { - nextOptimum.Price = curAnd1Price; - nextOptimum.PosPrev = cur; + nextOptimum._price = curAnd1Price; + nextOptimum._posPrev = cur; nextOptimum.MakeAsChar(); nextIsChar = true; } - matchPrice = curPrice + _isMatch[(state.Index << Base.kNumPosStatesBitsMax) + posState].GetPrice1(); - repMatchPrice = matchPrice + _isRep[state.Index].GetPrice1(); + matchPrice = curPrice + _isMatch[(state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].GetPrice1(); + repMatchPrice = matchPrice + _isRep[state._index].GetPrice1(); if (matchByte == currentByte && - !(nextOptimum.PosPrev < cur && nextOptimum.BackPrev == 0)) + !(nextOptimum._posPrev < cur && nextOptimum._backPrev == 0)) { UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(state, posState); - if (shortRepPrice <= nextOptimum.Price) + if (shortRepPrice <= nextOptimum._price) { - nextOptimum.Price = shortRepPrice; - nextOptimum.PosPrev = cur; + nextOptimum._price = shortRepPrice; + nextOptimum._posPrev = cur; nextOptimum.MakeAsShortRep(); nextIsChar = true; } } UInt32 numAvailableBytesFull = _matchFinder.GetNumAvailableBytes() + 1; - numAvailableBytesFull = Math.Min(kNumOpts - 1 - cur, numAvailableBytesFull); + numAvailableBytesFull = Math.Min(K_NUM_OPTS - 1 - cur, numAvailableBytesFull); numAvailableBytes = numAvailableBytesFull; if (numAvailableBytes < 2) @@ -965,32 +965,32 @@ namespace SharpCompress.Compressors.LZMA { // try Literal + rep0 UInt32 t = Math.Min(numAvailableBytesFull - 1, _numFastBytes); - UInt32 lenTest2 = _matchFinder.GetMatchLen(0, reps[0], t); + UInt32 lenTest2 = _matchFinder.GetMatchLen(0, _reps[0], t); if (lenTest2 >= 2) { Base.State state2 = state; state2.UpdateChar(); UInt32 posStateNext = (position + 1) & _posStateMask; UInt32 nextRepMatchPrice = curAnd1Price + - _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext] + _isMatch[(state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext] .GetPrice1() + - _isRep[state2.Index].GetPrice1(); + _isRep[state2._index].GetPrice1(); { UInt32 offset = cur + 1 + lenTest2; while (lenEnd < offset) { - _optimum[++lenEnd].Price = kIfinityPrice; + _optimum[++lenEnd]._price = K_IFINITY_PRICE; } UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice( 0, lenTest2, state2, posStateNext); Optimal optimum = _optimum[offset]; - if (curAndLenPrice < optimum.Price) + if (curAndLenPrice < optimum._price) { - optimum.Price = curAndLenPrice; - optimum.PosPrev = cur + 1; - optimum.BackPrev = 0; - optimum.Prev1IsChar = true; - optimum.Prev2 = false; + optimum._price = curAndLenPrice; + optimum._posPrev = cur + 1; + optimum._backPrev = 0; + optimum._prev1IsChar = true; + optimum._prev2 = false; } } } @@ -998,9 +998,9 @@ namespace SharpCompress.Compressors.LZMA UInt32 startLen = 2; // speed optimization - for (UInt32 repIndex = 0; repIndex < Base.kNumRepDistances; repIndex++) + for (UInt32 repIndex = 0; repIndex < Base.K_NUM_REP_DISTANCES; repIndex++) { - UInt32 lenTest = _matchFinder.GetMatchLen(0 - 1, reps[repIndex], numAvailableBytes); + UInt32 lenTest = _matchFinder.GetMatchLen(0 - 1, _reps[repIndex], numAvailableBytes); if (lenTest < 2) { continue; @@ -1010,16 +1010,16 @@ namespace SharpCompress.Compressors.LZMA { while (lenEnd < cur + lenTest) { - _optimum[++lenEnd].Price = kIfinityPrice; + _optimum[++lenEnd]._price = K_IFINITY_PRICE; } UInt32 curAndLenPrice = repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState); Optimal optimum = _optimum[cur + lenTest]; - if (curAndLenPrice < optimum.Price) + if (curAndLenPrice < optimum._price) { - optimum.Price = curAndLenPrice; - optimum.PosPrev = cur; - optimum.BackPrev = repIndex; - optimum.Prev1IsChar = false; + optimum._price = curAndLenPrice; + optimum._posPrev = cur; + optimum._backPrev = repIndex; + optimum._prev1IsChar = false; } } while (--lenTest >= 2); @@ -1034,7 +1034,7 @@ namespace SharpCompress.Compressors.LZMA if (lenTest < numAvailableBytesFull) { UInt32 t = Math.Min(numAvailableBytesFull - 1 - lenTest, _numFastBytes); - UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, reps[repIndex], t); + UInt32 lenTest2 = _matchFinder.GetMatchLen((Int32)lenTest, _reps[repIndex], t); if (lenTest2 >= 2) { Base.State state2 = state; @@ -1042,39 +1042,39 @@ namespace SharpCompress.Compressors.LZMA UInt32 posStateNext = (position + lenTest) & _posStateMask; UInt32 curAndLenCharPrice = repMatchPrice + GetRepPrice(repIndex, lenTest, state, posState) + - _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext].GetPrice0() + + _isMatch[(state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext].GetPrice0() + _literalEncoder.GetSubCoder(position + lenTest, _matchFinder.GetIndexByte((Int32)lenTest - 1 - 1)) .GetPrice(true, _matchFinder.GetIndexByte( - (Int32)lenTest - 1 - (Int32)(reps[repIndex] + 1)), + (Int32)lenTest - 1 - (Int32)(_reps[repIndex] + 1)), _matchFinder.GetIndexByte((Int32)lenTest - 1)); state2.UpdateChar(); posStateNext = (position + lenTest + 1) & _posStateMask; UInt32 nextMatchPrice = curAndLenCharPrice + - _isMatch[(state2.Index << Base.kNumPosStatesBitsMax) + posStateNext] + _isMatch[(state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext] .GetPrice1(); - UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1(); + UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2._index].GetPrice1(); // for(; lenTest2 >= 2; lenTest2--) { UInt32 offset = lenTest + 1 + lenTest2; while (lenEnd < cur + offset) { - _optimum[++lenEnd].Price = kIfinityPrice; + _optimum[++lenEnd]._price = K_IFINITY_PRICE; } UInt32 curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); Optimal optimum = _optimum[cur + offset]; - if (curAndLenPrice < optimum.Price) + if (curAndLenPrice < optimum._price) { - optimum.Price = curAndLenPrice; - optimum.PosPrev = cur + lenTest + 1; - optimum.BackPrev = 0; - optimum.Prev1IsChar = true; - optimum.Prev2 = true; - optimum.PosPrev2 = cur; - optimum.BackPrev2 = repIndex; + optimum._price = curAndLenPrice; + optimum._posPrev = cur + lenTest + 1; + optimum._backPrev = 0; + optimum._prev1IsChar = true; + optimum._prev2 = true; + optimum._posPrev2 = cur; + optimum._backPrev2 = repIndex; } } } @@ -1093,10 +1093,10 @@ namespace SharpCompress.Compressors.LZMA } if (newLen >= startLen) { - normalMatchPrice = matchPrice + _isRep[state.Index].GetPrice0(); + normalMatchPrice = matchPrice + _isRep[state._index].GetPrice0(); while (lenEnd < cur + newLen) { - _optimum[++lenEnd].Price = kIfinityPrice; + _optimum[++lenEnd]._price = K_IFINITY_PRICE; } UInt32 offs = 0; @@ -1110,12 +1110,12 @@ namespace SharpCompress.Compressors.LZMA UInt32 curBack = _matchDistances[offs + 1]; UInt32 curAndLenPrice = normalMatchPrice + GetPosLenPrice(curBack, lenTest, posState); Optimal optimum = _optimum[cur + lenTest]; - if (curAndLenPrice < optimum.Price) + if (curAndLenPrice < optimum._price) { - optimum.Price = curAndLenPrice; - optimum.PosPrev = cur; - optimum.BackPrev = curBack + Base.kNumRepDistances; - optimum.Prev1IsChar = false; + optimum._price = curAndLenPrice; + optimum._posPrev = cur; + optimum._backPrev = curBack + Base.K_NUM_REP_DISTANCES; + optimum._prev1IsChar = false; } if (lenTest == _matchDistances[offs]) @@ -1131,7 +1131,7 @@ namespace SharpCompress.Compressors.LZMA UInt32 posStateNext = (position + lenTest) & _posStateMask; UInt32 curAndLenCharPrice = curAndLenPrice + _isMatch[ - (state2.Index << Base.kNumPosStatesBitsMax) + + (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext].GetPrice0() + _literalEncoder.GetSubCoder(position + lenTest, _matchFinder.GetIndexByte( @@ -1147,26 +1147,26 @@ namespace SharpCompress.Compressors.LZMA posStateNext = (position + lenTest + 1) & _posStateMask; UInt32 nextMatchPrice = curAndLenCharPrice + _isMatch[ - (state2.Index << Base.kNumPosStatesBitsMax) + + (state2._index << Base.K_NUM_POS_STATES_BITS_MAX) + posStateNext].GetPrice1(); - UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2.Index].GetPrice1(); + UInt32 nextRepMatchPrice = nextMatchPrice + _isRep[state2._index].GetPrice1(); UInt32 offset = lenTest + 1 + lenTest2; while (lenEnd < cur + offset) { - _optimum[++lenEnd].Price = kIfinityPrice; + _optimum[++lenEnd]._price = K_IFINITY_PRICE; } curAndLenPrice = nextRepMatchPrice + GetRepPrice(0, lenTest2, state2, posStateNext); optimum = _optimum[cur + offset]; - if (curAndLenPrice < optimum.Price) + if (curAndLenPrice < optimum._price) { - optimum.Price = curAndLenPrice; - optimum.PosPrev = cur + lenTest + 1; - optimum.BackPrev = 0; - optimum.Prev1IsChar = true; - optimum.Prev2 = true; - optimum.PosPrev2 = cur; - optimum.BackPrev2 = curBack + Base.kNumRepDistances; + optimum._price = curAndLenPrice; + optimum._posPrev = cur + lenTest + 1; + optimum._backPrev = 0; + optimum._prev1IsChar = true; + optimum._prev2 = true; + optimum._posPrev2 = cur; + optimum._backPrev2 = curBack + Base.K_NUM_REP_DISTANCES; } } } @@ -1194,23 +1194,23 @@ namespace SharpCompress.Compressors.LZMA return; } - _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 1); - _isRep[_state.Index].Encode(_rangeEncoder, 0); + _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode(_rangeEncoder, 1); + _isRep[_state._index].Encode(_rangeEncoder, 0); _state.UpdateMatch(); - UInt32 len = Base.kMatchMinLen; - _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); - UInt32 posSlot = (1 << Base.kNumPosSlotBits) - 1; + UInt32 len = Base.K_MATCH_MIN_LEN; + _lenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState); + UInt32 posSlot = (1 << Base.K_NUM_POS_SLOT_BITS) - 1; UInt32 lenToPosState = Base.GetLenToPosState(len); _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); int footerBits = 30; UInt32 posReduced = (((UInt32)1) << footerBits) - 1; - _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits); - _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask); + _rangeEncoder.EncodeDirectBits(posReduced >> Base.K_NUM_ALIGN_BITS, footerBits - Base.K_NUM_ALIGN_BITS); + _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.K_ALIGN_MASK); } private void Flush(UInt32 nowPos) { - ReleaseMFStream(); + ReleaseMfStream(); WriteEndMarker(nowPos & _posStateMask); _rangeEncoder.FlushData(); _rangeEncoder.FlushStream(); @@ -1225,7 +1225,7 @@ namespace SharpCompress.Compressors.LZMA if (_inStream != null) { _matchFinder.SetStream(_inStream); - _needReleaseMFStream = true; + _needReleaseMfStream = true; _inStream = null; } @@ -1235,8 +1235,8 @@ namespace SharpCompress.Compressors.LZMA } _finished = true; - Int64 progressPosValuePrev = nowPos64; - if (nowPos64 == 0) + Int64 progressPosValuePrev = _nowPos64; + if (_nowPos64 == 0) { if (_trainSize > 0) { @@ -1256,19 +1256,19 @@ namespace SharpCompress.Compressors.LZMA } if (_matchFinder.GetNumAvailableBytes() == 0) { - Flush((UInt32)nowPos64); + Flush((UInt32)_nowPos64); return; } UInt32 len, numDistancePairs; // it's not used ReadMatchDistances(out len, out numDistancePairs); - UInt32 posState = (UInt32)(nowPos64) & _posStateMask; - _isMatch[(_state.Index << Base.kNumPosStatesBitsMax) + posState].Encode(_rangeEncoder, 0); + UInt32 posState = (UInt32)(_nowPos64) & _posStateMask; + _isMatch[(_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState].Encode(_rangeEncoder, 0); _state.UpdateChar(); Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset)); - _literalEncoder.GetSubCoder((UInt32)(nowPos64), _previousByte).Encode(_rangeEncoder, curByte); + _literalEncoder.GetSubCoder((UInt32)(_nowPos64), _previousByte).Encode(_rangeEncoder, curByte); _previousByte = curByte; _additionalOffset--; - nowPos64++; + _nowPos64++; } if (_processingMode && _matchFinder.IsDataStarved) { @@ -1277,7 +1277,7 @@ namespace SharpCompress.Compressors.LZMA } if (_matchFinder.GetNumAvailableBytes() == 0) { - Flush((UInt32)nowPos64); + Flush((UInt32)_nowPos64); return; } while (true) @@ -1289,15 +1289,15 @@ namespace SharpCompress.Compressors.LZMA } UInt32 pos; - UInt32 len = GetOptimum((UInt32)nowPos64, out pos); + UInt32 len = GetOptimum((UInt32)_nowPos64, out pos); - UInt32 posState = ((UInt32)nowPos64) & _posStateMask; - UInt32 complexState = (_state.Index << Base.kNumPosStatesBitsMax) + posState; + UInt32 posState = ((UInt32)_nowPos64) & _posStateMask; + UInt32 complexState = (_state._index << Base.K_NUM_POS_STATES_BITS_MAX) + posState; if (len == 1 && pos == 0xFFFFFFFF) { _isMatch[complexState].Encode(_rangeEncoder, 0); Byte curByte = _matchFinder.GetIndexByte((Int32)(0 - _additionalOffset)); - LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((UInt32)nowPos64, _previousByte); + LiteralEncoder.Encoder2 subCoder = _literalEncoder.GetSubCoder((UInt32)_nowPos64, _previousByte); if (!_state.IsCharState()) { Byte matchByte = @@ -1314,12 +1314,12 @@ namespace SharpCompress.Compressors.LZMA else { _isMatch[complexState].Encode(_rangeEncoder, 1); - if (pos < Base.kNumRepDistances) + if (pos < Base.K_NUM_REP_DISTANCES) { - _isRep[_state.Index].Encode(_rangeEncoder, 1); + _isRep[_state._index].Encode(_rangeEncoder, 1); if (pos == 0) { - _isRepG0[_state.Index].Encode(_rangeEncoder, 0); + _isRepG0[_state._index].Encode(_rangeEncoder, 0); if (len == 1) { _isRep0Long[complexState].Encode(_rangeEncoder, 0); @@ -1331,15 +1331,15 @@ namespace SharpCompress.Compressors.LZMA } else { - _isRepG0[_state.Index].Encode(_rangeEncoder, 1); + _isRepG0[_state._index].Encode(_rangeEncoder, 1); if (pos == 1) { - _isRepG1[_state.Index].Encode(_rangeEncoder, 0); + _isRepG1[_state._index].Encode(_rangeEncoder, 0); } else { - _isRepG1[_state.Index].Encode(_rangeEncoder, 1); - _isRepG2[_state.Index].Encode(_rangeEncoder, pos - 2); + _isRepG1[_state._index].Encode(_rangeEncoder, 1); + _isRepG2[_state._index].Encode(_rangeEncoder, pos - 2); } } if (len == 1) @@ -1348,7 +1348,7 @@ namespace SharpCompress.Compressors.LZMA } else { - _repMatchLenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); + _repMatchLenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState); _state.UpdateRep(); } UInt32 distance = _repDistances[pos]; @@ -1363,21 +1363,21 @@ namespace SharpCompress.Compressors.LZMA } else { - _isRep[_state.Index].Encode(_rangeEncoder, 0); + _isRep[_state._index].Encode(_rangeEncoder, 0); _state.UpdateMatch(); - _lenEncoder.Encode(_rangeEncoder, len - Base.kMatchMinLen, posState); - pos -= Base.kNumRepDistances; + _lenEncoder.Encode(_rangeEncoder, len - Base.K_MATCH_MIN_LEN, posState); + pos -= Base.K_NUM_REP_DISTANCES; UInt32 posSlot = GetPosSlot(pos); UInt32 lenToPosState = Base.GetLenToPosState(len); _posSlotEncoder[lenToPosState].Encode(_rangeEncoder, posSlot); - if (posSlot >= Base.kStartPosModelIndex) + if (posSlot >= Base.K_START_POS_MODEL_INDEX) { int footerBits = (int)((posSlot >> 1) - 1); UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits); UInt32 posReduced = pos - baseVal; - if (posSlot < Base.kEndPosModelIndex) + if (posSlot < Base.K_END_POS_MODEL_INDEX) { BitTreeEncoder.ReverseEncode(_posEncoders, baseVal - posSlot - 1, _rangeEncoder, footerBits, @@ -1385,14 +1385,14 @@ namespace SharpCompress.Compressors.LZMA } else { - _rangeEncoder.EncodeDirectBits(posReduced >> Base.kNumAlignBits, - footerBits - Base.kNumAlignBits); - _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.kAlignMask); + _rangeEncoder.EncodeDirectBits(posReduced >> Base.K_NUM_ALIGN_BITS, + footerBits - Base.K_NUM_ALIGN_BITS); + _posAlignEncoder.ReverseEncode(_rangeEncoder, posReduced & Base.K_ALIGN_MASK); _alignPriceCount++; } } UInt32 distance = pos; - for (UInt32 i = Base.kNumRepDistances - 1; i >= 1; i--) + for (UInt32 i = Base.K_NUM_REP_DISTANCES - 1; i >= 1; i--) { _repDistances[i] = _repDistances[i - 1]; } @@ -1402,7 +1402,7 @@ namespace SharpCompress.Compressors.LZMA _previousByte = _matchFinder.GetIndexByte((Int32)(len - 1 - _additionalOffset)); } _additionalOffset -= len; - nowPos64 += len; + _nowPos64 += len; if (_additionalOffset == 0) { // if (!_fastMode) @@ -1410,11 +1410,11 @@ namespace SharpCompress.Compressors.LZMA { FillDistancesPrices(); } - if (_alignPriceCount >= Base.kAlignTableSize) + if (_alignPriceCount >= Base.K_ALIGN_TABLE_SIZE) { FillAlignPrices(); } - inSize = nowPos64; + inSize = _nowPos64; outSize = _rangeEncoder.GetProcessedSizeAdd(); if (_processingMode && _matchFinder.IsDataStarved) { @@ -1423,11 +1423,11 @@ namespace SharpCompress.Compressors.LZMA } if (_matchFinder.GetNumAvailableBytes() == 0) { - Flush((UInt32)nowPos64); + Flush((UInt32)_nowPos64); return; } - if (nowPos64 - progressPosValuePrev >= (1 << 12)) + if (_nowPos64 - progressPosValuePrev >= (1 << 12)) { _finished = false; finished = false; @@ -1437,12 +1437,12 @@ namespace SharpCompress.Compressors.LZMA } } - private void ReleaseMFStream() + private void ReleaseMfStream() { - if (_matchFinder != null && _needReleaseMFStream) + if (_matchFinder != null && _needReleaseMfStream) { _matchFinder.ReleaseStream(); - _needReleaseMFStream = false; + _needReleaseMfStream = false; } } @@ -1458,7 +1458,7 @@ namespace SharpCompress.Compressors.LZMA private void ReleaseStreams() { - ReleaseMFStream(); + ReleaseMfStream(); ReleaseOutStream(); } @@ -1478,18 +1478,18 @@ namespace SharpCompress.Compressors.LZMA FillAlignPrices(); } - _lenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen); + _lenEncoder.SetTableSize(_numFastBytes + 1 - Base.K_MATCH_MIN_LEN); _lenEncoder.UpdateTables((UInt32)1 << _posStateBits); - _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.kMatchMinLen); + _repMatchLenEncoder.SetTableSize(_numFastBytes + 1 - Base.K_MATCH_MIN_LEN); _repMatchLenEncoder.UpdateTables((UInt32)1 << _posStateBits); - nowPos64 = 0; + _nowPos64 = 0; } public void Code(Stream inStream, Stream outStream, Int64 inSize, Int64 outSize, ICodeProgress progress) { - _needReleaseMFStream = false; + _needReleaseMfStream = false; _processingMode = false; try { @@ -1546,7 +1546,7 @@ namespace SharpCompress.Compressors.LZMA public void Train(Stream trainStream) { - if (nowPos64 > 0) + if (_nowPos64 > 0) { throw new InvalidOperationException(); } @@ -1566,58 +1566,58 @@ namespace SharpCompress.Compressors.LZMA } } - private const int kPropSize = 5; - private readonly Byte[] properties = new Byte[kPropSize]; + private const int K_PROP_SIZE = 5; + private readonly Byte[] _properties = new Byte[K_PROP_SIZE]; public void WriteCoderProperties(Stream outStream) { - properties[0] = (Byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits); + _properties[0] = (Byte)((_posStateBits * 5 + _numLiteralPosStateBits) * 9 + _numLiteralContextBits); for (int i = 0; i < 4; i++) { - properties[1 + i] = (Byte)((_dictionarySize >> (8 * i)) & 0xFF); + _properties[1 + i] = (Byte)((_dictionarySize >> (8 * i)) & 0xFF); } - outStream.Write(properties, 0, kPropSize); + outStream.Write(_properties, 0, K_PROP_SIZE); } - private readonly UInt32[] tempPrices = new UInt32[Base.kNumFullDistances]; + private readonly UInt32[] _tempPrices = new UInt32[Base.K_NUM_FULL_DISTANCES]; private UInt32 _matchPriceCount; private void FillDistancesPrices() { - for (UInt32 i = Base.kStartPosModelIndex; i < Base.kNumFullDistances; i++) + for (UInt32 i = Base.K_START_POS_MODEL_INDEX; i < Base.K_NUM_FULL_DISTANCES; i++) { UInt32 posSlot = GetPosSlot(i); int footerBits = (int)((posSlot >> 1) - 1); UInt32 baseVal = ((2 | (posSlot & 1)) << footerBits); - tempPrices[i] = BitTreeEncoder.ReverseGetPrice(_posEncoders, + _tempPrices[i] = BitTreeEncoder.ReverseGetPrice(_posEncoders, baseVal - posSlot - 1, footerBits, i - baseVal); } - for (UInt32 lenToPosState = 0; lenToPosState < Base.kNumLenToPosStates; lenToPosState++) + for (UInt32 lenToPosState = 0; lenToPosState < Base.K_NUM_LEN_TO_POS_STATES; lenToPosState++) { UInt32 posSlot; BitTreeEncoder encoder = _posSlotEncoder[lenToPosState]; - UInt32 st = (lenToPosState << Base.kNumPosSlotBits); + UInt32 st = (lenToPosState << Base.K_NUM_POS_SLOT_BITS); for (posSlot = 0; posSlot < _distTableSize; posSlot++) { _posSlotPrices[st + posSlot] = encoder.GetPrice(posSlot); } - for (posSlot = Base.kEndPosModelIndex; posSlot < _distTableSize; posSlot++) + for (posSlot = Base.K_END_POS_MODEL_INDEX; posSlot < _distTableSize; posSlot++) { - _posSlotPrices[st + posSlot] += ((((posSlot >> 1) - 1) - Base.kNumAlignBits) << - BitEncoder.kNumBitPriceShiftBits); + _posSlotPrices[st + posSlot] += ((((posSlot >> 1) - 1) - Base.K_NUM_ALIGN_BITS) << + BitEncoder.K_NUM_BIT_PRICE_SHIFT_BITS); } - UInt32 st2 = lenToPosState * Base.kNumFullDistances; + UInt32 st2 = lenToPosState * Base.K_NUM_FULL_DISTANCES; UInt32 i; - for (i = 0; i < Base.kStartPosModelIndex; i++) + for (i = 0; i < Base.K_START_POS_MODEL_INDEX; i++) { _distancesPrices[st2 + i] = _posSlotPrices[st + i]; } - for (; i < Base.kNumFullDistances; i++) + for (; i < Base.K_NUM_FULL_DISTANCES; i++) { - _distancesPrices[st2 + i] = _posSlotPrices[st + GetPosSlot(i)] + tempPrices[i]; + _distancesPrices[st2 + i] = _posSlotPrices[st + GetPosSlot(i)] + _tempPrices[i]; } } _matchPriceCount = 0; @@ -1625,14 +1625,14 @@ namespace SharpCompress.Compressors.LZMA private void FillAlignPrices() { - for (UInt32 i = 0; i < Base.kAlignTableSize; i++) + for (UInt32 i = 0; i < Base.K_ALIGN_TABLE_SIZE; i++) { _alignPrices[i] = _posAlignEncoder.ReverseGetPrice(i); } _alignPriceCount = 0; } - private static readonly string[] kMatchFinderIDs = + private static readonly string[] K_MATCH_FINDER_I_DS = { "BT2", "BT4" @@ -1640,9 +1640,9 @@ namespace SharpCompress.Compressors.LZMA private static int FindMatchFinder(string s) { - for (int m = 0; m < kMatchFinderIDs.Length; m++) + for (int m = 0; m < K_MATCH_FINDER_I_DS.Length; m++) { - if (s == kMatchFinderIDs[m]) + if (s == K_MATCH_FINDER_I_DS[m]) { return m; } @@ -1650,28 +1650,28 @@ namespace SharpCompress.Compressors.LZMA return -1; } - public void SetCoderProperties(CoderPropID[] propIDs, object[] properties) + public void SetCoderProperties(CoderPropId[] propIDs, object[] properties) { for (UInt32 i = 0; i < properties.Length; i++) { object prop = properties[i]; switch (propIDs[i]) { - case CoderPropID.NumFastBytes: + case CoderPropId.NumFastBytes: { if (!(prop is Int32)) { throw new InvalidParamException(); } Int32 numFastBytes = (Int32)prop; - if (numFastBytes < 5 || numFastBytes > Base.kMatchMaxLen) + if (numFastBytes < 5 || numFastBytes > Base.K_MATCH_MAX_LEN) { throw new InvalidParamException(); } _numFastBytes = (UInt32)numFastBytes; break; } - case CoderPropID.Algorithm: + case CoderPropId.Algorithm: { /* if (!(prop is Int32)) @@ -1682,7 +1682,7 @@ namespace SharpCompress.Compressors.LZMA */ break; } - case CoderPropID.MatchFinder: + case CoderPropId.MatchFinder: { if (!(prop is String)) { @@ -1702,7 +1702,7 @@ namespace SharpCompress.Compressors.LZMA } break; } - case CoderPropID.DictionarySize: + case CoderPropId.DictionarySize: { const int kDicLogSizeMaxCompress = 30; if (!(prop is Int32)) @@ -1711,7 +1711,7 @@ namespace SharpCompress.Compressors.LZMA } ; Int32 dictionarySize = (Int32)prop; - if (dictionarySize < (UInt32)(1 << Base.kDicLogSizeMin) || + if (dictionarySize < (UInt32)(1 << Base.K_DIC_LOG_SIZE_MIN) || dictionarySize > (UInt32)(1 << kDicLogSizeMaxCompress)) { throw new InvalidParamException(); @@ -1728,14 +1728,14 @@ namespace SharpCompress.Compressors.LZMA _distTableSize = (UInt32)dicLogSize * 2; break; } - case CoderPropID.PosStateBits: + case CoderPropId.PosStateBits: { if (!(prop is Int32)) { throw new InvalidParamException(); } Int32 v = (Int32)prop; - if (v < 0 || v > (UInt32)Base.kNumPosStatesBitsEncodingMax) + if (v < 0 || v > (UInt32)Base.K_NUM_POS_STATES_BITS_ENCODING_MAX) { throw new InvalidParamException(); } @@ -1743,28 +1743,28 @@ namespace SharpCompress.Compressors.LZMA _posStateMask = (((UInt32)1) << _posStateBits) - 1; break; } - case CoderPropID.LitPosBits: + case CoderPropId.LitPosBits: { if (!(prop is Int32)) { throw new InvalidParamException(); } Int32 v = (Int32)prop; - if (v < 0 || v > Base.kNumLitPosStatesBitsEncodingMax) + if (v < 0 || v > Base.K_NUM_LIT_POS_STATES_BITS_ENCODING_MAX) { throw new InvalidParamException(); } _numLiteralPosStateBits = v; break; } - case CoderPropID.LitContextBits: + case CoderPropId.LitContextBits: { if (!(prop is Int32)) { throw new InvalidParamException(); } Int32 v = (Int32)prop; - if (v < 0 || v > Base.kNumLitContextBitsMax) + if (v < 0 || v > Base.K_NUM_LIT_CONTEXT_BITS_MAX) { throw new InvalidParamException(); } @@ -1772,7 +1772,7 @@ namespace SharpCompress.Compressors.LZMA _numLiteralContextBits = v; break; } - case CoderPropID.EndMarker: + case CoderPropId.EndMarker: { if (!(prop is Boolean)) { diff --git a/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs b/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs index dd49e9dd..de7fa6f1 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaEncoderProperties.cs @@ -2,8 +2,8 @@ { public class LzmaEncoderProperties { - internal CoderPropID[] propIDs; - internal object[] properties; + internal CoderPropId[] _propIDs; + internal object[] _properties; public LzmaEncoderProperties() : this(false) @@ -28,18 +28,18 @@ int algorithm = 2; string mf = "bt4"; - propIDs = new[] + _propIDs = new[] { - CoderPropID.DictionarySize, - CoderPropID.PosStateBits, - CoderPropID.LitContextBits, - CoderPropID.LitPosBits, - CoderPropID.Algorithm, - CoderPropID.NumFastBytes, - CoderPropID.MatchFinder, - CoderPropID.EndMarker + CoderPropId.DictionarySize, + CoderPropId.PosStateBits, + CoderPropId.LitContextBits, + CoderPropId.LitPosBits, + CoderPropId.Algorithm, + CoderPropId.NumFastBytes, + CoderPropId.MatchFinder, + CoderPropId.EndMarker }; - properties = new object[] + _properties = new object[] { dictionary, posStateBits, diff --git a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs index 616cc211..c074704f 100644 --- a/src/SharpCompress/Compressors/LZMA/LzmaStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LzmaStream.cs @@ -7,29 +7,29 @@ namespace SharpCompress.Compressors.LZMA { public class LzmaStream : Stream { - private readonly Stream inputStream; - private readonly long inputSize; - private readonly long outputSize; + private readonly Stream _inputStream; + private readonly long _inputSize; + private readonly long _outputSize; - private readonly int dictionarySize; - private readonly OutWindow outWindow = new OutWindow(); - private readonly RangeCoder.Decoder rangeDecoder = new RangeCoder.Decoder(); - private Decoder decoder; + private readonly int _dictionarySize; + private readonly OutWindow _outWindow = new OutWindow(); + private readonly RangeCoder.Decoder _rangeDecoder = new RangeCoder.Decoder(); + private Decoder _decoder; - private long position; - private bool endReached; - private long availableBytes; - private long rangeDecoderLimit; - private long inputPosition; + private long _position; + private bool _endReached; + private long _availableBytes; + private long _rangeDecoderLimit; + private long _inputPosition; // LZMA2 - private readonly bool isLZMA2; - private bool uncompressedChunk; - private bool needDictReset = true; - private bool needProps = true; + private readonly bool _isLzma2; + private bool _uncompressedChunk; + private bool _needDictReset = true; + private bool _needProps = true; - private readonly Encoder encoder; - private bool isDisposed; + private readonly Encoder _encoder; + private bool _isDisposed; public LzmaStream(byte[] properties, Stream inputStream) : this(properties, inputStream, -1, -1, null, properties.Length < 5) @@ -47,82 +47,82 @@ namespace SharpCompress.Compressors.LZMA } public LzmaStream(byte[] properties, Stream inputStream, long inputSize, long outputSize, - Stream presetDictionary, bool isLZMA2) + Stream presetDictionary, bool isLzma2) { - this.inputStream = inputStream; - this.inputSize = inputSize; - this.outputSize = outputSize; - this.isLZMA2 = isLZMA2; + _inputStream = inputStream; + _inputSize = inputSize; + _outputSize = outputSize; + _isLzma2 = isLzma2; - if (!isLZMA2) + if (!isLzma2) { - dictionarySize = DataConverter.LittleEndian.GetInt32(properties, 1); - outWindow.Create(dictionarySize); + _dictionarySize = DataConverter.LittleEndian.GetInt32(properties, 1); + _outWindow.Create(_dictionarySize); if (presetDictionary != null) { - outWindow.Train(presetDictionary); + _outWindow.Train(presetDictionary); } - rangeDecoder.Init(inputStream); + _rangeDecoder.Init(inputStream); - decoder = new Decoder(); - decoder.SetDecoderProperties(properties); + _decoder = new Decoder(); + _decoder.SetDecoderProperties(properties); Properties = properties; - availableBytes = outputSize < 0 ? long.MaxValue : outputSize; - rangeDecoderLimit = inputSize; + _availableBytes = outputSize < 0 ? long.MaxValue : outputSize; + _rangeDecoderLimit = inputSize; } else { - dictionarySize = 2 | (properties[0] & 1); - dictionarySize <<= (properties[0] >> 1) + 11; + _dictionarySize = 2 | (properties[0] & 1); + _dictionarySize <<= (properties[0] >> 1) + 11; - outWindow.Create(dictionarySize); + _outWindow.Create(_dictionarySize); if (presetDictionary != null) { - outWindow.Train(presetDictionary); - needDictReset = false; + _outWindow.Train(presetDictionary); + _needDictReset = false; } Properties = new byte[1]; - availableBytes = 0; + _availableBytes = 0; } } - public LzmaStream(LzmaEncoderProperties properties, bool isLZMA2, Stream outputStream) - : this(properties, isLZMA2, null, outputStream) + public LzmaStream(LzmaEncoderProperties properties, bool isLzma2, Stream outputStream) + : this(properties, isLzma2, null, outputStream) { } - public LzmaStream(LzmaEncoderProperties properties, bool isLZMA2, Stream presetDictionary, Stream outputStream) + public LzmaStream(LzmaEncoderProperties properties, bool isLzma2, Stream presetDictionary, Stream outputStream) { - this.isLZMA2 = isLZMA2; - availableBytes = 0; - endReached = true; + _isLzma2 = isLzma2; + _availableBytes = 0; + _endReached = true; - if (isLZMA2) + if (isLzma2) { throw new NotImplementedException(); } - encoder = new Encoder(); - encoder.SetCoderProperties(properties.propIDs, properties.properties); + _encoder = new Encoder(); + _encoder.SetCoderProperties(properties._propIDs, properties._properties); MemoryStream propStream = new MemoryStream(5); - encoder.WriteCoderProperties(propStream); + _encoder.WriteCoderProperties(propStream); Properties = propStream.ToArray(); - encoder.SetStreams(null, outputStream, -1, -1); + _encoder.SetStreams(null, outputStream, -1, -1); if (presetDictionary != null) { - encoder.Train(presetDictionary); + _encoder.Train(presetDictionary); } } - public override bool CanRead => encoder == null; + public override bool CanRead => _encoder == null; public override bool CanSeek => false; - public override bool CanWrite => encoder != null; + public override bool CanWrite => _encoder != null; public override void Flush() { @@ -130,29 +130,29 @@ namespace SharpCompress.Compressors.LZMA protected override void Dispose(bool disposing) { - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; + _isDisposed = true; if (disposing) { - if (encoder != null) + if (_encoder != null) { - position = encoder.Code(null, true); + _position = _encoder.Code(null, true); } - inputStream?.Dispose(); + _inputStream?.Dispose(); } base.Dispose(disposing); } - public override long Length => position + availableBytes; + public override long Length => _position + _availableBytes; - public override long Position { get => position; set => throw new NotSupportedException(); } + public override long Position { get => _position; set => throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { - if (endReached) + if (_endReached) { return 0; } @@ -160,67 +160,67 @@ namespace SharpCompress.Compressors.LZMA int total = 0; while (total < count) { - if (availableBytes == 0) + if (_availableBytes == 0) { - if (isLZMA2) + if (_isLzma2) { - decodeChunkHeader(); + DecodeChunkHeader(); } else { - endReached = true; + _endReached = true; } - if (endReached) + if (_endReached) { break; } } int toProcess = count - total; - if (toProcess > availableBytes) + if (toProcess > _availableBytes) { - toProcess = (int)availableBytes; + toProcess = (int)_availableBytes; } - outWindow.SetLimit(toProcess); - if (uncompressedChunk) + _outWindow.SetLimit(toProcess); + if (_uncompressedChunk) { - inputPosition += outWindow.CopyStream(inputStream, toProcess); + _inputPosition += _outWindow.CopyStream(_inputStream, toProcess); } - else if (decoder.Code(dictionarySize, outWindow, rangeDecoder) - && outputSize < 0) + else if (_decoder.Code(_dictionarySize, _outWindow, _rangeDecoder) + && _outputSize < 0) { - availableBytes = outWindow.AvailableBytes; + _availableBytes = _outWindow.AvailableBytes; } - int read = outWindow.Read(buffer, offset, toProcess); + int read = _outWindow.Read(buffer, offset, toProcess); total += read; offset += read; - position += read; - availableBytes -= read; + _position += read; + _availableBytes -= read; - if (availableBytes == 0 && !uncompressedChunk) + if (_availableBytes == 0 && !_uncompressedChunk) { - rangeDecoder.ReleaseStream(); - if (!rangeDecoder.IsFinished || (rangeDecoderLimit >= 0 && rangeDecoder.Total != rangeDecoderLimit)) + _rangeDecoder.ReleaseStream(); + if (!_rangeDecoder.IsFinished || (_rangeDecoderLimit >= 0 && _rangeDecoder._total != _rangeDecoderLimit)) { throw new DataErrorException(); } - inputPosition += rangeDecoder.Total; - if (outWindow.HasPending) + _inputPosition += _rangeDecoder._total; + if (_outWindow.HasPending) { throw new DataErrorException(); } } } - if (endReached) + if (_endReached) { - if (inputSize >= 0 && inputPosition != inputSize) + if (_inputSize >= 0 && _inputPosition != _inputSize) { throw new DataErrorException(); } - if (outputSize >= 0 && position != outputSize) + if (_outputSize >= 0 && _position != _outputSize) { throw new DataErrorException(); } @@ -229,59 +229,59 @@ namespace SharpCompress.Compressors.LZMA return total; } - private void decodeChunkHeader() + private void DecodeChunkHeader() { - int control = inputStream.ReadByte(); - inputPosition++; + int control = _inputStream.ReadByte(); + _inputPosition++; if (control == 0x00) { - endReached = true; + _endReached = true; return; } if (control >= 0xE0 || control == 0x01) { - needProps = true; - needDictReset = false; - outWindow.Reset(); + _needProps = true; + _needDictReset = false; + _outWindow.Reset(); } - else if (needDictReset) + else if (_needDictReset) { throw new DataErrorException(); } if (control >= 0x80) { - uncompressedChunk = false; + _uncompressedChunk = false; - availableBytes = (control & 0x1F) << 16; - availableBytes += (inputStream.ReadByte() << 8) + inputStream.ReadByte() + 1; - inputPosition += 2; + _availableBytes = (control & 0x1F) << 16; + _availableBytes += (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1; + _inputPosition += 2; - rangeDecoderLimit = (inputStream.ReadByte() << 8) + inputStream.ReadByte() + 1; - inputPosition += 2; + _rangeDecoderLimit = (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1; + _inputPosition += 2; if (control >= 0xC0) { - needProps = false; - Properties[0] = (byte)inputStream.ReadByte(); - inputPosition++; + _needProps = false; + Properties[0] = (byte)_inputStream.ReadByte(); + _inputPosition++; - decoder = new Decoder(); - decoder.SetDecoderProperties(Properties); + _decoder = new Decoder(); + _decoder.SetDecoderProperties(Properties); } - else if (needProps) + else if (_needProps) { throw new DataErrorException(); } else if (control >= 0xA0) { - decoder = new Decoder(); - decoder.SetDecoderProperties(Properties); + _decoder = new Decoder(); + _decoder.SetDecoderProperties(Properties); } - rangeDecoder.Init(inputStream); + _rangeDecoder.Init(_inputStream); } else if (control > 0x02) { @@ -289,9 +289,9 @@ namespace SharpCompress.Compressors.LZMA } else { - uncompressedChunk = true; - availableBytes = (inputStream.ReadByte() << 8) + inputStream.ReadByte() + 1; - inputPosition += 2; + _uncompressedChunk = true; + _availableBytes = (_inputStream.ReadByte() << 8) + _inputStream.ReadByte() + 1; + _inputPosition += 2; } } @@ -307,9 +307,9 @@ namespace SharpCompress.Compressors.LZMA public override void Write(byte[] buffer, int offset, int count) { - if (encoder != null) + if (_encoder != null) { - position = encoder.Code(new MemoryStream(buffer, offset, count), false); + _position = _encoder.Code(new MemoryStream(buffer, offset, count), false); } } diff --git a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs index 6b6ab41e..f4ee15b3 100644 --- a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs +++ b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoder.cs @@ -5,12 +5,12 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder { internal class Encoder { - public const uint kTopValue = (1 << 24); + public const uint K_TOP_VALUE = (1 << 24); - private Stream Stream; + private Stream _stream; - public UInt64 Low; - public uint Range; + public UInt64 _low; + public uint _range; private uint _cacheSize; private byte _cache; @@ -18,20 +18,20 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder public void SetStream(Stream stream) { - Stream = stream; + _stream = stream; } public void ReleaseStream() { - Stream = null; + _stream = null; } public void Init() { //StartPosition = Stream.Position; - Low = 0; - Range = 0xFFFFFFFF; + _low = 0; + _range = 0xFFFFFFFF; _cacheSize = 1; _cache = 0; } @@ -46,54 +46,54 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder public void FlushStream() { - Stream.Flush(); + _stream.Flush(); } public void CloseStream() { - Stream.Dispose(); + _stream.Dispose(); } public void Encode(uint start, uint size, uint total) { - Low += start * (Range /= total); - Range *= size; - while (Range < kTopValue) + _low += start * (_range /= total); + _range *= size; + while (_range < K_TOP_VALUE) { - Range <<= 8; + _range <<= 8; ShiftLow(); } } public void ShiftLow() { - if ((uint)Low < 0xFF000000 || (uint)(Low >> 32) == 1) + if ((uint)_low < 0xFF000000 || (uint)(_low >> 32) == 1) { byte temp = _cache; do { - Stream.WriteByte((byte)(temp + (Low >> 32))); + _stream.WriteByte((byte)(temp + (_low >> 32))); temp = 0xFF; } while (--_cacheSize != 0); - _cache = (byte)(((uint)Low) >> 24); + _cache = (byte)(((uint)_low) >> 24); } _cacheSize++; - Low = ((uint)Low) << 8; + _low = ((uint)_low) << 8; } public void EncodeDirectBits(uint v, int numTotalBits) { for (int i = numTotalBits - 1; i >= 0; i--) { - Range >>= 1; + _range >>= 1; if (((v >> i) & 1) == 1) { - Low += Range; + _low += _range; } - if (Range < kTopValue) + if (_range < K_TOP_VALUE) { - Range <<= 8; + _range <<= 8; ShiftLow(); } } @@ -101,19 +101,19 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder public void EncodeBit(uint size0, int numTotalBits, uint symbol) { - uint newBound = (Range >> numTotalBits) * size0; + uint newBound = (_range >> numTotalBits) * size0; if (symbol == 0) { - Range = newBound; + _range = newBound; } else { - Low += newBound; - Range -= newBound; + _low += newBound; + _range -= newBound; } - while (Range < kTopValue) + while (_range < K_TOP_VALUE) { - Range <<= 8; + _range <<= 8; ShiftLow(); } } @@ -129,75 +129,75 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder internal class Decoder { - public const uint kTopValue = (1 << 24); - public uint Range; - public uint Code; + public const uint K_TOP_VALUE = (1 << 24); + public uint _range; + public uint _code; // public Buffer.InBuffer Stream = new Buffer.InBuffer(1 << 16); - public Stream Stream; - public long Total; + public Stream _stream; + public long _total; public void Init(Stream stream) { // Stream.Init(stream); - Stream = stream; + _stream = stream; - Code = 0; - Range = 0xFFFFFFFF; + _code = 0; + _range = 0xFFFFFFFF; for (int i = 0; i < 5; i++) { - Code = (Code << 8) | (byte)Stream.ReadByte(); + _code = (_code << 8) | (byte)_stream.ReadByte(); } - Total = 5; + _total = 5; } public void ReleaseStream() { // Stream.ReleaseStream(); - Stream = null; + _stream = null; } public void CloseStream() { - Stream.Dispose(); + _stream.Dispose(); } public void Normalize() { - while (Range < kTopValue) + while (_range < K_TOP_VALUE) { - Code = (Code << 8) | (byte)Stream.ReadByte(); - Range <<= 8; - Total++; + _code = (_code << 8) | (byte)_stream.ReadByte(); + _range <<= 8; + _total++; } } public void Normalize2() { - if (Range < kTopValue) + if (_range < K_TOP_VALUE) { - Code = (Code << 8) | (byte)Stream.ReadByte(); - Range <<= 8; - Total++; + _code = (_code << 8) | (byte)_stream.ReadByte(); + _range <<= 8; + _total++; } } public uint GetThreshold(uint total) { - return Code / (Range /= total); + return _code / (_range /= total); } public void Decode(uint start, uint size) { - Code -= start * Range; - Range *= size; + _code -= start * _range; + _range *= size; Normalize(); } public uint DecodeDirectBits(int numTotalBits) { - uint range = Range; - uint code = Code; + uint range = _range; + uint code = _code; uint result = 0; for (int i = numTotalBits; i > 0; i--) { @@ -214,38 +214,38 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder code -= range & (t - 1); result = (result << 1) | (1 - t); - if (range < kTopValue) + if (range < K_TOP_VALUE) { - code = (code << 8) | (byte)Stream.ReadByte(); + code = (code << 8) | (byte)_stream.ReadByte(); range <<= 8; - Total++; + _total++; } } - Range = range; - Code = code; + _range = range; + _code = code; return result; } public uint DecodeBit(uint size0, int numTotalBits) { - uint newBound = (Range >> numTotalBits) * size0; + uint newBound = (_range >> numTotalBits) * size0; uint symbol; - if (Code < newBound) + if (_code < newBound) { symbol = 0; - Range = newBound; + _range = newBound; } else { symbol = 1; - Code -= newBound; - Range -= newBound; + _code -= newBound; + _range -= newBound; } Normalize(); return symbol; } - public bool IsFinished => Code == 0; + public bool IsFinished => _code == 0; // ulong GetProcessedSize() {return Stream.GetProcessedSize(); } } diff --git a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBit.cs b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBit.cs index cfb312a2..2ab53aff 100644 --- a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBit.cs +++ b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBit.cs @@ -4,28 +4,28 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder { internal struct BitEncoder { - public const int kNumBitModelTotalBits = 11; - public const uint kBitModelTotal = (1 << kNumBitModelTotalBits); - private const int kNumMoveBits = 5; - private const int kNumMoveReducingBits = 2; - public const int kNumBitPriceShiftBits = 6; + public const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; + public const uint K_BIT_MODEL_TOTAL = (1 << K_NUM_BIT_MODEL_TOTAL_BITS); + private const int K_NUM_MOVE_BITS = 5; + private const int K_NUM_MOVE_REDUCING_BITS = 2; + public const int K_NUM_BIT_PRICE_SHIFT_BITS = 6; - private uint Prob; + private uint _prob; public void Init() { - Prob = kBitModelTotal >> 1; + _prob = K_BIT_MODEL_TOTAL >> 1; } public void UpdateModel(uint symbol) { if (symbol == 0) { - Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + _prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS; } else { - Prob -= (Prob) >> kNumMoveBits; + _prob -= (_prob) >> K_NUM_MOVE_BITS; } } @@ -33,106 +33,106 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder { // encoder.EncodeBit(Prob, kNumBitModelTotalBits, symbol); // UpdateModel(symbol); - uint newBound = (encoder.Range >> kNumBitModelTotalBits) * Prob; + uint newBound = (encoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob; if (symbol == 0) { - encoder.Range = newBound; - Prob += (kBitModelTotal - Prob) >> kNumMoveBits; + encoder._range = newBound; + _prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS; } else { - encoder.Low += newBound; - encoder.Range -= newBound; - Prob -= (Prob) >> kNumMoveBits; + encoder._low += newBound; + encoder._range -= newBound; + _prob -= (_prob) >> K_NUM_MOVE_BITS; } - if (encoder.Range < Encoder.kTopValue) + if (encoder._range < Encoder.K_TOP_VALUE) { - encoder.Range <<= 8; + encoder._range <<= 8; encoder.ShiftLow(); } } - private static readonly UInt32[] ProbPrices = new UInt32[kBitModelTotal >> kNumMoveReducingBits]; + private static readonly UInt32[] PROB_PRICES = new UInt32[K_BIT_MODEL_TOTAL >> K_NUM_MOVE_REDUCING_BITS]; static BitEncoder() { - const int kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits); + const int kNumBits = (K_NUM_BIT_MODEL_TOTAL_BITS - K_NUM_MOVE_REDUCING_BITS); for (int i = kNumBits - 1; i >= 0; i--) { UInt32 start = (UInt32)1 << (kNumBits - i - 1); UInt32 end = (UInt32)1 << (kNumBits - i); for (UInt32 j = start; j < end; j++) { - ProbPrices[j] = ((UInt32)i << kNumBitPriceShiftBits) + - (((end - j) << kNumBitPriceShiftBits) >> (kNumBits - i - 1)); + PROB_PRICES[j] = ((UInt32)i << K_NUM_BIT_PRICE_SHIFT_BITS) + + (((end - j) << K_NUM_BIT_PRICE_SHIFT_BITS) >> (kNumBits - i - 1)); } } } public uint GetPrice(uint symbol) { - return ProbPrices[(((Prob - symbol) ^ ((-(int)symbol))) & (kBitModelTotal - 1)) >> kNumMoveReducingBits]; + return PROB_PRICES[(((_prob - symbol) ^ ((-(int)symbol))) & (K_BIT_MODEL_TOTAL - 1)) >> K_NUM_MOVE_REDUCING_BITS]; } public uint GetPrice0() { - return ProbPrices[Prob >> kNumMoveReducingBits]; + return PROB_PRICES[_prob >> K_NUM_MOVE_REDUCING_BITS]; } public uint GetPrice1() { - return ProbPrices[(kBitModelTotal - Prob) >> kNumMoveReducingBits]; + return PROB_PRICES[(K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_REDUCING_BITS]; } } internal struct BitDecoder { - public const int kNumBitModelTotalBits = 11; - public const uint kBitModelTotal = (1 << kNumBitModelTotalBits); - private const int kNumMoveBits = 5; + public const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; + public const uint K_BIT_MODEL_TOTAL = (1 << K_NUM_BIT_MODEL_TOTAL_BITS); + private const int K_NUM_MOVE_BITS = 5; - private uint Prob; + private uint _prob; public void UpdateModel(int numMoveBits, uint symbol) { if (symbol == 0) { - Prob += (kBitModelTotal - Prob) >> numMoveBits; + _prob += (K_BIT_MODEL_TOTAL - _prob) >> numMoveBits; } else { - Prob -= (Prob) >> numMoveBits; + _prob -= (_prob) >> numMoveBits; } } public void Init() { - Prob = kBitModelTotal >> 1; + _prob = K_BIT_MODEL_TOTAL >> 1; } public uint Decode(Decoder rangeDecoder) { - uint newBound = (rangeDecoder.Range >> kNumBitModelTotalBits) * Prob; - if (rangeDecoder.Code < newBound) + uint newBound = (rangeDecoder._range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _prob; + if (rangeDecoder._code < newBound) { - rangeDecoder.Range = newBound; - Prob += (kBitModelTotal - Prob) >> kNumMoveBits; - if (rangeDecoder.Range < Decoder.kTopValue) + rangeDecoder._range = newBound; + _prob += (K_BIT_MODEL_TOTAL - _prob) >> K_NUM_MOVE_BITS; + if (rangeDecoder._range < Decoder.K_TOP_VALUE) { - rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); - rangeDecoder.Range <<= 8; - rangeDecoder.Total++; + rangeDecoder._code = (rangeDecoder._code << 8) | (byte)rangeDecoder._stream.ReadByte(); + rangeDecoder._range <<= 8; + rangeDecoder._total++; } return 0; } - rangeDecoder.Range -= newBound; - rangeDecoder.Code -= newBound; - Prob -= (Prob) >> kNumMoveBits; - if (rangeDecoder.Range < Decoder.kTopValue) + rangeDecoder._range -= newBound; + rangeDecoder._code -= newBound; + _prob -= (_prob) >> K_NUM_MOVE_BITS; + if (rangeDecoder._range < Decoder.K_TOP_VALUE) { - rangeDecoder.Code = (rangeDecoder.Code << 8) | (byte)rangeDecoder.Stream.ReadByte(); - rangeDecoder.Range <<= 8; - rangeDecoder.Total++; + rangeDecoder._code = (rangeDecoder._code << 8) | (byte)rangeDecoder._stream.ReadByte(); + rangeDecoder._range <<= 8; + rangeDecoder._total++; } return 1; } diff --git a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBitTree.cs b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBitTree.cs index 7196129f..cc511603 100644 --- a/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBitTree.cs +++ b/src/SharpCompress/Compressors/LZMA/RangeCoder/RangeCoderBitTree.cs @@ -4,31 +4,31 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder { internal struct BitTreeEncoder { - private readonly BitEncoder[] Models; - private readonly int NumBitLevels; + private readonly BitEncoder[] _models; + private readonly int _numBitLevels; public BitTreeEncoder(int numBitLevels) { - NumBitLevels = numBitLevels; - Models = new BitEncoder[1 << numBitLevels]; + _numBitLevels = numBitLevels; + _models = new BitEncoder[1 << numBitLevels]; } public void Init() { - for (uint i = 1; i < (1 << NumBitLevels); i++) + for (uint i = 1; i < (1 << _numBitLevels); i++) { - Models[i].Init(); + _models[i].Init(); } } public void Encode(Encoder rangeEncoder, UInt32 symbol) { UInt32 m = 1; - for (int bitIndex = NumBitLevels; bitIndex > 0;) + for (int bitIndex = _numBitLevels; bitIndex > 0;) { bitIndex--; UInt32 bit = (symbol >> bitIndex) & 1; - Models[m].Encode(rangeEncoder, bit); + _models[m].Encode(rangeEncoder, bit); m = (m << 1) | bit; } } @@ -36,10 +36,10 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder public void ReverseEncode(Encoder rangeEncoder, UInt32 symbol) { UInt32 m = 1; - for (UInt32 i = 0; i < NumBitLevels; i++) + for (UInt32 i = 0; i < _numBitLevels; i++) { UInt32 bit = symbol & 1; - Models[m].Encode(rangeEncoder, bit); + _models[m].Encode(rangeEncoder, bit); m = (m << 1) | bit; symbol >>= 1; } @@ -49,11 +49,11 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder { UInt32 price = 0; UInt32 m = 1; - for (int bitIndex = NumBitLevels; bitIndex > 0;) + for (int bitIndex = _numBitLevels; bitIndex > 0;) { bitIndex--; UInt32 bit = (symbol >> bitIndex) & 1; - price += Models[m].GetPrice(bit); + price += _models[m].GetPrice(bit); m = (m << 1) + bit; } return price; @@ -63,39 +63,39 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder { UInt32 price = 0; UInt32 m = 1; - for (int i = NumBitLevels; i > 0; i--) + for (int i = _numBitLevels; i > 0; i--) { UInt32 bit = symbol & 1; symbol >>= 1; - price += Models[m].GetPrice(bit); + price += _models[m].GetPrice(bit); m = (m << 1) | bit; } return price; } - public static UInt32 ReverseGetPrice(BitEncoder[] Models, UInt32 startIndex, - int NumBitLevels, UInt32 symbol) + public static UInt32 ReverseGetPrice(BitEncoder[] models, UInt32 startIndex, + int numBitLevels, UInt32 symbol) { UInt32 price = 0; UInt32 m = 1; - for (int i = NumBitLevels; i > 0; i--) + for (int i = numBitLevels; i > 0; i--) { UInt32 bit = symbol & 1; symbol >>= 1; - price += Models[startIndex + m].GetPrice(bit); + price += models[startIndex + m].GetPrice(bit); m = (m << 1) | bit; } return price; } - public static void ReverseEncode(BitEncoder[] Models, UInt32 startIndex, - Encoder rangeEncoder, int NumBitLevels, UInt32 symbol) + public static void ReverseEncode(BitEncoder[] models, UInt32 startIndex, + Encoder rangeEncoder, int numBitLevels, UInt32 symbol) { UInt32 m = 1; - for (int i = 0; i < NumBitLevels; i++) + for (int i = 0; i < numBitLevels; i++) { UInt32 bit = symbol & 1; - Models[startIndex + m].Encode(rangeEncoder, bit); + models[startIndex + m].Encode(rangeEncoder, bit); m = (m << 1) | bit; symbol >>= 1; } @@ -104,40 +104,40 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder internal struct BitTreeDecoder { - private readonly BitDecoder[] Models; - private readonly int NumBitLevels; + private readonly BitDecoder[] _models; + private readonly int _numBitLevels; public BitTreeDecoder(int numBitLevels) { - NumBitLevels = numBitLevels; - Models = new BitDecoder[1 << numBitLevels]; + _numBitLevels = numBitLevels; + _models = new BitDecoder[1 << numBitLevels]; } public void Init() { - for (uint i = 1; i < (1 << NumBitLevels); i++) + for (uint i = 1; i < (1 << _numBitLevels); i++) { - Models[i].Init(); + _models[i].Init(); } } public uint Decode(Decoder rangeDecoder) { uint m = 1; - for (int bitIndex = NumBitLevels; bitIndex > 0; bitIndex--) + for (int bitIndex = _numBitLevels; bitIndex > 0; bitIndex--) { - m = (m << 1) + Models[m].Decode(rangeDecoder); + m = (m << 1) + _models[m].Decode(rangeDecoder); } - return m - ((uint)1 << NumBitLevels); + return m - ((uint)1 << _numBitLevels); } public uint ReverseDecode(Decoder rangeDecoder) { uint m = 1; uint symbol = 0; - for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + for (int bitIndex = 0; bitIndex < _numBitLevels; bitIndex++) { - uint bit = Models[m].Decode(rangeDecoder); + uint bit = _models[m].Decode(rangeDecoder); m <<= 1; m += bit; symbol |= (bit << bitIndex); @@ -145,14 +145,14 @@ namespace SharpCompress.Compressors.LZMA.RangeCoder return symbol; } - public static uint ReverseDecode(BitDecoder[] Models, UInt32 startIndex, - Decoder rangeDecoder, int NumBitLevels) + public static uint ReverseDecode(BitDecoder[] models, UInt32 startIndex, + Decoder rangeDecoder, int numBitLevels) { uint m = 1; uint symbol = 0; - for (int bitIndex = 0; bitIndex < NumBitLevels; bitIndex++) + for (int bitIndex = 0; bitIndex < numBitLevels; bitIndex++) { - uint bit = Models[startIndex + m].Decode(rangeDecoder); + uint bit = models[startIndex + m].Decode(rangeDecoder); m <<= 1; m += bit; symbol |= (bit << bitIndex); diff --git a/src/SharpCompress/Compressors/LZMA/Registry.cs b/src/SharpCompress/Compressors/LZMA/Registry.cs index 6a42e70c..9c95a5ae 100644 --- a/src/SharpCompress/Compressors/LZMA/Registry.cs +++ b/src/SharpCompress/Compressors/LZMA/Registry.cs @@ -12,43 +12,43 @@ namespace SharpCompress.Compressors.LZMA { internal static class DecoderRegistry { - private const uint k_Copy = 0x0; - private const uint k_Delta = 3; - private const uint k_LZMA2 = 0x21; - private const uint k_LZMA = 0x030101; - private const uint k_PPMD = 0x030401; - private const uint k_BCJ = 0x03030103; - private const uint k_BCJ2 = 0x0303011B; - private const uint k_Deflate = 0x040108; - private const uint k_BZip2 = 0x040202; + private const uint K_COPY = 0x0; + private const uint K_DELTA = 3; + private const uint K_LZMA2 = 0x21; + private const uint K_LZMA = 0x030101; + private const uint K_PPMD = 0x030401; + private const uint K_BCJ = 0x03030103; + private const uint K_BCJ2 = 0x0303011B; + private const uint K_DEFLATE = 0x040108; + private const uint K_B_ZIP2 = 0x040202; internal static Stream CreateDecoderStream(CMethodId id, Stream[] inStreams, byte[] info, IPasswordProvider pass, long limit) { switch (id.Id) { - case k_Copy: + case K_COPY: if (info != null) { throw new NotSupportedException(); } return inStreams.Single(); - case k_LZMA: - case k_LZMA2: + case K_LZMA: + case K_LZMA2: return new LzmaStream(info, inStreams.Single(), -1, limit); #if !NO_CRYPTO case CMethodId.kAESId: return new AesDecoderStream(inStreams.Single(), info, pass, limit); #endif - case k_BCJ: + case K_BCJ: return new BCJFilter(false, inStreams.Single()); - case k_BCJ2: + case K_BCJ2: return new Bcj2DecoderStream(inStreams, info, limit); - case k_BZip2: + case K_B_ZIP2: return new BZip2Stream(inStreams.Single(), CompressionMode.Decompress, true); - case k_PPMD: + case K_PPMD: return new PpmdStream(new PpmdProperties(info), inStreams.Single(), false); - case k_Deflate: + case K_DEFLATE: return new DeflateStream(inStreams.Single(), CompressionMode.Decompress); default: throw new NotSupportedException(); diff --git a/src/SharpCompress/Compressors/LZMA/Utilites/CrcBuilderStream.cs b/src/SharpCompress/Compressors/LZMA/Utilites/CrcBuilderStream.cs index 1da7a723..4410dd9a 100644 --- a/src/SharpCompress/Compressors/LZMA/Utilites/CrcBuilderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/Utilites/CrcBuilderStream.cs @@ -5,25 +5,25 @@ namespace SharpCompress.Compressors.LZMA.Utilites { internal class CrcBuilderStream : Stream { - private readonly Stream mTarget; - private uint mCRC; - private bool mFinished; - private bool isDisposed; + private readonly Stream _mTarget; + private uint _mCrc; + private bool _mFinished; + private bool _isDisposed; public CrcBuilderStream(Stream target) { - mTarget = target; - mCRC = CRC.kInitCRC; + _mTarget = target; + _mCrc = Crc.INIT_CRC; } protected override void Dispose(bool disposing) { - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; - mTarget.Dispose(); + _isDisposed = true; + _mTarget.Dispose(); base.Dispose(disposing); } @@ -31,13 +31,13 @@ namespace SharpCompress.Compressors.LZMA.Utilites public uint Finish() { - if (!mFinished) + if (!_mFinished) { - mFinished = true; - mCRC = CRC.Finish(mCRC); + _mFinished = true; + _mCrc = Crc.Finish(_mCrc); } - return mCRC; + return _mCrc; } public override bool CanRead => false; @@ -71,103 +71,14 @@ namespace SharpCompress.Compressors.LZMA.Utilites public override void Write(byte[] buffer, int offset, int count) { - if (mFinished) + if (_mFinished) { throw new InvalidOperationException("CRC calculation has been finished."); } Processed += count; - mCRC = CRC.Update(mCRC, buffer, offset, count); - mTarget.Write(buffer, offset, count); - } - } - - internal class ReadingCrcBuilderStream : Stream - { - private readonly Stream mSource; - private uint mCRC; - private bool mFinished; - - public ReadingCrcBuilderStream(Stream source) - { - mSource = source; - mCRC = CRC.kInitCRC; - } - - protected override void Dispose(bool disposing) - { - try - { - if (disposing) - { - mSource.Dispose(); - } - } - finally - { - base.Dispose(disposing); - } - } - - public long Processed { get; private set; } - - public uint Finish() - { - if (!mFinished) - { - mFinished = true; - mCRC = CRC.Finish(mCRC); - } - - return mCRC; - } - - public override bool CanRead => mSource.CanRead; - - public override bool CanSeek => false; - - public override bool CanWrite => false; - - public override void Flush() - { - throw new NotSupportedException(); - } - - public override long Length => throw new NotSupportedException(); - - public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } - - public override int Read(byte[] buffer, int offset, int count) - { - if (count > 0 && !mFinished) - { - int read = mSource.Read(buffer, offset, count); - if (read > 0) - { - Processed += read; - mCRC = CRC.Update(mCRC, buffer, offset, read); - return read; - } - - Finish(); - } - - return 0; - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotSupportedException(); + _mCrc = Crc.Update(_mCrc, buffer, offset, count); + _mTarget.Write(buffer, offset, count); } } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/LZMA/Utilites/CrcCheckStream.cs b/src/SharpCompress/Compressors/LZMA/Utilites/CrcCheckStream.cs index f64887e4..04b00f81 100644 --- a/src/SharpCompress/Compressors/LZMA/Utilites/CrcCheckStream.cs +++ b/src/SharpCompress/Compressors/LZMA/Utilites/CrcCheckStream.cs @@ -6,35 +6,35 @@ namespace SharpCompress.Compressors.LZMA.Utilites { internal class CrcCheckStream : Stream { - private readonly uint mExpectedCRC; - private uint mCurrentCRC; - private bool mClosed; + private readonly uint _mExpectedCrc; + private uint _mCurrentCrc; + private bool _mClosed; - private readonly long[] mBytes = new long[256]; - private long mLength; + private readonly long[] _mBytes = new long[256]; + private long _mLength; public CrcCheckStream(uint crc) { - mExpectedCRC = crc; - mCurrentCRC = CRC.kInitCRC; + _mExpectedCrc = crc; + _mCurrentCrc = Crc.INIT_CRC; } protected override void Dispose(bool disposing) { - if (mCurrentCRC != mExpectedCRC) + if (_mCurrentCrc != _mExpectedCrc) { throw new InvalidOperationException(); } try { - if (disposing && !mClosed) + if (disposing && !_mClosed) { - mClosed = true; - mCurrentCRC = CRC.Finish(mCurrentCRC); + _mClosed = true; + _mCurrentCrc = Crc.Finish(_mCurrentCrc); #if DEBUG - if (mCurrentCRC == mExpectedCRC) + if (_mCurrentCrc == _mExpectedCrc) { - Debug.WriteLine("CRC ok: " + mExpectedCRC.ToString("x8")); + Debug.WriteLine("CRC ok: " + _mExpectedCrc.ToString("x8")); } else { @@ -42,13 +42,13 @@ namespace SharpCompress.Compressors.LZMA.Utilites Debug.WriteLine("bad CRC"); } - double lengthInv = 1.0 / mLength; + double lengthInv = 1.0 / _mLength; double entropy = 0; for (int i = 0; i < 256; i++) { - if (mBytes[i] != 0) + if (_mBytes[i] != 0) { - double p = lengthInv * mBytes[i]; + double p = lengthInv * _mBytes[i]; entropy -= p * Math.Log(p, 256); } } @@ -93,13 +93,13 @@ namespace SharpCompress.Compressors.LZMA.Utilites public override void Write(byte[] buffer, int offset, int count) { - mLength += count; + _mLength += count; for (int i = 0; i < count; i++) { - mBytes[buffer[offset + i]]++; + _mBytes[buffer[offset + i]]++; } - mCurrentCRC = CRC.Update(mCurrentCRC, buffer, offset, count); + _mCurrentCrc = Crc.Update(_mCurrentCrc, buffer, offset, count); } } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/LZMA/Utilites/Utils.cs b/src/SharpCompress/Compressors/LZMA/Utilites/Utils.cs index ad0e54b0..9967e292 100644 --- a/src/SharpCompress/Compressors/LZMA/Utilites/Utils.cs +++ b/src/SharpCompress/Compressors/LZMA/Utilites/Utils.cs @@ -18,7 +18,7 @@ namespace SharpCompress.Compressors.LZMA.Utilites UnpackInfo = 7, SubStreamsInfo = 8, Size = 9, - CRC = 10, + Crc = 10, Folder = 11, CodersUnpackSize = 12, NumUnpackStream = 13, diff --git a/src/SharpCompress/Compressors/PPMd/H/FreqData.cs b/src/SharpCompress/Compressors/PPMd/H/FreqData.cs index 731ed972..1c01404b 100644 --- a/src/SharpCompress/Compressors/PPMd/H/FreqData.cs +++ b/src/SharpCompress/Compressors/PPMd/H/FreqData.cs @@ -6,7 +6,7 @@ namespace SharpCompress.Compressors.PPMd.H { internal class FreqData : Pointer { - internal const int Size = 6; + internal const int SIZE = 6; // struct FreqData // { @@ -14,8 +14,8 @@ namespace SharpCompress.Compressors.PPMd.H // STATE _PACK_ATTR * Stats; // }; - internal FreqData(byte[] Memory) - : base(Memory) + internal FreqData(byte[] memory) + : base(memory) { } @@ -55,7 +55,7 @@ namespace SharpCompress.Compressors.PPMd.H buffer.Append("\n Address="); buffer.Append(Address); buffer.Append("\n size="); - buffer.Append(Size); + buffer.Append(SIZE); buffer.Append("\n summFreq="); buffer.Append(SummFreq); buffer.Append("\n stats="); diff --git a/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs b/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs index 7c16cc38..883aa062 100644 --- a/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs +++ b/src/SharpCompress/Compressors/PPMd/H/ModelPPM.cs @@ -6,41 +6,41 @@ using Decoder = SharpCompress.Compressors.LZMA.RangeCoder.Decoder; namespace SharpCompress.Compressors.PPMd.H { - internal class ModelPPM + internal class ModelPpm { private void InitBlock() { for (int i = 0; i < 25; i++) { - SEE2Cont[i] = new SEE2Context[16]; + _see2Cont[i] = new See2Context[16]; } for (int i2 = 0; i2 < 128; i2++) { - binSumm[i2] = new int[64]; + _binSumm[i2] = new int[64]; } } public SubAllocator SubAlloc { get; } = new SubAllocator(); - public virtual SEE2Context DummySEE2Cont => dummySEE2Cont; + public virtual See2Context DummySee2Cont => _dummySee2Cont; - public virtual int InitRL => initRL; + public virtual int InitRl => _initRl; - public virtual int EscCount { get => escCount; set => escCount = value & 0xff; } + public virtual int EscCount { get => _escCount; set => _escCount = value & 0xff; } - public virtual int[] CharMask => charMask; + public virtual int[] CharMask => _charMask; - public virtual int NumMasked { get => numMasked; set => numMasked = value; } + public virtual int NumMasked { get => _numMasked; set => _numMasked = value; } - public virtual int PrevSuccess { get => prevSuccess; set => prevSuccess = value & 0xff; } + public virtual int PrevSuccess { get => _prevSuccess; set => _prevSuccess = value & 0xff; } - public virtual int InitEsc { get => initEsc; set => initEsc = value; } + public virtual int InitEsc { get => _initEsc; set => _initEsc = value; } - public virtual int RunLength { get => runLength; set => runLength = value; } + public virtual int RunLength { get => _runLength; set => _runLength = value; } - public virtual int HiBitsFlag { get => hiBitsFlag; set => hiBitsFlag = value & 0xff; } + public virtual int HiBitsFlag { get => _hiBitsFlag; set => _hiBitsFlag = value & 0xff; } - public virtual int[][] BinSumm => binSumm; + public virtual int[][] BinSumm => _binSumm; internal RangeCoder Coder { get; private set; } @@ -48,7 +48,7 @@ namespace SharpCompress.Compressors.PPMd.H public virtual byte[] Heap => SubAlloc.Heap; - public virtual int OrderFall => orderFall; + public virtual int OrderFall => _orderFall; public const int MAX_O = 64; /* maximum allowed model order */ @@ -67,98 +67,98 @@ namespace SharpCompress.Compressors.PPMd.H public const int MAX_FREQ = 124; - private readonly SEE2Context[][] SEE2Cont = new SEE2Context[25][]; + private readonly See2Context[][] _see2Cont = new See2Context[25][]; - private SEE2Context dummySEE2Cont; + private See2Context _dummySee2Cont; - private PPMContext minContext; //medContext + private PpmContext _minContext; //medContext - private PPMContext maxContext; + private PpmContext _maxContext; - private int numMasked, initEsc, orderFall, maxOrder, runLength, initRL; + private int _numMasked, _initEsc, _orderFall, _maxOrder, _runLength, _initRl; - private readonly int[] charMask = new int[256]; + private readonly int[] _charMask = new int[256]; - private readonly int[] NS2Indx = new int[256]; + private readonly int[] _ns2Indx = new int[256]; - private readonly int[] NS2BSIndx = new int[256]; + private readonly int[] _ns2BsIndx = new int[256]; - private readonly int[] HB2Flag = new int[256]; + private readonly int[] _hb2Flag = new int[256]; // byte EscCount, PrevSuccess, HiBitsFlag; - private int escCount, prevSuccess, hiBitsFlag; + private int _escCount, _prevSuccess, _hiBitsFlag; - private readonly int[][] binSumm = new int[128][]; // binary SEE-contexts + private readonly int[][] _binSumm = new int[128][]; // binary SEE-contexts - private static readonly int[] InitBinEsc = {0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; + private static readonly int[] INIT_BIN_ESC = {0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051}; // Temp fields //UPGRADE_NOTE: Final was removed from the declaration of 'tempState1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State tempState1 = new State(null); + private readonly State _tempState1 = new State(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State tempState2 = new State(null); + private readonly State _tempState2 = new State(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State tempState3 = new State(null); + private readonly State _tempState3 = new State(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State tempState4 = new State(null); + private readonly State _tempState4 = new State(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempStateRef1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly StateRef tempStateRef1 = new StateRef(); + private readonly StateRef _tempStateRef1 = new StateRef(); //UPGRADE_NOTE: Final was removed from the declaration of 'tempStateRef2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly StateRef tempStateRef2 = new StateRef(); + private readonly StateRef _tempStateRef2 = new StateRef(); //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PPMContext tempPPMContext1 = new PPMContext(null); + private readonly PpmContext _tempPpmContext1 = new PpmContext(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PPMContext tempPPMContext2 = new PPMContext(null); + private readonly PpmContext _tempPpmContext2 = new PpmContext(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PPMContext tempPPMContext3 = new PPMContext(null); + private readonly PpmContext _tempPpmContext3 = new PpmContext(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempPPMContext4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly PPMContext tempPPMContext4 = new PPMContext(null); + private readonly PpmContext _tempPpmContext4 = new PpmContext(null); //UPGRADE_NOTE: Final was removed from the declaration of 'ps '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly int[] ps = new int[MAX_O]; + private readonly int[] _ps = new int[MAX_O]; - public ModelPPM() + public ModelPpm() { InitBlock(); - minContext = null; - maxContext = null; + _minContext = null; + _maxContext = null; //medContext = null; } - private void restartModelRare() + private void RestartModelRare() { - Utility.Fill(charMask, 0); - SubAlloc.initSubAllocator(); - initRL = -(maxOrder < 12 ? maxOrder : 12) - 1; - int addr = SubAlloc.allocContext(); - minContext.Address = addr; - maxContext.Address = addr; - minContext.setSuffix(0); - orderFall = maxOrder; - minContext.NumStats = 256; - minContext.FreqData.SummFreq = minContext.NumStats + 1; + Utility.Fill(_charMask, 0); + SubAlloc.InitSubAllocator(); + _initRl = -(_maxOrder < 12 ? _maxOrder : 12) - 1; + int addr = SubAlloc.AllocContext(); + _minContext.Address = addr; + _maxContext.Address = addr; + _minContext.SetSuffix(0); + _orderFall = _maxOrder; + _minContext.NumStats = 256; + _minContext.FreqData.SummFreq = _minContext.NumStats + 1; - addr = SubAlloc.allocUnits(256 / 2); + addr = SubAlloc.AllocUnits(256 / 2); FoundState.Address = addr; - minContext.FreqData.SetStats(addr); + _minContext.FreqData.SetStats(addr); State state = new State(SubAlloc.Heap); - addr = minContext.FreqData.GetStats(); - runLength = initRL; - prevSuccess = 0; + addr = _minContext.FreqData.GetStats(); + _runLength = _initRl; + _prevSuccess = 0; for (int i = 0; i < 256; i++) { - state.Address = addr + i * State.Size; + state.Address = addr + i * State.SIZE; state.Symbol = i; state.Freq = 1; state.SetSuccessor(0); @@ -170,7 +170,7 @@ namespace SharpCompress.Compressors.PPMd.H { for (int m = 0; m < 64; m += 8) { - binSumm[i][k + m] = BIN_SCALE - InitBinEsc[k] / (i + 2); + _binSumm[i][k + m] = BIN_SCALE - INIT_BIN_ESC[k] / (i + 2); } } } @@ -178,68 +178,68 @@ namespace SharpCompress.Compressors.PPMd.H { for (int k = 0; k < 16; k++) { - SEE2Cont[i][k].Initialize(5 * i + 10); + _see2Cont[i][k].Initialize(5 * i + 10); } } } - private void startModelRare(int MaxOrder) + private void StartModelRare(int maxOrder) { - int i, k, m, Step; - escCount = 1; - maxOrder = MaxOrder; - restartModelRare(); + int i, k, m, step; + _escCount = 1; + _maxOrder = maxOrder; + RestartModelRare(); // Bug Fixed - NS2BSIndx[0] = 0; - NS2BSIndx[1] = 2; + _ns2BsIndx[0] = 0; + _ns2BsIndx[1] = 2; for (int j = 0; j < 9; j++) { - NS2BSIndx[2 + j] = 4; + _ns2BsIndx[2 + j] = 4; } for (int j = 0; j < 256 - 11; j++) { - NS2BSIndx[11 + j] = 6; + _ns2BsIndx[11 + j] = 6; } for (i = 0; i < 3; i++) { - NS2Indx[i] = i; + _ns2Indx[i] = i; } - for (m = i, k = 1, Step = 1; i < 256; i++) + for (m = i, k = 1, step = 1; i < 256; i++) { - NS2Indx[i] = m; + _ns2Indx[i] = m; if ((--k) == 0) { - k = ++Step; + k = ++step; m++; } } for (int j = 0; j < 0x40; j++) { - HB2Flag[j] = 0; + _hb2Flag[j] = 0; } for (int j = 0; j < 0x100 - 0x40; j++) { - HB2Flag[0x40 + j] = 0x08; + _hb2Flag[0x40 + j] = 0x08; } - dummySEE2Cont.Shift = PERIOD_BITS; + _dummySee2Cont.Shift = PERIOD_BITS; } - private void clearMask() + private void ClearMask() { - escCount = 1; - Utility.Fill(charMask, 0); + _escCount = 1; + Utility.Fill(_charMask, 0); } - internal bool decodeInit(IRarUnpack unpackRead, int escChar) + internal bool DecodeInit(IRarUnpack unpackRead, int escChar) { - int MaxOrder = unpackRead.Char & 0xff; - bool reset = ((MaxOrder & 0x20) != 0); + int maxOrder = unpackRead.Char & 0xff; + bool reset = ((maxOrder & 0x20) != 0); - int MaxMB = 0; + int maxMb = 0; if (reset) { - MaxMB = unpackRead.Char; + maxMb = unpackRead.Char; } else { @@ -248,7 +248,7 @@ namespace SharpCompress.Compressors.PPMd.H return (false); } } - if ((MaxOrder & 0x40) != 0) + if ((maxOrder & 0x40) != 0) { escChar = unpackRead.Char; unpackRead.PpmEscChar = escChar; @@ -256,60 +256,60 @@ namespace SharpCompress.Compressors.PPMd.H Coder = new RangeCoder(unpackRead); if (reset) { - MaxOrder = (MaxOrder & 0x1f) + 1; - if (MaxOrder > 16) + maxOrder = (maxOrder & 0x1f) + 1; + if (maxOrder > 16) { - MaxOrder = 16 + (MaxOrder - 16) * 3; + maxOrder = 16 + (maxOrder - 16) * 3; } - if (MaxOrder == 1) + if (maxOrder == 1) { - SubAlloc.stopSubAllocator(); + SubAlloc.StopSubAllocator(); return (false); } - SubAlloc.startSubAllocator((MaxMB + 1) << 20); - minContext = new PPMContext(Heap); + SubAlloc.StartSubAllocator((maxMb + 1) << 20); + _minContext = new PpmContext(Heap); //medContext = new PPMContext(Heap); - maxContext = new PPMContext(Heap); + _maxContext = new PpmContext(Heap); FoundState = new State(Heap); - dummySEE2Cont = new SEE2Context(); + _dummySee2Cont = new See2Context(); for (int i = 0; i < 25; i++) { for (int j = 0; j < 16; j++) { - SEE2Cont[i][j] = new SEE2Context(); + _see2Cont[i][j] = new See2Context(); } } - startModelRare(MaxOrder); + StartModelRare(maxOrder); } - return (minContext.Address != 0); + return (_minContext.Address != 0); } - public virtual int decodeChar() + public virtual int DecodeChar() { // Debug //subAlloc.dumpHeap(); - if (minContext.Address <= SubAlloc.PText || minContext.Address > SubAlloc.HeapEnd) + if (_minContext.Address <= SubAlloc.PText || _minContext.Address > SubAlloc.HeapEnd) { return (-1); } - if (minContext.NumStats != 1) + if (_minContext.NumStats != 1) { - if (minContext.FreqData.GetStats() <= SubAlloc.PText || - minContext.FreqData.GetStats() > SubAlloc.HeapEnd) + if (_minContext.FreqData.GetStats() <= SubAlloc.PText || + _minContext.FreqData.GetStats() > SubAlloc.HeapEnd) { return (-1); } - if (!minContext.decodeSymbol1(this)) + if (!_minContext.DecodeSymbol1(this)) { return (-1); } } else { - minContext.decodeBinSymbol(this); + _minContext.DecodeBinSymbol(this); } Coder.Decode(); while (FoundState.Address == 0) @@ -317,94 +317,94 @@ namespace SharpCompress.Compressors.PPMd.H Coder.AriDecNormalize(); do { - orderFall++; - minContext.Address = minContext.getSuffix(); // =MinContext->Suffix; - if (minContext.Address <= SubAlloc.PText || minContext.Address > SubAlloc.HeapEnd) + _orderFall++; + _minContext.Address = _minContext.GetSuffix(); // =MinContext->Suffix; + if (_minContext.Address <= SubAlloc.PText || _minContext.Address > SubAlloc.HeapEnd) { return (-1); } } - while (minContext.NumStats == numMasked); - if (!minContext.decodeSymbol2(this)) + while (_minContext.NumStats == _numMasked); + if (!_minContext.DecodeSymbol2(this)) { return (-1); } Coder.Decode(); } - int Symbol = FoundState.Symbol; - if ((orderFall == 0) && FoundState.GetSuccessor() > SubAlloc.PText) + int symbol = FoundState.Symbol; + if ((_orderFall == 0) && FoundState.GetSuccessor() > SubAlloc.PText) { // MinContext=MaxContext=FoundState->Successor; int addr = FoundState.GetSuccessor(); - minContext.Address = addr; - maxContext.Address = addr; + _minContext.Address = addr; + _maxContext.Address = addr; } else { - updateModel(); + UpdateModel(); //this.foundState.Address=foundState.Address);//TODO just 4 debugging - if (escCount == 0) + if (_escCount == 0) { - clearMask(); + ClearMask(); } } Coder.AriDecNormalize(); // ARI_DEC_NORMALIZE(Coder.code,Coder.low,Coder.range,Coder.UnpackRead); - return (Symbol); + return (symbol); } - public virtual SEE2Context[][] getSEE2Cont() + public virtual See2Context[][] GetSee2Cont() { - return SEE2Cont; + return _see2Cont; } - public virtual void incEscCount(int dEscCount) + public virtual void IncEscCount(int dEscCount) { EscCount = EscCount + dEscCount; } - public virtual void incRunLength(int dRunLength) + public virtual void IncRunLength(int dRunLength) { RunLength = RunLength + dRunLength; } - public virtual int[] getHB2Flag() + public virtual int[] GetHb2Flag() { - return HB2Flag; + return _hb2Flag; } - public virtual int[] getNS2BSIndx() + public virtual int[] GetNs2BsIndx() { - return NS2BSIndx; + return _ns2BsIndx; } - public virtual int[] getNS2Indx() + public virtual int[] GetNs2Indx() { - return NS2Indx; + return _ns2Indx; } - private int createSuccessors(bool Skip, State p1) + private int CreateSuccessors(bool skip, State p1) { //State upState = tempState1.Initialize(null); - StateRef upState = tempStateRef2; - State tempState = tempState1.Initialize(Heap); + StateRef upState = _tempStateRef2; + State tempState = _tempState1.Initialize(Heap); // PPM_CONTEXT* pc=MinContext, * UpBranch=FoundState->Successor; - PPMContext pc = tempPPMContext1.Initialize(Heap); - pc.Address = minContext.Address; - PPMContext upBranch = tempPPMContext2.Initialize(Heap); + PpmContext pc = _tempPpmContext1.Initialize(Heap); + pc.Address = _minContext.Address; + PpmContext upBranch = _tempPpmContext2.Initialize(Heap); upBranch.Address = FoundState.GetSuccessor(); // STATE * p, * ps[MAX_O], ** pps=ps; - State p = tempState2.Initialize(Heap); + State p = _tempState2.Initialize(Heap); int pps = 0; bool noLoop = false; - if (!Skip) + if (!skip) { - ps[pps++] = FoundState.Address; // *pps++ = FoundState; - if (pc.getSuffix() == 0) + _ps[pps++] = FoundState.Address; // *pps++ = FoundState; + if (pc.GetSuffix() == 0) { noLoop = true; } @@ -415,14 +415,14 @@ namespace SharpCompress.Compressors.PPMd.H if (p1.Address != 0) { p.Address = p1.Address; - pc.Address = pc.getSuffix(); // =pc->Suffix; + pc.Address = pc.GetSuffix(); // =pc->Suffix; loopEntry = true; } do { if (!loopEntry) { - pc.Address = pc.getSuffix(); // pc=pc->Suffix; + pc.Address = pc.GetSuffix(); // pc=pc->Suffix; if (pc.NumStats != 1) { p.Address = pc.FreqData.GetStats(); // p=pc->U.Stats @@ -437,7 +437,7 @@ namespace SharpCompress.Compressors.PPMd.H } else { - p.Address = pc.getOneState().Address; // p=&(pc->OneState); + p.Address = pc.GetOneState().Address; // p=&(pc->OneState); } } // LOOP_ENTRY: loopEntry = false; @@ -446,9 +446,9 @@ namespace SharpCompress.Compressors.PPMd.H pc.Address = p.GetSuccessor(); // =p->Successor; break; } - ps[pps++] = p.Address; + _ps[pps++] = p.Address; } - while (pc.getSuffix() != 0); + while (pc.GetSuffix() != 0); } // NO_LOOP: if (pps == 0) { @@ -482,13 +482,13 @@ namespace SharpCompress.Compressors.PPMd.H } else { - upState.Freq = pc.getOneState().Freq; // UpState.Freq=pc->OneState.Freq; + upState.Freq = pc.GetOneState().Freq; // UpState.Freq=pc->OneState.Freq; } do { // pc = pc->createChild(this,*--pps,UpState); - tempState.Address = ps[--pps]; - pc.Address = pc.createChild(this, tempState, upState); + tempState.Address = _ps[--pps]; + pc.Address = pc.CreateChild(this, tempState, upState); if (pc.Address == 0) { return 0; @@ -498,26 +498,26 @@ namespace SharpCompress.Compressors.PPMd.H return pc.Address; } - private void updateModelRestart() + private void UpdateModelRestart() { - restartModelRare(); - escCount = 0; + RestartModelRare(); + _escCount = 0; } - private void updateModel() + private void UpdateModel() { //System.out.println("ModelPPM.updateModel()"); // STATE fs = *FoundState, *p = NULL; - StateRef fs = tempStateRef1; + StateRef fs = _tempStateRef1; fs.Values = FoundState; - State p = tempState3.Initialize(Heap); - State tempState = tempState4.Initialize(Heap); + State p = _tempState3.Initialize(Heap); + State tempState = _tempState4.Initialize(Heap); - PPMContext pc = tempPPMContext3.Initialize(Heap); - PPMContext successor = tempPPMContext4.Initialize(Heap); + PpmContext pc = _tempPpmContext3.Initialize(Heap); + PpmContext successor = _tempPpmContext4.Initialize(Heap); int ns1, ns, cf, sf, s0; - pc.Address = minContext.getSuffix(); + pc.Address = _minContext.GetSuffix(); if (fs.Freq < MAX_FREQ / 4 && pc.Address != 0) { if (pc.NumStats != 1) @@ -530,10 +530,10 @@ namespace SharpCompress.Compressors.PPMd.H p.IncrementAddress(); } while (p.Symbol != fs.Symbol); - tempState.Address = p.Address - State.Size; + tempState.Address = p.Address - State.SIZE; if (p.Freq >= tempState.Freq) { - State.PPMDSwap(p, tempState); + State.PpmdSwap(p, tempState); p.DecrementAddress(); } } @@ -545,31 +545,31 @@ namespace SharpCompress.Compressors.PPMd.H } else { - p.Address = pc.getOneState().Address; + p.Address = pc.GetOneState().Address; if (p.Freq < 32) { p.IncrementFreq(1); } } } - if (orderFall == 0) + if (_orderFall == 0) { - FoundState.SetSuccessor(createSuccessors(true, p)); - minContext.Address = FoundState.GetSuccessor(); - maxContext.Address = FoundState.GetSuccessor(); - if (minContext.Address == 0) + FoundState.SetSuccessor(CreateSuccessors(true, p)); + _minContext.Address = FoundState.GetSuccessor(); + _maxContext.Address = FoundState.GetSuccessor(); + if (_minContext.Address == 0) { - updateModelRestart(); + UpdateModelRestart(); return; } return; } SubAlloc.Heap[SubAlloc.PText] = (byte)fs.Symbol; - SubAlloc.incPText(); + SubAlloc.IncPText(); successor.Address = SubAlloc.PText; if (SubAlloc.PText >= SubAlloc.FakeUnitsStart) { - updateModelRestart(); + UpdateModelRestart(); return; } @@ -579,43 +579,43 @@ namespace SharpCompress.Compressors.PPMd.H { if (fs.GetSuccessor() <= SubAlloc.PText) { - fs.SetSuccessor(createSuccessors(false, p)); + fs.SetSuccessor(CreateSuccessors(false, p)); if (fs.GetSuccessor() == 0) { - updateModelRestart(); + UpdateModelRestart(); return; } } - if (--orderFall == 0) + if (--_orderFall == 0) { successor.Address = fs.GetSuccessor(); - if (maxContext.Address != minContext.Address) + if (_maxContext.Address != _minContext.Address) { - SubAlloc.decPText(1); + SubAlloc.DecPText(1); } } } else { FoundState.SetSuccessor(successor.Address); - fs.SetSuccessor(minContext); + fs.SetSuccessor(_minContext); } // // Debug // subAlloc.dumpHeap(); - ns = minContext.NumStats; - s0 = minContext.FreqData.SummFreq - (ns) - (fs.Freq - 1); - for (pc.Address = maxContext.Address; pc.Address != minContext.Address; pc.Address = pc.getSuffix()) + ns = _minContext.NumStats; + s0 = _minContext.FreqData.SummFreq - (ns) - (fs.Freq - 1); + for (pc.Address = _maxContext.Address; pc.Address != _minContext.Address; pc.Address = pc.GetSuffix()) { if ((ns1 = pc.NumStats) != 1) { if ((ns1 & 1) == 0) { //System.out.println(ns1); - pc.FreqData.SetStats(SubAlloc.expandUnits(pc.FreqData.GetStats(), Utility.URShift(ns1, 1))); + pc.FreqData.SetStats(SubAlloc.ExpandUnits(pc.FreqData.GetStats(), Utility.URShift(ns1, 1))); if (pc.FreqData.GetStats() == 0) { - updateModelRestart(); + UpdateModelRestart(); return; } } @@ -630,13 +630,13 @@ namespace SharpCompress.Compressors.PPMd.H } else { - p.Address = SubAlloc.allocUnits(1); + p.Address = SubAlloc.AllocUnits(1); if (p.Address == 0) { - updateModelRestart(); + UpdateModelRestart(); return; } - p.SetValues(pc.getOneState()); + p.SetValues(pc.GetOneState()); pc.FreqData.SetStats(p); if (p.Freq < MAX_FREQ / 4 - 1) { @@ -646,7 +646,7 @@ namespace SharpCompress.Compressors.PPMd.H { p.Freq = MAX_FREQ - 4; } - pc.FreqData.SummFreq = (p.Freq + initEsc + (ns > 3 ? 1 : 0)); + pc.FreqData.SummFreq = (p.Freq + _initEsc + (ns > 3 ? 1 : 0)); } cf = 2 * fs.Freq * (pc.FreqData.SummFreq + 6); sf = s0 + pc.FreqData.SummFreq; @@ -660,7 +660,7 @@ namespace SharpCompress.Compressors.PPMd.H cf = 4 + (cf >= 9 * sf ? 1 : 0) + (cf >= 12 * sf ? 1 : 0) + (cf >= 15 * sf ? 1 : 0); pc.FreqData.IncrementSummFreq(cf); } - p.Address = pc.FreqData.GetStats() + ns1 * State.Size; + p.Address = pc.FreqData.GetStats() + ns1 * State.SIZE; p.SetSuccessor(successor); p.Symbol = fs.Symbol; p.Freq = cf; @@ -668,8 +668,8 @@ namespace SharpCompress.Compressors.PPMd.H } int address = fs.GetSuccessor(); - maxContext.Address = address; - minContext.Address = address; + _maxContext.Address = address; + _minContext.Address = address; //TODO-----debug // int pos = minContext.getFreqData().getStats(); @@ -686,21 +686,21 @@ namespace SharpCompress.Compressors.PPMd.H StringBuilder buffer = new StringBuilder(); buffer.Append("ModelPPM["); buffer.Append("\n numMasked="); - buffer.Append(numMasked); + buffer.Append(_numMasked); buffer.Append("\n initEsc="); - buffer.Append(initEsc); + buffer.Append(_initEsc); buffer.Append("\n orderFall="); - buffer.Append(orderFall); + buffer.Append(_orderFall); buffer.Append("\n maxOrder="); - buffer.Append(maxOrder); + buffer.Append(_maxOrder); buffer.Append("\n runLength="); - buffer.Append(runLength); + buffer.Append(_runLength); buffer.Append("\n initRL="); - buffer.Append(initRL); + buffer.Append(_initRl); buffer.Append("\n escCount="); - buffer.Append(escCount); + buffer.Append(_escCount); buffer.Append("\n prevSuccess="); - buffer.Append(prevSuccess); + buffer.Append(_prevSuccess); buffer.Append("\n foundState="); buffer.Append(FoundState); buffer.Append("\n coder="); @@ -716,7 +716,7 @@ namespace SharpCompress.Compressors.PPMd.H // subAlloc.dumpHeap(); // } - internal bool decodeInit(Stream stream, int maxOrder, int maxMemory) + internal bool DecodeInit(Stream stream, int maxOrder, int maxMemory) { if (stream != null) { @@ -725,61 +725,61 @@ namespace SharpCompress.Compressors.PPMd.H if (maxOrder == 1) { - SubAlloc.stopSubAllocator(); + SubAlloc.StopSubAllocator(); return (false); } - SubAlloc.startSubAllocator(maxMemory); - minContext = new PPMContext(Heap); + SubAlloc.StartSubAllocator(maxMemory); + _minContext = new PpmContext(Heap); //medContext = new PPMContext(Heap); - maxContext = new PPMContext(Heap); + _maxContext = new PpmContext(Heap); FoundState = new State(Heap); - dummySEE2Cont = new SEE2Context(); + _dummySee2Cont = new See2Context(); for (int i = 0; i < 25; i++) { for (int j = 0; j < 16; j++) { - SEE2Cont[i][j] = new SEE2Context(); + _see2Cont[i][j] = new See2Context(); } } - startModelRare(maxOrder); + StartModelRare(maxOrder); - return (minContext.Address != 0); + return (_minContext.Address != 0); } - internal void nextContext() + internal void NextContext() { int addr = FoundState.GetSuccessor(); - if (orderFall == 0 && addr > SubAlloc.PText) + if (_orderFall == 0 && addr > SubAlloc.PText) { - minContext.Address = addr; - maxContext.Address = addr; + _minContext.Address = addr; + _maxContext.Address = addr; } else { - updateModel(); + UpdateModel(); } } - public int decodeChar(Decoder decoder) + public int DecodeChar(Decoder decoder) { - if (minContext.NumStats != 1) + if (_minContext.NumStats != 1) { - State s = tempState1.Initialize(Heap); - s.Address = minContext.FreqData.GetStats(); + State s = _tempState1.Initialize(Heap); + s.Address = _minContext.FreqData.GetStats(); int i; int count, hiCnt; - if ((count = (int)decoder.GetThreshold((uint)minContext.FreqData.SummFreq)) < (hiCnt = s.Freq)) + if ((count = (int)decoder.GetThreshold((uint)_minContext.FreqData.SummFreq)) < (hiCnt = s.Freq)) { byte symbol; decoder.Decode(0, (uint)s.Freq); symbol = (byte)s.Symbol; - minContext.update1_0(this, s.Address); - nextContext(); + _minContext.update1_0(this, s.Address); + NextContext(); return symbol; } - prevSuccess = 0; - i = minContext.NumStats - 1; + _prevSuccess = 0; + i = _minContext.NumStats - 1; do { s.IncrementAddress(); @@ -788,113 +788,113 @@ namespace SharpCompress.Compressors.PPMd.H byte symbol; decoder.Decode((uint)(hiCnt - s.Freq), (uint)s.Freq); symbol = (byte)s.Symbol; - minContext.update1(this, s.Address); - nextContext(); + _minContext.Update1(this, s.Address); + NextContext(); return symbol; } } while (--i > 0); - if (count >= minContext.FreqData.SummFreq) + if (count >= _minContext.FreqData.SummFreq) { return -2; } - hiBitsFlag = HB2Flag[FoundState.Symbol]; - decoder.Decode((uint)hiCnt, (uint)(minContext.FreqData.SummFreq - hiCnt)); + _hiBitsFlag = _hb2Flag[FoundState.Symbol]; + decoder.Decode((uint)hiCnt, (uint)(_minContext.FreqData.SummFreq - hiCnt)); for (i = 0; i < 256; i++) { - charMask[i] = -1; + _charMask[i] = -1; } - charMask[s.Symbol] = 0; - i = minContext.NumStats - 1; + _charMask[s.Symbol] = 0; + i = _minContext.NumStats - 1; do { s.DecrementAddress(); - charMask[s.Symbol] = 0; + _charMask[s.Symbol] = 0; } while (--i > 0); } else { - State rs = tempState1.Initialize(Heap); - rs.Address = minContext.getOneState().Address; - hiBitsFlag = getHB2Flag()[FoundState.Symbol]; + State rs = _tempState1.Initialize(Heap); + rs.Address = _minContext.GetOneState().Address; + _hiBitsFlag = GetHb2Flag()[FoundState.Symbol]; int off1 = rs.Freq - 1; - int off2 = minContext.getArrayIndex(this, rs); - int bs = binSumm[off1][off2]; + int off2 = _minContext.GetArrayIndex(this, rs); + int bs = _binSumm[off1][off2]; if (decoder.DecodeBit((uint)bs, 14) == 0) { byte symbol; - binSumm[off1][off2] = (bs + INTERVAL - minContext.getMean(bs, PERIOD_BITS, 2)) & 0xFFFF; + _binSumm[off1][off2] = (bs + INTERVAL - _minContext.GetMean(bs, PERIOD_BITS, 2)) & 0xFFFF; FoundState.Address = rs.Address; symbol = (byte)rs.Symbol; rs.IncrementFreq((rs.Freq < 128) ? 1 : 0); - prevSuccess = 1; - incRunLength(1); - nextContext(); + _prevSuccess = 1; + IncRunLength(1); + NextContext(); return symbol; } - bs = (bs - minContext.getMean(bs, PERIOD_BITS, 2)) & 0xFFFF; - binSumm[off1][off2] = bs; - initEsc = PPMContext.ExpEscape[Utility.URShift(bs, 10)]; + bs = (bs - _minContext.GetMean(bs, PERIOD_BITS, 2)) & 0xFFFF; + _binSumm[off1][off2] = bs; + _initEsc = PpmContext.EXP_ESCAPE[Utility.URShift(bs, 10)]; int i; for (i = 0; i < 256; i++) { - charMask[i] = -1; + _charMask[i] = -1; } - charMask[rs.Symbol] = 0; - prevSuccess = 0; + _charMask[rs.Symbol] = 0; + _prevSuccess = 0; } for (;;) { - State s = tempState1.Initialize(Heap); + State s = _tempState1.Initialize(Heap); int i; int freqSum, count, hiCnt; - SEE2Context see; - int num, numMasked = minContext.NumStats; + See2Context see; + int num, numMasked = _minContext.NumStats; do { - orderFall++; - minContext.Address = minContext.getSuffix(); - if (minContext.Address <= SubAlloc.PText || minContext.Address > SubAlloc.HeapEnd) + _orderFall++; + _minContext.Address = _minContext.GetSuffix(); + if (_minContext.Address <= SubAlloc.PText || _minContext.Address > SubAlloc.HeapEnd) { return -1; } } - while (minContext.NumStats == numMasked); + while (_minContext.NumStats == numMasked); hiCnt = 0; - s.Address = minContext.FreqData.GetStats(); + s.Address = _minContext.FreqData.GetStats(); i = 0; - num = minContext.NumStats - numMasked; + num = _minContext.NumStats - numMasked; do { - int k = charMask[s.Symbol]; + int k = _charMask[s.Symbol]; hiCnt += s.Freq & k; - minContext.ps[i] = s.Address; + _minContext._ps[i] = s.Address; s.IncrementAddress(); i -= k; } while (i != num); - see = minContext.makeEscFreq(this, numMasked, out freqSum); + see = _minContext.MakeEscFreq(this, numMasked, out freqSum); freqSum += hiCnt; count = (int)decoder.GetThreshold((uint)freqSum); if (count < hiCnt) { byte symbol; - State ps = tempState2.Initialize(Heap); - for (hiCnt = 0, i = 0, ps.Address = minContext.ps[i]; + State ps = _tempState2.Initialize(Heap); + for (hiCnt = 0, i = 0, ps.Address = _minContext._ps[i]; (hiCnt += ps.Freq) <= count; - i++, ps.Address = minContext.ps[i]) + i++, ps.Address = _minContext._ps[i]) { ; } s.Address = ps.Address; decoder.Decode((uint)(hiCnt - s.Freq), (uint)s.Freq); - see.update(); + see.Update(); symbol = (byte)s.Symbol; - minContext.update2(this, s.Address); - updateModel(); + _minContext.Update2(this, s.Address); + UpdateModel(); return symbol; } if (count >= freqSum) @@ -905,8 +905,8 @@ namespace SharpCompress.Compressors.PPMd.H see.Summ = see.Summ + freqSum; do { - s.Address = minContext.ps[--i]; - charMask[s.Symbol] = 0; + s.Address = _minContext._ps[--i]; + _charMask[s.Symbol] = 0; } while (i != 0); } diff --git a/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs b/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs index e39fa69c..7be31003 100644 --- a/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs +++ b/src/SharpCompress/Compressors/PPMd/H/PPMContext.cs @@ -4,15 +4,15 @@ using SharpCompress.Converters; namespace SharpCompress.Compressors.PPMd.H { - internal class PPMContext : Pointer + internal class PpmContext : Pointer { internal FreqData FreqData { - get => freqData; + get => _freqData; set { - freqData.SummFreq = value.SummFreq; - freqData.SetStats(value.GetStats()); + _freqData.SummFreq = value.SummFreq; + _freqData.SetStats(value.GetStats()); } } @@ -22,14 +22,14 @@ namespace SharpCompress.Compressors.PPMd.H { if (Memory != null) { - numStats = DataConverter.LittleEndian.GetInt16(Memory, Address) & 0xffff; + _numStats = DataConverter.LittleEndian.GetInt16(Memory, Address) & 0xffff; } - return numStats; + return _numStats; } set { - numStats = value & 0xffff; + _numStats = value & 0xffff; if (Memory != null) { DataConverter.LittleEndian.PutBytes(Memory, Address, (short)value); @@ -39,89 +39,89 @@ namespace SharpCompress.Compressors.PPMd.H //UPGRADE_NOTE: Final was removed from the declaration of 'unionSize '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" //UPGRADE_NOTE: The initialization of 'unionSize' was moved to static method 'SharpCompress.Unpack.PPM.PPMContext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1005'" - private static readonly int unionSize; + private static readonly int UNION_SIZE; //UPGRADE_NOTE: Final was removed from the declaration of 'size '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public static readonly int size = 2 + unionSize + 4; // 12 + public static readonly int SIZE = 2 + UNION_SIZE + 4; // 12 // ushort NumStats; - private int numStats; // determines if feqData or onstate is used + private int _numStats; // determines if feqData or onstate is used // (1==onestate) //UPGRADE_NOTE: Final was removed from the declaration of 'freqData '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly FreqData freqData; // -\ + private readonly FreqData _freqData; // -\ // |-> union //UPGRADE_NOTE: Final was removed from the declaration of 'oneState '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State oneState; // -/ + private readonly State _oneState; // -/ - private int suffix; // pointer ppmcontext + private int _suffix; // pointer ppmcontext //UPGRADE_NOTE: Final was removed from the declaration of 'ExpEscape'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - public static readonly int[] ExpEscape = {25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2}; + public static readonly int[] EXP_ESCAPE = {25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2}; // Temp fields //UPGRADE_NOTE: Final was removed from the declaration of 'tempState1 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State tempState1 = new State(null); + private readonly State _tempState1 = new State(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState2 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State tempState2 = new State(null); + private readonly State _tempState2 = new State(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState3 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State tempState3 = new State(null); + private readonly State _tempState3 = new State(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State tempState4 = new State(null); + private readonly State _tempState4 = new State(null); //UPGRADE_NOTE: Final was removed from the declaration of 'tempState5 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly State tempState5 = new State(null); - private PPMContext tempPPMContext; + private readonly State _tempState5 = new State(null); + private PpmContext _tempPpmContext; //UPGRADE_NOTE: Final was removed from the declaration of 'ps '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - internal int[] ps = new int[256]; + internal int[] _ps = new int[256]; - public PPMContext(byte[] Memory) - : base(Memory) + public PpmContext(byte[] memory) + : base(memory) { - oneState = new State(Memory); - freqData = new FreqData(Memory); + _oneState = new State(memory); + _freqData = new FreqData(memory); } - internal PPMContext Initialize(byte[] mem) + internal PpmContext Initialize(byte[] mem) { - oneState.Initialize(mem); - freqData.Initialize(mem); - return base.Initialize(mem); + _oneState.Initialize(mem); + _freqData.Initialize(mem); + return base.Initialize(mem); } - internal State getOneState() + internal State GetOneState() { - return oneState; + return _oneState; } - internal void setOneState(StateRef oneState) + internal void SetOneState(StateRef oneState) { - this.oneState.SetValues(oneState); + _oneState.SetValues(oneState); } - internal int getSuffix() + internal int GetSuffix() { if (Memory != null) { - suffix = DataConverter.LittleEndian.GetInt32(Memory, Address + 8); + _suffix = DataConverter.LittleEndian.GetInt32(Memory, Address + 8); } - return suffix; + return _suffix; } - internal void setSuffix(PPMContext suffix) + internal void SetSuffix(PpmContext suffix) { - setSuffix(suffix.Address); + SetSuffix(suffix.Address); } - internal void setSuffix(int suffix) + internal void SetSuffix(int suffix) { - this.suffix = suffix; + _suffix = suffix; if (Memory != null) { DataConverter.LittleEndian.PutBytes(Memory, Address + 8, suffix); @@ -134,62 +134,62 @@ namespace SharpCompress.Compressors.PPMd.H set { base.Address = value; - oneState.Address = value + 2; - freqData.Address = value + 2; + _oneState.Address = value + 2; + _freqData.Address = value + 2; } } - private PPMContext getTempPPMContext(byte[] Memory) + private PpmContext GetTempPpmContext(byte[] memory) { - if (tempPPMContext == null) + if (_tempPpmContext == null) { - tempPPMContext = new PPMContext(null); + _tempPpmContext = new PpmContext(null); } - return tempPPMContext.Initialize(Memory); + return _tempPpmContext.Initialize(memory); } - internal int createChild(ModelPPM model, State pStats, StateRef firstState) + internal int CreateChild(ModelPpm model, State pStats, StateRef firstState) { - PPMContext pc = getTempPPMContext(model.SubAlloc.Heap); - pc.Address = model.SubAlloc.allocContext(); + PpmContext pc = GetTempPpmContext(model.SubAlloc.Heap); + pc.Address = model.SubAlloc.AllocContext(); if (pc != null) { pc.NumStats = 1; - pc.setOneState(firstState); - pc.setSuffix(this); + pc.SetOneState(firstState); + pc.SetSuffix(this); pStats.SetSuccessor(pc); } return pc.Address; } - internal void rescale(ModelPPM model) + internal void Rescale(ModelPpm model) { - int OldNS = NumStats, i = NumStats - 1, Adder, EscFreq; + int oldNs = NumStats, i = NumStats - 1, adder, escFreq; // STATE* p1, * p; State p1 = new State(model.Heap); State p = new State(model.Heap); State temp = new State(model.Heap); - for (p.Address = model.FoundState.Address; p.Address != freqData.GetStats(); p.DecrementAddress()) + for (p.Address = model.FoundState.Address; p.Address != _freqData.GetStats(); p.DecrementAddress()) { - temp.Address = p.Address - State.Size; - State.PPMDSwap(p, temp); + temp.Address = p.Address - State.SIZE; + State.PpmdSwap(p, temp); } - temp.Address = freqData.GetStats(); + temp.Address = _freqData.GetStats(); temp.IncrementFreq(4); - freqData.IncrementSummFreq(4); - EscFreq = freqData.SummFreq - p.Freq; - Adder = (model.OrderFall != 0) ? 1 : 0; - p.Freq = Utility.URShift((p.Freq + Adder), 1); - freqData.SummFreq = p.Freq; + _freqData.IncrementSummFreq(4); + escFreq = _freqData.SummFreq - p.Freq; + adder = (model.OrderFall != 0) ? 1 : 0; + p.Freq = Utility.URShift((p.Freq + adder), 1); + _freqData.SummFreq = p.Freq; do { p.IncrementAddress(); - EscFreq -= p.Freq; - p.Freq = Utility.URShift((p.Freq + Adder), 1); - freqData.IncrementSummFreq(p.Freq); - temp.Address = p.Address - State.Size; + escFreq -= p.Freq; + p.Freq = Utility.URShift((p.Freq + adder), 1); + _freqData.IncrementSummFreq(p.Freq); + temp.Address = p.Address - State.SIZE; if (p.Freq > temp.Freq) { p1.Address = p.Address; @@ -200,12 +200,12 @@ namespace SharpCompress.Compressors.PPMd.H do { // p1[0]=p1[-1]; - temp2.Address = p1.Address - State.Size; + temp2.Address = p1.Address - State.SIZE; p1.SetValues(temp2); p1.DecrementAddress(); - temp3.Address = p1.Address - State.Size; + temp3.Address = p1.Address - State.SIZE; } - while (p1.Address != freqData.GetStats() && tmp.Freq > temp3.Freq); + while (p1.Address != _freqData.GetStats() && tmp.Freq > temp3.Freq); p1.SetValues(tmp); } } @@ -218,12 +218,12 @@ namespace SharpCompress.Compressors.PPMd.H p.DecrementAddress(); } while (p.Freq == 0); - EscFreq += i; + escFreq += i; NumStats = NumStats - i; if (NumStats == 1) { StateRef tmp = new StateRef(); - temp.Address = freqData.GetStats(); + temp.Address = _freqData.GetStats(); tmp.Values = temp; // STATE tmp=*U.Stats; @@ -231,68 +231,68 @@ namespace SharpCompress.Compressors.PPMd.H { // tmp.Freq-=(tmp.Freq >> 1) tmp.DecrementFreq(Utility.URShift(tmp.Freq, 1)); - EscFreq = Utility.URShift(EscFreq, 1); + escFreq = Utility.URShift(escFreq, 1); } - while (EscFreq > 1); - model.SubAlloc.freeUnits(freqData.GetStats(), Utility.URShift((OldNS + 1), 1)); - oneState.SetValues(tmp); - model.FoundState.Address = oneState.Address; + while (escFreq > 1); + model.SubAlloc.FreeUnits(_freqData.GetStats(), Utility.URShift((oldNs + 1), 1)); + _oneState.SetValues(tmp); + model.FoundState.Address = _oneState.Address; return; } } - EscFreq -= Utility.URShift(EscFreq, 1); - freqData.IncrementSummFreq(EscFreq); - int n0 = Utility.URShift((OldNS + 1), 1), n1 = Utility.URShift((NumStats + 1), 1); + escFreq -= Utility.URShift(escFreq, 1); + _freqData.IncrementSummFreq(escFreq); + int n0 = Utility.URShift((oldNs + 1), 1), n1 = Utility.URShift((NumStats + 1), 1); if (n0 != n1) { - freqData.SetStats(model.SubAlloc.shrinkUnits(freqData.GetStats(), n0, n1)); + _freqData.SetStats(model.SubAlloc.ShrinkUnits(_freqData.GetStats(), n0, n1)); } - model.FoundState.Address = freqData.GetStats(); + model.FoundState.Address = _freqData.GetStats(); } - internal int getArrayIndex(ModelPPM Model, State rs) + internal int GetArrayIndex(ModelPpm model, State rs) { - PPMContext tempSuffix = getTempPPMContext(Model.SubAlloc.Heap); - tempSuffix.Address = getSuffix(); + PpmContext tempSuffix = GetTempPpmContext(model.SubAlloc.Heap); + tempSuffix.Address = GetSuffix(); int ret = 0; - ret += Model.PrevSuccess; - ret += Model.getNS2BSIndx()[tempSuffix.NumStats - 1]; - ret += Model.HiBitsFlag + 2 * Model.getHB2Flag()[rs.Symbol]; - ret += ((Utility.URShift(Model.RunLength, 26)) & 0x20); + ret += model.PrevSuccess; + ret += model.GetNs2BsIndx()[tempSuffix.NumStats - 1]; + ret += model.HiBitsFlag + 2 * model.GetHb2Flag()[rs.Symbol]; + ret += ((Utility.URShift(model.RunLength, 26)) & 0x20); return ret; } - internal int getMean(int summ, int shift, int round) + internal int GetMean(int summ, int shift, int round) { return (Utility.URShift((summ + (1 << (shift - round))), (shift))); } - internal void decodeBinSymbol(ModelPPM model) + internal void DecodeBinSymbol(ModelPpm model) { - State rs = tempState1.Initialize(model.Heap); - rs.Address = oneState.Address; // State& - model.HiBitsFlag = model.getHB2Flag()[model.FoundState.Symbol]; + State rs = _tempState1.Initialize(model.Heap); + rs.Address = _oneState.Address; // State& + model.HiBitsFlag = model.GetHb2Flag()[model.FoundState.Symbol]; int off1 = rs.Freq - 1; - int off2 = getArrayIndex(model, rs); + int off2 = GetArrayIndex(model, rs); int bs = model.BinSumm[off1][off2]; - if (model.Coder.GetCurrentShiftCount(ModelPPM.TOT_BITS) < bs) + if (model.Coder.GetCurrentShiftCount(ModelPpm.TOT_BITS) < bs) { model.FoundState.Address = rs.Address; rs.IncrementFreq((rs.Freq < 128) ? 1 : 0); model.Coder.SubRange.LowCount = 0; model.Coder.SubRange.HighCount = bs; - bs = ((bs + ModelPPM.INTERVAL - getMean(bs, ModelPPM.PERIOD_BITS, 2)) & 0xffff); + bs = ((bs + ModelPpm.INTERVAL - GetMean(bs, ModelPpm.PERIOD_BITS, 2)) & 0xffff); model.BinSumm[off1][off2] = bs; model.PrevSuccess = 1; - model.incRunLength(1); + model.IncRunLength(1); } else { model.Coder.SubRange.LowCount = bs; - bs = (bs - getMean(bs, ModelPPM.PERIOD_BITS, 2)) & 0xFFFF; + bs = (bs - GetMean(bs, ModelPpm.PERIOD_BITS, 2)) & 0xFFFF; model.BinSumm[off1][off2] = bs; - model.Coder.SubRange.HighCount = ModelPPM.BIN_SCALE; - model.InitEsc = ExpEscape[Utility.URShift(bs, 10)]; + model.Coder.SubRange.HighCount = ModelPpm.BIN_SCALE; + model.InitEsc = EXP_ESCAPE[Utility.URShift(bs, 10)]; model.NumMasked = 1; model.CharMask[rs.Symbol] = model.EscCount; model.PrevSuccess = 0; @@ -317,50 +317,50 @@ namespace SharpCompress.Compressors.PPMd.H // state2.Address=p2); // } - internal void update1(ModelPPM model, int p) + internal void Update1(ModelPpm model, int p) { model.FoundState.Address = p; model.FoundState.IncrementFreq(4); - freqData.IncrementSummFreq(4); - State p0 = tempState3.Initialize(model.Heap); - State p1 = tempState4.Initialize(model.Heap); + _freqData.IncrementSummFreq(4); + State p0 = _tempState3.Initialize(model.Heap); + State p1 = _tempState4.Initialize(model.Heap); p0.Address = p; - p1.Address = p - State.Size; + p1.Address = p - State.SIZE; if (p0.Freq > p1.Freq) { - State.PPMDSwap(p0, p1); + State.PpmdSwap(p0, p1); model.FoundState.Address = p1.Address; - if (p1.Freq > ModelPPM.MAX_FREQ) + if (p1.Freq > ModelPpm.MAX_FREQ) { - rescale(model); + Rescale(model); } } } - internal void update1_0(ModelPPM model, int p) + internal void update1_0(ModelPpm model, int p) { model.FoundState.Address = p; - model.PrevSuccess = 2 * model.FoundState.Freq > freqData.SummFreq ? 1 : 0; - model.incRunLength(model.PrevSuccess); - freqData.IncrementSummFreq(4); + model.PrevSuccess = 2 * model.FoundState.Freq > _freqData.SummFreq ? 1 : 0; + model.IncRunLength(model.PrevSuccess); + _freqData.IncrementSummFreq(4); model.FoundState.IncrementFreq(4); - if (model.FoundState.Freq > ModelPPM.MAX_FREQ) + if (model.FoundState.Freq > ModelPpm.MAX_FREQ) { - rescale(model); + Rescale(model); } } - internal bool decodeSymbol2(ModelPPM model) + internal bool DecodeSymbol2(ModelPpm model) { long count; int hiCnt, i = NumStats - model.NumMasked; - SEE2Context psee2c = makeEscFreq2(model, i); + See2Context psee2C = MakeEscFreq2(model, i); RangeCoder coder = model.Coder; // STATE* ps[256], ** pps=ps, * p=U.Stats-1; - State p = tempState1.Initialize(model.Heap); - State temp = tempState2.Initialize(model.Heap); - p.Address = freqData.GetStats() - State.Size; + State p = _tempState1.Initialize(model.Heap); + State temp = _tempState2.Initialize(model.Heap); + p.Address = _freqData.GetStats() - State.SIZE; int pps = 0; hiCnt = 0; @@ -372,28 +372,28 @@ namespace SharpCompress.Compressors.PPMd.H } while (model.CharMask[p.Symbol] == model.EscCount); hiCnt += p.Freq; - ps[pps++] = p.Address; + _ps[pps++] = p.Address; } while (--i != 0); - coder.SubRange.incScale(hiCnt); + coder.SubRange.IncScale(hiCnt); count = coder.CurrentCount; if (count >= coder.SubRange.Scale) { return false; } pps = 0; - p.Address = ps[pps]; + p.Address = _ps[pps]; if (count < hiCnt) { hiCnt = 0; while ((hiCnt += p.Freq) <= count) { - p.Address = ps[++pps]; // p=*++pps; + p.Address = _ps[++pps]; // p=*++pps; } coder.SubRange.HighCount = hiCnt; coder.SubRange.LowCount = hiCnt - p.Freq; - psee2c.update(); - update2(model, p.Address); + psee2C.Update(); + Update2(model, p.Address); } else { @@ -403,106 +403,106 @@ namespace SharpCompress.Compressors.PPMd.H pps--; do { - temp.Address = ps[++pps]; // (*++pps) + temp.Address = _ps[++pps]; // (*++pps) model.CharMask[temp.Symbol] = model.EscCount; } while (--i != 0); - psee2c.incSumm((int)coder.SubRange.Scale); + psee2C.IncSumm((int)coder.SubRange.Scale); model.NumMasked = NumStats; } return (true); } - internal void update2(ModelPPM model, int p) + internal void Update2(ModelPpm model, int p) { - State temp = tempState5.Initialize(model.Heap); + State temp = _tempState5.Initialize(model.Heap); temp.Address = p; model.FoundState.Address = p; model.FoundState.IncrementFreq(4); - freqData.IncrementSummFreq(4); - if (temp.Freq > ModelPPM.MAX_FREQ) + _freqData.IncrementSummFreq(4); + if (temp.Freq > ModelPpm.MAX_FREQ) { - rescale(model); + Rescale(model); } - model.incEscCount(1); - model.RunLength = model.InitRL; + model.IncEscCount(1); + model.RunLength = model.InitRl; } - private SEE2Context makeEscFreq2(ModelPPM model, int Diff) + private See2Context MakeEscFreq2(ModelPpm model, int diff) { - SEE2Context psee2c; + See2Context psee2C; int numStats = NumStats; if (numStats != 256) { - PPMContext suff = getTempPPMContext(model.Heap); - suff.Address = getSuffix(); - int idx1 = model.getNS2Indx()[Diff - 1]; + PpmContext suff = GetTempPpmContext(model.Heap); + suff.Address = GetSuffix(); + int idx1 = model.GetNs2Indx()[diff - 1]; int idx2 = 0; - idx2 += ((Diff < suff.NumStats - numStats) ? 1 : 0); - idx2 += 2 * ((freqData.SummFreq < 11 * numStats) ? 1 : 0); - idx2 += 4 * ((model.NumMasked > Diff) ? 1 : 0); + idx2 += ((diff < suff.NumStats - numStats) ? 1 : 0); + idx2 += 2 * ((_freqData.SummFreq < 11 * numStats) ? 1 : 0); + idx2 += 4 * ((model.NumMasked > diff) ? 1 : 0); idx2 += model.HiBitsFlag; - psee2c = model.getSEE2Cont()[idx1][idx2]; - model.Coder.SubRange.Scale = psee2c.Mean; + psee2C = model.GetSee2Cont()[idx1][idx2]; + model.Coder.SubRange.Scale = psee2C.Mean; } else { - psee2c = model.DummySEE2Cont; + psee2C = model.DummySee2Cont; model.Coder.SubRange.Scale = 1; } - return psee2c; + return psee2C; } - internal SEE2Context makeEscFreq(ModelPPM model, int numMasked, out int escFreq) + internal See2Context MakeEscFreq(ModelPpm model, int numMasked, out int escFreq) { - SEE2Context psee2c; + See2Context psee2C; int numStats = NumStats; int nonMasked = numStats - numMasked; if (numStats != 256) { - PPMContext suff = getTempPPMContext(model.Heap); - suff.Address = getSuffix(); - int idx1 = model.getNS2Indx()[nonMasked - 1]; + PpmContext suff = GetTempPpmContext(model.Heap); + suff.Address = GetSuffix(); + int idx1 = model.GetNs2Indx()[nonMasked - 1]; int idx2 = 0; idx2 += ((nonMasked < suff.NumStats - numStats) ? 1 : 0); - idx2 += 2 * ((freqData.SummFreq < 11 * numStats) ? 1 : 0); + idx2 += 2 * ((_freqData.SummFreq < 11 * numStats) ? 1 : 0); idx2 += 4 * ((numMasked > nonMasked) ? 1 : 0); idx2 += model.HiBitsFlag; - psee2c = model.getSEE2Cont()[idx1][idx2]; - escFreq = psee2c.Mean; + psee2C = model.GetSee2Cont()[idx1][idx2]; + escFreq = psee2C.Mean; } else { - psee2c = model.DummySEE2Cont; + psee2C = model.DummySee2Cont; escFreq = 1; } - return psee2c; + return psee2C; } - internal bool decodeSymbol1(ModelPPM model) + internal bool DecodeSymbol1(ModelPpm model) { RangeCoder coder = model.Coder; - coder.SubRange.Scale = freqData.SummFreq; + coder.SubRange.Scale = _freqData.SummFreq; State p = new State(model.Heap); - p.Address = freqData.GetStats(); - int i, HiCnt; + p.Address = _freqData.GetStats(); + int i, hiCnt; long count = coder.CurrentCount; if (count >= coder.SubRange.Scale) { return false; } - if (count < (HiCnt = p.Freq)) + if (count < (hiCnt = p.Freq)) { - coder.SubRange.HighCount = HiCnt; - model.PrevSuccess = (2 * HiCnt > coder.SubRange.Scale) ? 1 : 0; - model.incRunLength(model.PrevSuccess); - HiCnt += 4; + coder.SubRange.HighCount = hiCnt; + model.PrevSuccess = (2 * hiCnt > coder.SubRange.Scale) ? 1 : 0; + model.IncRunLength(model.PrevSuccess); + hiCnt += 4; model.FoundState.Address = p.Address; - model.FoundState.Freq = HiCnt; - freqData.IncrementSummFreq(4); - if (HiCnt > ModelPPM.MAX_FREQ) + model.FoundState.Freq = hiCnt; + _freqData.IncrementSummFreq(4); + if (hiCnt > ModelPpm.MAX_FREQ) { - rescale(model); + Rescale(model); } coder.SubRange.LowCount = 0; return true; @@ -514,12 +514,12 @@ namespace SharpCompress.Compressors.PPMd.H model.PrevSuccess = 0; int numStats = NumStats; i = numStats - 1; - while ((HiCnt += p.IncrementAddress().Freq) <= count) + while ((hiCnt += p.IncrementAddress().Freq) <= count) { if (--i == 0) { - model.HiBitsFlag = model.getHB2Flag()[model.FoundState.Symbol]; - coder.SubRange.LowCount = HiCnt; + model.HiBitsFlag = model.GetHb2Flag()[model.FoundState.Symbol]; + coder.SubRange.LowCount = hiCnt; model.CharMask[p.Symbol] = model.EscCount; model.NumMasked = numStats; i = numStats - 1; @@ -533,9 +533,9 @@ namespace SharpCompress.Compressors.PPMd.H return (true); } } - coder.SubRange.LowCount = HiCnt - p.Freq; - coder.SubRange.HighCount = HiCnt; - update1(model, p.Address); + coder.SubRange.LowCount = hiCnt - p.Freq; + coder.SubRange.HighCount = hiCnt; + Update1(model, p.Address); return (true); } @@ -546,22 +546,22 @@ namespace SharpCompress.Compressors.PPMd.H buffer.Append("\n Address="); buffer.Append(Address); buffer.Append("\n size="); - buffer.Append(size); + buffer.Append(SIZE); buffer.Append("\n numStats="); buffer.Append(NumStats); buffer.Append("\n Suffix="); - buffer.Append(getSuffix()); + buffer.Append(GetSuffix()); buffer.Append("\n freqData="); - buffer.Append(freqData); + buffer.Append(_freqData); buffer.Append("\n oneState="); - buffer.Append(oneState); + buffer.Append(_oneState); buffer.Append("\n]"); return buffer.ToString(); } - static PPMContext() + static PpmContext() { - unionSize = Math.Max(FreqData.Size, State.Size); + UNION_SIZE = Math.Max(FreqData.SIZE, State.SIZE); } } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/PPMd/H/RangeCoder.cs b/src/SharpCompress/Compressors/PPMd/H/RangeCoder.cs index 3b5177fc..e1946852 100644 --- a/src/SharpCompress/Compressors/PPMd/H/RangeCoder.cs +++ b/src/SharpCompress/Compressors/PPMd/H/RangeCoder.cs @@ -9,22 +9,22 @@ namespace SharpCompress.Compressors.PPMd.H { internal const int TOP = 1 << 24; internal const int BOT = 1 << 15; - internal const long UintMask = 0xFFFFffffL; + internal const long UINT_MASK = 0xFFFFffffL; // uint low, code, range; - private long low, code, range; - private readonly IRarUnpack unpackRead; - private readonly Stream stream; + private long _low, _code, _range; + private readonly IRarUnpack _unpackRead; + private readonly Stream _stream; internal RangeCoder(IRarUnpack unpackRead) { - this.unpackRead = unpackRead; + _unpackRead = unpackRead; Init(); } internal RangeCoder(Stream stream) { - this.stream = stream; + _stream = stream; Init(); } @@ -32,11 +32,11 @@ namespace SharpCompress.Compressors.PPMd.H { SubRange = new SubRange(); - low = code = 0L; - range = 0xFFFFffffL; + _low = _code = 0L; + _range = 0xFFFFffffL; for (int i = 0; i < 4; i++) { - code = ((code << 8) | Char) & UintMask; + _code = ((_code << 8) | Char) & UINT_MASK; } } @@ -44,8 +44,8 @@ namespace SharpCompress.Compressors.PPMd.H { get { - range = (range / SubRange.Scale) & UintMask; - return (int)((code - low) / (range)); + _range = (_range / SubRange.Scale) & UINT_MASK; + return (int)((_code - _low) / (_range)); } } @@ -53,13 +53,13 @@ namespace SharpCompress.Compressors.PPMd.H { get { - if (unpackRead != null) + if (_unpackRead != null) { - return (unpackRead.Char); + return (_unpackRead.Char); } - if (stream != null) + if (_stream != null) { - return stream.ReadByte(); + return _stream.ReadByte(); } return -1; } @@ -67,16 +67,16 @@ namespace SharpCompress.Compressors.PPMd.H internal SubRange SubRange { get; private set; } - internal long GetCurrentShiftCount(int SHIFT) + internal long GetCurrentShiftCount(int shift) { - range = Utility.URShift(range, SHIFT); - return ((code - low) / (range)) & UintMask; + _range = Utility.URShift(_range, shift); + return ((_code - _low) / (_range)) & UINT_MASK; } internal void Decode() { - low = (low + (range * SubRange.LowCount)) & UintMask; - range = (range * (SubRange.HighCount - SubRange.LowCount)) & UintMask; + _low = (_low + (_range * SubRange.LowCount)) & UINT_MASK; + _range = (_range * (SubRange.HighCount - SubRange.LowCount)) & UINT_MASK; } internal void AriDecNormalize() @@ -90,16 +90,16 @@ namespace SharpCompress.Compressors.PPMd.H // Rewrote for clarity bool c2 = false; - while ((low ^ (low + range)) < TOP || (c2 = range < BOT)) + while ((_low ^ (_low + _range)) < TOP || (c2 = _range < BOT)) { if (c2) { - range = (-low & (BOT - 1)) & UintMask; + _range = (-_low & (BOT - 1)) & UINT_MASK; c2 = false; } - code = ((code << 8) | Char) & UintMask; - range = (range << 8) & UintMask; - low = (low << 8) & UintMask; + _code = ((_code << 8) | Char) & UINT_MASK; + _range = (_range << 8) & UINT_MASK; + _low = (_low << 8) & UINT_MASK; } } @@ -109,11 +109,11 @@ namespace SharpCompress.Compressors.PPMd.H StringBuilder buffer = new StringBuilder(); buffer.Append("RangeCoder["); buffer.Append("\n low="); - buffer.Append(low); + buffer.Append(_low); buffer.Append("\n code="); - buffer.Append(code); + buffer.Append(_code); buffer.Append("\n range="); - buffer.Append(range); + buffer.Append(_range); buffer.Append("\n subrange="); buffer.Append(SubRange); buffer.Append("]"); @@ -124,18 +124,18 @@ namespace SharpCompress.Compressors.PPMd.H internal class SubRange { // uint LowCount, HighCount, scale; - private long lowCount, highCount, scale; + private long _lowCount, _highCount, _scale; - internal void incScale(int dScale) + internal void IncScale(int dScale) { Scale = Scale + dScale; } - internal long HighCount { get => highCount; set => highCount = value & RangeCoder.UintMask; } + internal long HighCount { get => _highCount; set => _highCount = value & RangeCoder.UINT_MASK; } - internal long LowCount { get => lowCount & RangeCoder.UintMask; set => lowCount = value & RangeCoder.UintMask; } + internal long LowCount { get => _lowCount & RangeCoder.UINT_MASK; set => _lowCount = value & RangeCoder.UINT_MASK; } - internal long Scale { get => scale; set => scale = value & RangeCoder.UintMask; } + internal long Scale { get => _scale; set => _scale = value & RangeCoder.UINT_MASK; } // Debug public override String ToString() @@ -143,11 +143,11 @@ namespace SharpCompress.Compressors.PPMd.H StringBuilder buffer = new StringBuilder(); buffer.Append("SubRange["); buffer.Append("\n lowCount="); - buffer.Append(lowCount); + buffer.Append(_lowCount); buffer.Append("\n highCount="); - buffer.Append(highCount); + buffer.Append(_highCount); buffer.Append("\n scale="); - buffer.Append(scale); + buffer.Append(_scale); buffer.Append("]"); return buffer.ToString(); } diff --git a/src/SharpCompress/Compressors/PPMd/H/RarMemBlock.cs b/src/SharpCompress/Compressors/PPMd/H/RarMemBlock.cs index 6f0eda1c..78e6824c 100644 --- a/src/SharpCompress/Compressors/PPMd/H/RarMemBlock.cs +++ b/src/SharpCompress/Compressors/PPMd/H/RarMemBlock.cs @@ -4,14 +4,14 @@ namespace SharpCompress.Compressors.PPMd.H { internal class RarMemBlock : Pointer { - public const int size = 12; + public const int SIZE = 12; - private int stamp, NU; + private int _stamp, _nu; - private int next, prev; // Pointer RarMemBlock + private int _next, _prev; // Pointer RarMemBlock - public RarMemBlock(byte[] Memory) - : base(Memory) + public RarMemBlock(byte[] memory) + : base(memory) { } @@ -21,14 +21,14 @@ namespace SharpCompress.Compressors.PPMd.H { if (Memory != null) { - stamp = DataConverter.LittleEndian.GetInt16(Memory, Address) & 0xffff; + _stamp = DataConverter.LittleEndian.GetInt16(Memory, Address) & 0xffff; } - return stamp; + return _stamp; } set { - stamp = value; + _stamp = value; if (Memory != null) { DataConverter.LittleEndian.PutBytes(Memory, Address, (short)value); @@ -63,9 +63,9 @@ namespace SharpCompress.Compressors.PPMd.H { if (Memory != null) { - next = DataConverter.LittleEndian.GetInt32(Memory, Address + 4); + _next = DataConverter.LittleEndian.GetInt32(Memory, Address + 4); } - return next; + return _next; } internal void SetNext(RarMemBlock next) @@ -75,25 +75,25 @@ namespace SharpCompress.Compressors.PPMd.H internal void SetNext(int next) { - this.next = next; + _next = next; if (Memory != null) { DataConverter.LittleEndian.PutBytes(Memory, Address + 4, next); } } - internal int GetNU() + internal int GetNu() { if (Memory != null) { - NU = DataConverter.LittleEndian.GetInt16(Memory, Address + 2) & 0xffff; + _nu = DataConverter.LittleEndian.GetInt16(Memory, Address + 2) & 0xffff; } - return NU; + return _nu; } - internal void SetNU(int nu) + internal void SetNu(int nu) { - NU = nu & 0xffff; + _nu = nu & 0xffff; if (Memory != null) { DataConverter.LittleEndian.PutBytes(Memory, Address + 2, (short)nu); @@ -104,9 +104,9 @@ namespace SharpCompress.Compressors.PPMd.H { if (Memory != null) { - prev = DataConverter.LittleEndian.GetInt32(Memory, Address + 8); + _prev = DataConverter.LittleEndian.GetInt32(Memory, Address + 8); } - return prev; + return _prev; } internal void SetPrev(RarMemBlock prev) @@ -116,7 +116,7 @@ namespace SharpCompress.Compressors.PPMd.H internal void SetPrev(int prev) { - this.prev = prev; + _prev = prev; if (Memory != null) { DataConverter.LittleEndian.PutBytes(Memory, Address + 8, prev); diff --git a/src/SharpCompress/Compressors/PPMd/H/RarNode.cs b/src/SharpCompress/Compressors/PPMd/H/RarNode.cs index 69af3f35..3cbf2894 100644 --- a/src/SharpCompress/Compressors/PPMd/H/RarNode.cs +++ b/src/SharpCompress/Compressors/PPMd/H/RarNode.cs @@ -5,12 +5,12 @@ namespace SharpCompress.Compressors.PPMd.H { internal class RarNode : Pointer { - private int next; //rarnode pointer + private int _next; //rarnode pointer - public const int size = 4; + public const int SIZE = 4; - public RarNode(byte[] Memory) - : base(Memory) + public RarNode(byte[] memory) + : base(memory) { } @@ -18,9 +18,9 @@ namespace SharpCompress.Compressors.PPMd.H { if (Memory != null) { - next = DataConverter.LittleEndian.GetInt32(Memory, Address); + _next = DataConverter.LittleEndian.GetInt32(Memory, Address); } - return next; + return _next; } internal void SetNext(RarNode next) @@ -30,7 +30,7 @@ namespace SharpCompress.Compressors.PPMd.H internal void SetNext(int next) { - this.next = next; + _next = next; if (Memory != null) { DataConverter.LittleEndian.PutBytes(Memory, Address, next); @@ -44,7 +44,7 @@ namespace SharpCompress.Compressors.PPMd.H buffer.Append("\n Address="); buffer.Append(Address); buffer.Append("\n size="); - buffer.Append(size); + buffer.Append(SIZE); buffer.Append("\n next="); buffer.Append(GetNext()); buffer.Append("\n]"); diff --git a/src/SharpCompress/Compressors/PPMd/H/SEE2Context.cs b/src/SharpCompress/Compressors/PPMd/H/SEE2Context.cs index 66344c00..c97f0836 100644 --- a/src/SharpCompress/Compressors/PPMd/H/SEE2Context.cs +++ b/src/SharpCompress/Compressors/PPMd/H/SEE2Context.cs @@ -3,55 +3,55 @@ using System.Text; namespace SharpCompress.Compressors.PPMd.H { - internal class SEE2Context + internal class See2Context { public virtual int Mean { get { - int retVal = Utility.URShift(summ, shift); - summ -= retVal; + int retVal = Utility.URShift(_summ, _shift); + _summ -= retVal; return retVal + ((retVal == 0) ? 1 : 0); } } - public virtual int Count { get => count; set => count = value & 0xff; } + public virtual int Count { get => _count; set => _count = value & 0xff; } - public virtual int Shift { get => shift; set => shift = value & 0xff; } + public virtual int Shift { get => _shift; set => _shift = value & 0xff; } - public virtual int Summ { get => summ; set => summ = value & 0xffff; } + public virtual int Summ { get => _summ; set => _summ = value & 0xffff; } - public const int size = 4; + public const int SIZE = 4; // ushort Summ; - private int summ; + private int _summ; // byte Shift; - private int shift; + private int _shift; // byte Count; - private int count; + private int _count; public void Initialize(int initVal) { - shift = (ModelPPM.PERIOD_BITS - 4) & 0xff; - summ = (initVal << shift) & 0xffff; - count = 4; + _shift = (ModelPpm.PERIOD_BITS - 4) & 0xff; + _summ = (initVal << _shift) & 0xffff; + _count = 4; } - public virtual void update() + public virtual void Update() { - if (shift < ModelPPM.PERIOD_BITS && --count == 0) + if (_shift < ModelPpm.PERIOD_BITS && --_count == 0) { - summ += summ; - count = (3 << shift++); + _summ += _summ; + _count = (3 << _shift++); } - summ &= 0xffff; - count &= 0xff; - shift &= 0xff; + _summ &= 0xffff; + _count &= 0xff; + _shift &= 0xff; } - public virtual void incSumm(int dSumm) + public virtual void IncSumm(int dSumm) { Summ = Summ + dSumm; } @@ -61,13 +61,13 @@ namespace SharpCompress.Compressors.PPMd.H StringBuilder buffer = new StringBuilder(); buffer.Append("SEE2Context["); buffer.Append("\n size="); - buffer.Append(size); + buffer.Append(SIZE); buffer.Append("\n summ="); - buffer.Append(summ); + buffer.Append(_summ); buffer.Append("\n shift="); - buffer.Append(shift); + buffer.Append(_shift); buffer.Append("\n count="); - buffer.Append(count); + buffer.Append(_count); buffer.Append("\n]"); return buffer.ToString(); } diff --git a/src/SharpCompress/Compressors/PPMd/H/State.cs b/src/SharpCompress/Compressors/PPMd/H/State.cs index 2771e5b3..8062da14 100644 --- a/src/SharpCompress/Compressors/PPMd/H/State.cs +++ b/src/SharpCompress/Compressors/PPMd/H/State.cs @@ -6,10 +6,10 @@ namespace SharpCompress.Compressors.PPMd.H { internal class State : Pointer { - internal const int Size = 6; + internal const int SIZE = 6; - internal State(byte[] Memory) - : base(Memory) + internal State(byte[] memory) + : base(memory) { } @@ -32,7 +32,7 @@ namespace SharpCompress.Compressors.PPMd.H return DataConverter.LittleEndian.GetInt32(Memory, Address + 2); } - internal void SetSuccessor(PPMContext successor) + internal void SetSuccessor(PpmContext successor) { SetSuccessor(successor.Address); } @@ -51,25 +51,25 @@ namespace SharpCompress.Compressors.PPMd.H internal void SetValues(State ptr) { - Array.Copy(ptr.Memory, ptr.Address, Memory, Address, Size); + Array.Copy(ptr.Memory, ptr.Address, Memory, Address, SIZE); } internal State DecrementAddress() { - Address = Address - Size; + Address = Address - SIZE; return this; } internal State IncrementAddress() { - Address = Address + Size; + Address = Address + SIZE; return this; } - internal static void PPMDSwap(State ptr1, State ptr2) + internal static void PpmdSwap(State ptr1, State ptr2) { byte[] mem1 = ptr1.Memory, mem2 = ptr2.Memory; - for (int i = 0, pos1 = ptr1.Address, pos2 = ptr2.Address; i < Size; i++, pos1++, pos2++) + for (int i = 0, pos1 = ptr1.Address, pos2 = ptr2.Address; i < SIZE; i++, pos1++, pos2++) { byte temp = mem1[pos1]; mem1[pos1] = mem2[pos2]; @@ -84,7 +84,7 @@ namespace SharpCompress.Compressors.PPMd.H buffer.Append("\n Address="); buffer.Append(Address); buffer.Append("\n size="); - buffer.Append(Size); + buffer.Append(SIZE); buffer.Append("\n symbol="); buffer.Append(Symbol); buffer.Append("\n freq="); diff --git a/src/SharpCompress/Compressors/PPMd/H/StateRef.cs b/src/SharpCompress/Compressors/PPMd/H/StateRef.cs index 3499bdd8..f1e8e90f 100644 --- a/src/SharpCompress/Compressors/PPMd/H/StateRef.cs +++ b/src/SharpCompress/Compressors/PPMd/H/StateRef.cs @@ -5,15 +5,15 @@ namespace SharpCompress.Compressors.PPMd.H { internal class StateRef { - private int symbol; + private int _symbol; - private int freq; + private int _freq; - private int successor; // pointer ppmcontext + private int _successor; // pointer ppmcontext - internal int Symbol { get => symbol; set => symbol = value & 0xff; } + internal int Symbol { get => _symbol; set => _symbol = value & 0xff; } - internal int Freq { get => freq; set => freq = value & 0xff; } + internal int Freq { get => _freq; set => _freq = value & 0xff; } internal State Values { @@ -27,27 +27,27 @@ namespace SharpCompress.Compressors.PPMd.H public virtual void IncrementFreq(int dFreq) { - freq = (freq + dFreq) & 0xff; + _freq = (_freq + dFreq) & 0xff; } public virtual void DecrementFreq(int dFreq) { - freq = (freq - dFreq) & 0xff; + _freq = (_freq - dFreq) & 0xff; } public virtual int GetSuccessor() { - return successor; + return _successor; } - public virtual void SetSuccessor(PPMContext successor) + public virtual void SetSuccessor(PpmContext successor) { SetSuccessor(successor.Address); } public virtual void SetSuccessor(int successor) { - this.successor = successor; + _successor = successor; } public override String ToString() diff --git a/src/SharpCompress/Compressors/PPMd/H/SubAllocator.cs b/src/SharpCompress/Compressors/PPMd/H/SubAllocator.cs index 0918e9a9..c49eed97 100644 --- a/src/SharpCompress/Compressors/PPMd/H/SubAllocator.cs +++ b/src/SharpCompress/Compressors/PPMd/H/SubAllocator.cs @@ -5,15 +5,15 @@ namespace SharpCompress.Compressors.PPMd.H { internal class SubAllocator { - public virtual int FakeUnitsStart { get => fakeUnitsStart; set => fakeUnitsStart = value; } + public virtual int FakeUnitsStart { get => _fakeUnitsStart; set => _fakeUnitsStart = value; } - public virtual int HeapEnd => heapEnd; + public virtual int HeapEnd => _heapEnd; - public virtual int PText { get => pText; set => pText = value; } + public virtual int PText { get => _pText; set => _pText = value; } - public virtual int UnitsStart { get => unitsStart; set => unitsStart = value; } + public virtual int UnitsStart { get => _unitsStart; set => _unitsStart = value; } - public virtual byte[] Heap => heap; + public virtual byte[] Heap => _heap; //UPGRADE_NOTE: Final was removed from the declaration of 'N4 '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" public const int N1 = 4; @@ -30,123 +30,123 @@ namespace SharpCompress.Compressors.PPMd.H public const int FIXED_UNIT_SIZE = 12; - private int subAllocatorSize; + private int _subAllocatorSize; // byte Indx2Units[N_INDEXES], Units2Indx[128], GlueCount; - private readonly int[] indx2Units = new int[N_INDEXES]; - private readonly int[] units2Indx = new int[128]; - private int glueCount; + private readonly int[] _indx2Units = new int[N_INDEXES]; + private readonly int[] _units2Indx = new int[128]; + private int _glueCount; // byte *HeapStart,*LoUnit, *HiUnit; - private int heapStart, loUnit, hiUnit; + private int _heapStart, _loUnit, _hiUnit; //UPGRADE_NOTE: Final was removed from the declaration of 'freeList '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" - private readonly RarNode[] freeList = new RarNode[N_INDEXES]; + private readonly RarNode[] _freeList = new RarNode[N_INDEXES]; // byte *pText, *UnitsStart,*HeapEnd,*FakeUnitsStart; - private int pText, unitsStart, heapEnd, fakeUnitsStart; + private int _pText, _unitsStart, _heapEnd, _fakeUnitsStart; - private byte[] heap; + private byte[] _heap; - private int freeListPos; + private int _freeListPos; - private int tempMemBlockPos; + private int _tempMemBlockPos; // Temp fields - private RarNode tempRarNode; - private RarMemBlock tempRarMemBlock1; - private RarMemBlock tempRarMemBlock2; - private RarMemBlock tempRarMemBlock3; + private RarNode _tempRarNode; + private RarMemBlock _tempRarMemBlock1; + private RarMemBlock _tempRarMemBlock2; + private RarMemBlock _tempRarMemBlock3; public SubAllocator() { - clean(); + Clean(); } - public virtual void clean() + public virtual void Clean() { - subAllocatorSize = 0; + _subAllocatorSize = 0; } - private void insertNode(int p, int indx) + private void InsertNode(int p, int indx) { - RarNode temp = tempRarNode; + RarNode temp = _tempRarNode; temp.Address = p; - temp.SetNext(freeList[indx].GetNext()); - freeList[indx].SetNext(temp); + temp.SetNext(_freeList[indx].GetNext()); + _freeList[indx].SetNext(temp); } - public virtual void incPText() + public virtual void IncPText() { - pText++; + _pText++; } - private int removeNode(int indx) + private int RemoveNode(int indx) { - int retVal = freeList[indx].GetNext(); - RarNode temp = tempRarNode; + int retVal = _freeList[indx].GetNext(); + RarNode temp = _tempRarNode; temp.Address = retVal; - freeList[indx].SetNext(temp.GetNext()); + _freeList[indx].SetNext(temp.GetNext()); return retVal; } - private int U2B(int NU) + private int U2B(int nu) { - return UNIT_SIZE * NU; + return UNIT_SIZE * nu; } /* memblockptr */ - private int MBPtr(int BasePtr, int Items) + private int MbPtr(int basePtr, int items) { - return (BasePtr + U2B(Items)); + return (basePtr + U2B(items)); } - private void splitBlock(int pv, int oldIndx, int newIndx) + private void SplitBlock(int pv, int oldIndx, int newIndx) { - int i, uDiff = indx2Units[oldIndx] - indx2Units[newIndx]; - int p = pv + U2B(indx2Units[newIndx]); - if (indx2Units[i = units2Indx[uDiff - 1]] != uDiff) + int i, uDiff = _indx2Units[oldIndx] - _indx2Units[newIndx]; + int p = pv + U2B(_indx2Units[newIndx]); + if (_indx2Units[i = _units2Indx[uDiff - 1]] != uDiff) { - insertNode(p, --i); - p += U2B(i = indx2Units[i]); + InsertNode(p, --i); + p += U2B(i = _indx2Units[i]); uDiff -= i; } - insertNode(p, units2Indx[uDiff - 1]); + InsertNode(p, _units2Indx[uDiff - 1]); } - public virtual void stopSubAllocator() + public virtual void StopSubAllocator() { - if (subAllocatorSize != 0) + if (_subAllocatorSize != 0) { - subAllocatorSize = 0; + _subAllocatorSize = 0; //ArrayFactory.BYTES_FACTORY.recycle(heap); - heap = null; - heapStart = 1; + _heap = null; + _heapStart = 1; // rarfree(HeapStart); // Free temp fields - tempRarNode = null; - tempRarMemBlock1 = null; - tempRarMemBlock2 = null; - tempRarMemBlock3 = null; + _tempRarNode = null; + _tempRarMemBlock1 = null; + _tempRarMemBlock2 = null; + _tempRarMemBlock3 = null; } } public virtual int GetAllocatedMemory() { - return subAllocatorSize; + return _subAllocatorSize; } - public virtual bool startSubAllocator(int SASize) + public virtual bool StartSubAllocator(int saSize) { - int t = SASize; - if (subAllocatorSize == t) + int t = saSize; + if (_subAllocatorSize == t) { return true; } - stopSubAllocator(); + StopSubAllocator(); int allocSize = t / FIXED_UNIT_SIZE * UNIT_SIZE + UNIT_SIZE; // adding space for freelist (needed for poiters) @@ -154,56 +154,56 @@ namespace SharpCompress.Compressors.PPMd.H int realAllocSize = 1 + allocSize + 4 * N_INDEXES; // adding space for an additional memblock - tempMemBlockPos = realAllocSize; - realAllocSize += RarMemBlock.size; + _tempMemBlockPos = realAllocSize; + realAllocSize += RarMemBlock.SIZE; - heap = new byte[realAllocSize]; - heapStart = 1; - heapEnd = heapStart + allocSize - UNIT_SIZE; - subAllocatorSize = t; + _heap = new byte[realAllocSize]; + _heapStart = 1; + _heapEnd = _heapStart + allocSize - UNIT_SIZE; + _subAllocatorSize = t; // Bug fixed - freeListPos = heapStart + allocSize; + _freeListPos = _heapStart + allocSize; //UPGRADE_ISSUE: The following fragment of code could not be parsed and was not converted. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1156'" //assert(realAllocSize - tempMemBlockPos == RarMemBlock.size): realAllocSize //+ + tempMemBlockPos + + RarMemBlock.size; // Init freeList - for (int i = 0, pos = freeListPos; i < freeList.Length; i++, pos += RarNode.size) + for (int i = 0, pos = _freeListPos; i < _freeList.Length; i++, pos += RarNode.SIZE) { - freeList[i] = new RarNode(heap); - freeList[i].Address = pos; + _freeList[i] = new RarNode(_heap); + _freeList[i].Address = pos; } // Init temp fields - tempRarNode = new RarNode(heap); - tempRarMemBlock1 = new RarMemBlock(heap); - tempRarMemBlock2 = new RarMemBlock(heap); - tempRarMemBlock3 = new RarMemBlock(heap); + _tempRarNode = new RarNode(_heap); + _tempRarMemBlock1 = new RarMemBlock(_heap); + _tempRarMemBlock2 = new RarMemBlock(_heap); + _tempRarMemBlock3 = new RarMemBlock(_heap); return true; } - private void glueFreeBlocks() + private void GlueFreeBlocks() { - RarMemBlock s0 = tempRarMemBlock1; - s0.Address = tempMemBlockPos; - RarMemBlock p = tempRarMemBlock2; - RarMemBlock p1 = tempRarMemBlock3; + RarMemBlock s0 = _tempRarMemBlock1; + s0.Address = _tempMemBlockPos; + RarMemBlock p = _tempRarMemBlock2; + RarMemBlock p1 = _tempRarMemBlock3; int i, k, sz; - if (loUnit != hiUnit) + if (_loUnit != _hiUnit) { - heap[loUnit] = 0; + _heap[_loUnit] = 0; } for (i = 0, s0.SetPrev(s0), s0.SetNext(s0); i < N_INDEXES; i++) { - while (freeList[i].GetNext() != 0) + while (_freeList[i].GetNext() != 0) { - p.Address = removeNode(i); // =(RAR_MEM_BLK*)RemoveNode(i); + p.Address = RemoveNode(i); // =(RAR_MEM_BLK*)RemoveNode(i); p.InsertAt(s0); // p->insertAt(&s0); p.Stamp = 0xFFFF; // p->Stamp=0xFFFF; - p.SetNU(indx2Units[i]); // p->NU=Indx2Units[i]; + p.SetNu(_indx2Units[i]); // p->NU=Indx2Units[i]; } } for (p.Address = s0.GetNext(); p.Address != s0.Address; p.Address = p.GetNext()) @@ -211,12 +211,12 @@ namespace SharpCompress.Compressors.PPMd.H // while ((p1=MBPtr(p,p->NU))->Stamp == 0xFFFF && int(p->NU)+p1->NU // < 0x10000) // Bug fixed - p1.Address = MBPtr(p.Address, p.GetNU()); - while (p1.Stamp == 0xFFFF && p.GetNU() + p1.GetNU() < 0x10000) + p1.Address = MbPtr(p.Address, p.GetNu()); + while (p1.Stamp == 0xFFFF && p.GetNu() + p1.GetNu() < 0x10000) { p1.Remove(); - p.SetNU(p.GetNU() + p1.GetNU()); // ->NU += p1->NU; - p1.Address = MBPtr(p.Address, p.GetNU()); + p.SetNu(p.GetNu() + p1.GetNu()); // ->NU += p1->NU; + p1.Address = MbPtr(p.Address, p.GetNu()); } } @@ -225,29 +225,29 @@ namespace SharpCompress.Compressors.PPMd.H p.Address = s0.GetNext(); while (p.Address != s0.Address) { - for (p.Remove(), sz = p.GetNU(); sz > 128; sz -= 128, p.Address = MBPtr(p.Address, 128)) + for (p.Remove(), sz = p.GetNu(); sz > 128; sz -= 128, p.Address = MbPtr(p.Address, 128)) { - insertNode(p.Address, N_INDEXES - 1); + InsertNode(p.Address, N_INDEXES - 1); } - if (indx2Units[i = units2Indx[sz - 1]] != sz) + if (_indx2Units[i = _units2Indx[sz - 1]] != sz) { - k = sz - indx2Units[--i]; - insertNode(MBPtr(p.Address, sz - k), k - 1); + k = sz - _indx2Units[--i]; + InsertNode(MbPtr(p.Address, sz - k), k - 1); } - insertNode(p.Address, i); + InsertNode(p.Address, i); p.Address = s0.GetNext(); } } - private int allocUnitsRare(int indx) + private int AllocUnitsRare(int indx) { - if (glueCount == 0) + if (_glueCount == 0) { - glueCount = 255; - glueFreeBlocks(); - if (freeList[indx].GetNext() != 0) + _glueCount = 255; + GlueFreeBlocks(); + if (_freeList[indx].GetNext() != 0) { - return removeNode(indx); + return RemoveNode(indx); } } int i = indx; @@ -255,151 +255,151 @@ namespace SharpCompress.Compressors.PPMd.H { if (++i == N_INDEXES) { - glueCount--; - i = U2B(indx2Units[indx]); - int j = FIXED_UNIT_SIZE * indx2Units[indx]; - if (fakeUnitsStart - pText > j) + _glueCount--; + i = U2B(_indx2Units[indx]); + int j = FIXED_UNIT_SIZE * _indx2Units[indx]; + if (_fakeUnitsStart - _pText > j) { - fakeUnitsStart -= j; - unitsStart -= i; - return unitsStart; + _fakeUnitsStart -= j; + _unitsStart -= i; + return _unitsStart; } return (0); } } - while (freeList[i].GetNext() == 0); - int retVal = removeNode(i); - splitBlock(retVal, i, indx); + while (_freeList[i].GetNext() == 0); + int retVal = RemoveNode(i); + SplitBlock(retVal, i, indx); return retVal; } - public virtual int allocUnits(int NU) + public virtual int AllocUnits(int nu) { - int indx = units2Indx[NU - 1]; - if (freeList[indx].GetNext() != 0) + int indx = _units2Indx[nu - 1]; + if (_freeList[indx].GetNext() != 0) { - return removeNode(indx); + return RemoveNode(indx); } - int retVal = loUnit; - loUnit += U2B(indx2Units[indx]); - if (loUnit <= hiUnit) + int retVal = _loUnit; + _loUnit += U2B(_indx2Units[indx]); + if (_loUnit <= _hiUnit) { return retVal; } - loUnit -= U2B(indx2Units[indx]); - return allocUnitsRare(indx); + _loUnit -= U2B(_indx2Units[indx]); + return AllocUnitsRare(indx); } - public virtual int allocContext() + public virtual int AllocContext() { - if (hiUnit != loUnit) + if (_hiUnit != _loUnit) { - return (hiUnit -= UNIT_SIZE); + return (_hiUnit -= UNIT_SIZE); } - if (freeList[0].GetNext() != 0) + if (_freeList[0].GetNext() != 0) { - return removeNode(0); + return RemoveNode(0); } - return allocUnitsRare(0); + return AllocUnitsRare(0); } - public virtual int expandUnits(int oldPtr, int OldNU) + public virtual int ExpandUnits(int oldPtr, int oldNu) { - int i0 = units2Indx[OldNU - 1]; - int i1 = units2Indx[OldNU - 1 + 1]; + int i0 = _units2Indx[oldNu - 1]; + int i1 = _units2Indx[oldNu - 1 + 1]; if (i0 == i1) { return oldPtr; } - int ptr = allocUnits(OldNU + 1); + int ptr = AllocUnits(oldNu + 1); if (ptr != 0) { // memcpy(ptr,OldPtr,U2B(OldNU)); - Array.Copy(heap, oldPtr, heap, ptr, U2B(OldNU)); - insertNode(oldPtr, i0); + Array.Copy(_heap, oldPtr, _heap, ptr, U2B(oldNu)); + InsertNode(oldPtr, i0); } return ptr; } - public virtual int shrinkUnits(int oldPtr, int oldNU, int newNU) + public virtual int ShrinkUnits(int oldPtr, int oldNu, int newNu) { // System.out.println("SubAllocator.shrinkUnits(" + OldPtr + ", " + // OldNU + ", " + NewNU + ")"); - int i0 = units2Indx[oldNU - 1]; - int i1 = units2Indx[newNU - 1]; + int i0 = _units2Indx[oldNu - 1]; + int i1 = _units2Indx[newNu - 1]; if (i0 == i1) { return oldPtr; } - if (freeList[i1].GetNext() != 0) + if (_freeList[i1].GetNext() != 0) { - int ptr = removeNode(i1); + int ptr = RemoveNode(i1); // memcpy(ptr,OldPtr,U2B(NewNU)); // for (int i = 0; i < U2B(NewNU); i++) { // heap[ptr + i] = heap[OldPtr + i]; // } - Array.Copy(heap, oldPtr, heap, ptr, U2B(newNU)); - insertNode(oldPtr, i0); + Array.Copy(_heap, oldPtr, _heap, ptr, U2B(newNu)); + InsertNode(oldPtr, i0); return ptr; } - splitBlock(oldPtr, i0, i1); + SplitBlock(oldPtr, i0, i1); return oldPtr; } - public virtual void freeUnits(int ptr, int OldNU) + public virtual void FreeUnits(int ptr, int oldNu) { - insertNode(ptr, units2Indx[OldNU - 1]); + InsertNode(ptr, _units2Indx[oldNu - 1]); } - public virtual void decPText(int dPText) + public virtual void DecPText(int dPText) { PText = PText - dPText; } - public virtual void initSubAllocator() + public virtual void InitSubAllocator() { int i, k; - Utility.Fill(heap, freeListPos, freeListPos + sizeOfFreeList(), (byte)0); + Utility.Fill(_heap, _freeListPos, _freeListPos + SizeOfFreeList(), (byte)0); - pText = heapStart; + _pText = _heapStart; - int size2 = FIXED_UNIT_SIZE * (subAllocatorSize / 8 / FIXED_UNIT_SIZE * 7); + int size2 = FIXED_UNIT_SIZE * (_subAllocatorSize / 8 / FIXED_UNIT_SIZE * 7); int realSize2 = size2 / FIXED_UNIT_SIZE * UNIT_SIZE; - int size1 = subAllocatorSize - size2; + int size1 = _subAllocatorSize - size2; int realSize1 = size1 / FIXED_UNIT_SIZE * UNIT_SIZE + size1 % FIXED_UNIT_SIZE; - hiUnit = heapStart + subAllocatorSize; - loUnit = unitsStart = heapStart + realSize1; - fakeUnitsStart = heapStart + size1; - hiUnit = loUnit + realSize2; + _hiUnit = _heapStart + _subAllocatorSize; + _loUnit = _unitsStart = _heapStart + realSize1; + _fakeUnitsStart = _heapStart + size1; + _hiUnit = _loUnit + realSize2; for (i = 0, k = 1; i < N1; i++, k += 1) { - indx2Units[i] = k & 0xff; + _indx2Units[i] = k & 0xff; } for (k++; i < N1 + N2; i++, k += 2) { - indx2Units[i] = k & 0xff; + _indx2Units[i] = k & 0xff; } for (k++; i < N1 + N2 + N3; i++, k += 3) { - indx2Units[i] = k & 0xff; + _indx2Units[i] = k & 0xff; } for (k++; i < (N1 + N2 + N3 + N4); i++, k += 4) { - indx2Units[i] = k & 0xff; + _indx2Units[i] = k & 0xff; } - for (glueCount = 0, k = 0, i = 0; k < 128; k++) + for (_glueCount = 0, k = 0, i = 0; k < 128; k++) { - i += ((indx2Units[i] < (k + 1)) ? 1 : 0); - units2Indx[k] = i & 0xff; + i += ((_indx2Units[i] < (k + 1)) ? 1 : 0); + _units2Indx[k] = i & 0xff; } } - private int sizeOfFreeList() + private int SizeOfFreeList() { - return freeList.Length * RarNode.size; + return _freeList.Length * RarNode.SIZE; } // Debug @@ -426,26 +426,26 @@ namespace SharpCompress.Compressors.PPMd.H StringBuilder buffer = new StringBuilder(); buffer.Append("SubAllocator["); buffer.Append("\n subAllocatorSize="); - buffer.Append(subAllocatorSize); + buffer.Append(_subAllocatorSize); buffer.Append("\n glueCount="); - buffer.Append(glueCount); + buffer.Append(_glueCount); buffer.Append("\n heapStart="); - buffer.Append(heapStart); + buffer.Append(_heapStart); buffer.Append("\n loUnit="); - buffer.Append(loUnit); + buffer.Append(_loUnit); buffer.Append("\n hiUnit="); - buffer.Append(hiUnit); + buffer.Append(_hiUnit); buffer.Append("\n pText="); - buffer.Append(pText); + buffer.Append(_pText); buffer.Append("\n unitsStart="); - buffer.Append(unitsStart); + buffer.Append(_unitsStart); buffer.Append("\n]"); return buffer.ToString(); } static SubAllocator() { - UNIT_SIZE = Math.Max(PPMContext.size, RarMemBlock.size); + UNIT_SIZE = Math.Max(PpmContext.SIZE, RarMemBlock.SIZE); } } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs b/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs index 5b23c422..1f8849aa 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Allocator.cs @@ -6,11 +6,11 @@ namespace SharpCompress.Compressors.PPMd.I1 /// of which also simply contain a single address value): internal class Allocator { - private const uint UnitSize = 12; - private const uint LocalOffset = 4; // reserve the first four bytes for Pointer.Zero - private const uint NodeOffset = LocalOffset + MemoryNode.Size; // reserve space for a single memory node + private const uint UNIT_SIZE = 12; + private const uint LOCAL_OFFSET = 4; // reserve the first four bytes for Pointer.Zero + private const uint NODE_OFFSET = LOCAL_OFFSET + MemoryNode.SIZE; // reserve space for a single memory node - private const uint HeapOffset = NodeOffset + IndexCount * MemoryNode.Size; + private const uint HEAP_OFFSET = NODE_OFFSET + INDEX_COUNT * MemoryNode.SIZE; // reserve space for the array of memory nodes @@ -18,21 +18,21 @@ namespace SharpCompress.Compressors.PPMd.I1 private const uint N2 = 4; private const uint N3 = 4; private const uint N4 = (128 + 3 - 1 * N1 - 2 * N2 - 3 * N3) / 4; - private const uint IndexCount = N1 + N2 + N3 + N4; + private const uint INDEX_COUNT = N1 + N2 + N3 + N4; - private static readonly byte[] indexToUnits; - private static readonly byte[] unitsToIndex; + private static readonly byte[] INDEX_TO_UNITS; + private static readonly byte[] UNITS_TO_INDEX; - public uint AllocatorSize; - public uint GlueCount; - public Pointer BaseUnit; - public Pointer LowUnit; - public Pointer HighUnit; - public Pointer Text; - public Pointer Heap; - public MemoryNode[] MemoryNodes; + public uint _allocatorSize; + public uint _glueCount; + public Pointer _baseUnit; + public Pointer _lowUnit; + public Pointer _highUnit; + public Pointer _text; + public Pointer _heap; + public MemoryNode[] _memoryNodes; - public byte[] Memory; + public byte[] _memory; /// /// Initializes static read-only arrays used by the . @@ -47,26 +47,26 @@ namespace SharpCompress.Compressors.PPMd.I1 uint index; uint unitCount; - indexToUnits = new byte[IndexCount]; + INDEX_TO_UNITS = new byte[INDEX_COUNT]; for (index = 0, unitCount = 1; index < N1; index++, unitCount += 1) { - indexToUnits[index] = (byte)unitCount; + INDEX_TO_UNITS[index] = (byte)unitCount; } for (unitCount++; index < N1 + N2; index++, unitCount += 2) { - indexToUnits[index] = (byte)unitCount; + INDEX_TO_UNITS[index] = (byte)unitCount; } for (unitCount++; index < N1 + N2 + N3; index++, unitCount += 3) { - indexToUnits[index] = (byte)unitCount; + INDEX_TO_UNITS[index] = (byte)unitCount; } for (unitCount++; index < N1 + N2 + N3 + N4; index++, unitCount += 4) { - indexToUnits[index] = (byte)unitCount; + INDEX_TO_UNITS[index] = (byte)unitCount; } // Construct the static units to index lookup array. It will contain the following values. @@ -76,12 +76,12 @@ namespace SharpCompress.Compressors.PPMd.I1 // 22 22 22 22 23 23 23 23 24 24 24 24 25 25 25 25 26 26 26 26 27 27 27 27 28 28 28 28 29 29 29 29 // 30 30 30 30 31 31 31 31 32 32 32 32 33 33 33 33 34 34 34 34 35 35 35 35 36 36 36 36 37 37 37 37 - unitsToIndex = new byte[128]; + UNITS_TO_INDEX = new byte[128]; for (unitCount = index = 0; unitCount < 128; unitCount++) { - index += (uint)((indexToUnits[index] < unitCount + 1) ? 1 : 0); - unitsToIndex[unitCount] = (byte)index; + index += (uint)((INDEX_TO_UNITS[index] < unitCount + 1) ? 1 : 0); + UNITS_TO_INDEX[unitCount] = (byte)index; } } @@ -89,7 +89,7 @@ namespace SharpCompress.Compressors.PPMd.I1 public Allocator() { - MemoryNodes = new MemoryNode[IndexCount]; + _memoryNodes = new MemoryNode[INDEX_COUNT]; } /// @@ -98,23 +98,23 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public void Initialize() { - for (int index = 0; index < IndexCount; index++) + for (int index = 0; index < INDEX_COUNT; index++) { - MemoryNodes[index] = new MemoryNode((uint)(NodeOffset + index * MemoryNode.Size), Memory); - MemoryNodes[index].Stamp = 0; - MemoryNodes[index].Next = MemoryNode.Zero; - MemoryNodes[index].UnitCount = 0; + _memoryNodes[index] = new MemoryNode((uint)(NODE_OFFSET + index * MemoryNode.SIZE), _memory); + _memoryNodes[index].Stamp = 0; + _memoryNodes[index].Next = MemoryNode.ZERO; + _memoryNodes[index].UnitCount = 0; } - Text = Heap; + _text = _heap; - uint difference = UnitSize * (AllocatorSize / 8 / UnitSize * 7); + uint difference = UNIT_SIZE * (_allocatorSize / 8 / UNIT_SIZE * 7); - HighUnit = Heap + AllocatorSize; - LowUnit = HighUnit - difference; - BaseUnit = HighUnit - difference; + _highUnit = _heap + _allocatorSize; + _lowUnit = _highUnit - difference; + _baseUnit = _highUnit - difference; - GlueCount = 0; + _glueCount = 0; } /// @@ -127,12 +127,12 @@ namespace SharpCompress.Compressors.PPMd.I1 public void Start(int allocatorSize) { uint size = (uint)allocatorSize; - if (AllocatorSize != size) + if (_allocatorSize != size) { Stop(); - Memory = new byte[HeapOffset + size]; // the single, large array of bytes - Heap = new Pointer(HeapOffset, Memory); // reserve bytes in the range 0 .. HeapOffset - 1 - AllocatorSize = size; + _memory = new byte[HEAP_OFFSET + size]; // the single, large array of bytes + _heap = new Pointer(HEAP_OFFSET, _memory); // reserve bytes in the range 0 .. HeapOffset - 1 + _allocatorSize = size; } } @@ -145,11 +145,11 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public void Stop() { - if (AllocatorSize != 0) + if (_allocatorSize != 0) { - AllocatorSize = 0; - Memory = null; - Heap = Pointer.Zero; + _allocatorSize = 0; + _memory = null; + _heap = Pointer.ZERO; } } @@ -159,36 +159,36 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public uint GetMemoryUsed() { - uint memoryUsed = AllocatorSize - (HighUnit - LowUnit) - (BaseUnit - Text); - for (uint index = 0; index < IndexCount; index++) + uint memoryUsed = _allocatorSize - (_highUnit - _lowUnit) - (_baseUnit - _text); + for (uint index = 0; index < INDEX_COUNT; index++) { - memoryUsed -= UnitSize * indexToUnits[index] * MemoryNodes[index].Stamp; + memoryUsed -= UNIT_SIZE * INDEX_TO_UNITS[index] * _memoryNodes[index].Stamp; } return memoryUsed; } /// - /// Allocate a given number of units from the single, large array. Each unit is bytes + /// Allocate a given number of units from the single, large array. Each unit is bytes /// in size. /// /// /// public Pointer AllocateUnits(uint unitCount) { - uint index = unitsToIndex[unitCount - 1]; - if (MemoryNodes[index].Available) + uint index = UNITS_TO_INDEX[unitCount - 1]; + if (_memoryNodes[index].Available) { - return MemoryNodes[index].Remove(); + return _memoryNodes[index].Remove(); } - Pointer allocatedBlock = LowUnit; - LowUnit += indexToUnits[index] * UnitSize; - if (LowUnit <= HighUnit) + Pointer allocatedBlock = _lowUnit; + _lowUnit += INDEX_TO_UNITS[index] * UNIT_SIZE; + if (_lowUnit <= _highUnit) { return allocatedBlock; } - LowUnit -= indexToUnits[index] * UnitSize; + _lowUnit -= INDEX_TO_UNITS[index] * UNIT_SIZE; return AllocateUnitsRare(index); } @@ -198,13 +198,13 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public Pointer AllocateContext() { - if (HighUnit != LowUnit) + if (_highUnit != _lowUnit) { - return (HighUnit -= UnitSize); + return (_highUnit -= UNIT_SIZE); } - if (MemoryNodes[0].Available) + if (_memoryNodes[0].Available) { - return MemoryNodes[0].Remove(); + return _memoryNodes[0].Remove(); } return AllocateUnitsRare(0); } @@ -217,8 +217,8 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public Pointer ExpandUnits(Pointer oldPointer, uint oldUnitCount) { - uint oldIndex = unitsToIndex[oldUnitCount - 1]; - uint newIndex = unitsToIndex[oldUnitCount]; + uint oldIndex = UNITS_TO_INDEX[oldUnitCount - 1]; + uint newIndex = UNITS_TO_INDEX[oldUnitCount]; if (oldIndex == newIndex) { @@ -227,10 +227,10 @@ namespace SharpCompress.Compressors.PPMd.I1 Pointer pointer = AllocateUnits(oldUnitCount + 1); - if (pointer != Pointer.Zero) + if (pointer != Pointer.ZERO) { CopyUnits(pointer, oldPointer, oldUnitCount); - MemoryNodes[oldIndex].Insert(oldPointer, oldUnitCount); + _memoryNodes[oldIndex].Insert(oldPointer, oldUnitCount); } return pointer; @@ -245,19 +245,19 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public Pointer ShrinkUnits(Pointer oldPointer, uint oldUnitCount, uint newUnitCount) { - uint oldIndex = unitsToIndex[oldUnitCount - 1]; - uint newIndex = unitsToIndex[newUnitCount - 1]; + uint oldIndex = UNITS_TO_INDEX[oldUnitCount - 1]; + uint newIndex = UNITS_TO_INDEX[newUnitCount - 1]; if (oldIndex == newIndex) { return oldPointer; } - if (MemoryNodes[newIndex].Available) + if (_memoryNodes[newIndex].Available) { - Pointer pointer = MemoryNodes[newIndex].Remove(); + Pointer pointer = _memoryNodes[newIndex].Remove(); CopyUnits(pointer, oldPointer, newUnitCount); - MemoryNodes[oldIndex].Insert(oldPointer, indexToUnits[oldIndex]); + _memoryNodes[oldIndex].Insert(oldPointer, INDEX_TO_UNITS[oldIndex]); return pointer; } SplitBlock(oldPointer, oldIndex, newIndex); @@ -272,44 +272,44 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public void FreeUnits(Pointer pointer, uint unitCount) { - uint index = unitsToIndex[unitCount - 1]; - MemoryNodes[index].Insert(pointer, indexToUnits[index]); + uint index = UNITS_TO_INDEX[unitCount - 1]; + _memoryNodes[index].Insert(pointer, INDEX_TO_UNITS[index]); } public void SpecialFreeUnits(Pointer pointer) { - if (pointer != BaseUnit) + if (pointer != _baseUnit) { - MemoryNodes[0].Insert(pointer, 1); + _memoryNodes[0].Insert(pointer, 1); } else { MemoryNode memoryNode = pointer; memoryNode.Stamp = uint.MaxValue; - BaseUnit += UnitSize; + _baseUnit += UNIT_SIZE; } } public Pointer MoveUnitsUp(Pointer oldPointer, uint unitCount) { - uint index = unitsToIndex[unitCount - 1]; + uint index = UNITS_TO_INDEX[unitCount - 1]; - if (oldPointer > BaseUnit + 16 * 1024 || oldPointer > MemoryNodes[index].Next) + if (oldPointer > _baseUnit + 16 * 1024 || oldPointer > _memoryNodes[index].Next) { return oldPointer; } - Pointer pointer = MemoryNodes[index].Remove(); + Pointer pointer = _memoryNodes[index].Remove(); CopyUnits(pointer, oldPointer, unitCount); - unitCount = indexToUnits[index]; + unitCount = INDEX_TO_UNITS[index]; - if (oldPointer != BaseUnit) + if (oldPointer != _baseUnit) { - MemoryNodes[index].Insert(oldPointer, unitCount); + _memoryNodes[index].Insert(oldPointer, unitCount); } else { - BaseUnit += unitCount * UnitSize; + _baseUnit += unitCount * UNIT_SIZE; } return pointer; @@ -322,23 +322,23 @@ namespace SharpCompress.Compressors.PPMd.I1 public void ExpandText() { MemoryNode memoryNode; - uint[] counts = new uint[IndexCount]; + uint[] counts = new uint[INDEX_COUNT]; - while ((memoryNode = BaseUnit).Stamp == uint.MaxValue) + while ((memoryNode = _baseUnit).Stamp == uint.MaxValue) { - BaseUnit = memoryNode + memoryNode.UnitCount; - counts[unitsToIndex[memoryNode.UnitCount - 1]]++; + _baseUnit = memoryNode + memoryNode.UnitCount; + counts[UNITS_TO_INDEX[memoryNode.UnitCount - 1]]++; memoryNode.Stamp = 0; } - for (uint index = 0; index < IndexCount; index++) + for (uint index = 0; index < INDEX_COUNT; index++) { - for (memoryNode = MemoryNodes[index]; counts[index] != 0; memoryNode = memoryNode.Next) + for (memoryNode = _memoryNodes[index]; counts[index] != 0; memoryNode = memoryNode.Next) { while (memoryNode.Next.Stamp == 0) { memoryNode.Unlink(); - MemoryNodes[index].Stamp--; + _memoryNodes[index].Stamp--; if (--counts[index] == 0) { break; @@ -354,73 +354,73 @@ namespace SharpCompress.Compressors.PPMd.I1 private Pointer AllocateUnitsRare(uint index) { - if (GlueCount == 0) + if (_glueCount == 0) { GlueFreeBlocks(); - if (MemoryNodes[index].Available) + if (_memoryNodes[index].Available) { - return MemoryNodes[index].Remove(); + return _memoryNodes[index].Remove(); } } uint oldIndex = index; do { - if (++oldIndex == IndexCount) + if (++oldIndex == INDEX_COUNT) { - GlueCount--; - oldIndex = indexToUnits[index] * UnitSize; - return (BaseUnit - Text > oldIndex) ? (BaseUnit -= oldIndex) : Pointer.Zero; + _glueCount--; + oldIndex = INDEX_TO_UNITS[index] * UNIT_SIZE; + return (_baseUnit - _text > oldIndex) ? (_baseUnit -= oldIndex) : Pointer.ZERO; } } - while (!MemoryNodes[oldIndex].Available); + while (!_memoryNodes[oldIndex].Available); - Pointer allocatedBlock = MemoryNodes[oldIndex].Remove(); + Pointer allocatedBlock = _memoryNodes[oldIndex].Remove(); SplitBlock(allocatedBlock, oldIndex, index); return allocatedBlock; } private void SplitBlock(Pointer pointer, uint oldIndex, uint newIndex) { - uint unitCountDifference = (uint)(indexToUnits[oldIndex] - indexToUnits[newIndex]); - Pointer newPointer = pointer + indexToUnits[newIndex] * UnitSize; + uint unitCountDifference = (uint)(INDEX_TO_UNITS[oldIndex] - INDEX_TO_UNITS[newIndex]); + Pointer newPointer = pointer + INDEX_TO_UNITS[newIndex] * UNIT_SIZE; - uint index = unitsToIndex[unitCountDifference - 1]; - if (indexToUnits[index] != unitCountDifference) + uint index = UNITS_TO_INDEX[unitCountDifference - 1]; + if (INDEX_TO_UNITS[index] != unitCountDifference) { - uint unitCount = indexToUnits[--index]; - MemoryNodes[index].Insert(newPointer, unitCount); - newPointer += unitCount * UnitSize; + uint unitCount = INDEX_TO_UNITS[--index]; + _memoryNodes[index].Insert(newPointer, unitCount); + newPointer += unitCount * UNIT_SIZE; unitCountDifference -= unitCount; } - MemoryNodes[unitsToIndex[unitCountDifference - 1]].Insert(newPointer, unitCountDifference); + _memoryNodes[UNITS_TO_INDEX[unitCountDifference - 1]].Insert(newPointer, unitCountDifference); } private void GlueFreeBlocks() { - MemoryNode memoryNode = new MemoryNode(LocalOffset, Memory); + MemoryNode memoryNode = new MemoryNode(LOCAL_OFFSET, _memory); memoryNode.Stamp = 0; - memoryNode.Next = MemoryNode.Zero; + memoryNode.Next = MemoryNode.ZERO; memoryNode.UnitCount = 0; MemoryNode memoryNode0; MemoryNode memoryNode1; MemoryNode memoryNode2; - if (LowUnit != HighUnit) + if (_lowUnit != _highUnit) { - LowUnit[0] = 0; + _lowUnit[0] = 0; } // Find all unused memory nodes. memoryNode1 = memoryNode; - for (uint index = 0; index < IndexCount; index++) + for (uint index = 0; index < INDEX_COUNT; index++) { - while (MemoryNodes[index].Available) + while (_memoryNodes[index].Available) { - memoryNode0 = MemoryNodes[index].Remove(); + memoryNode0 = _memoryNodes[index].Remove(); if (memoryNode0.UnitCount != 0) { while ((memoryNode2 = memoryNode0 + memoryNode0.UnitCount).Stamp == uint.MaxValue) @@ -444,22 +444,22 @@ namespace SharpCompress.Compressors.PPMd.I1 { for (; unitCount > 128; unitCount -= 128, memoryNode0 += 128) { - MemoryNodes[IndexCount - 1].Insert(memoryNode0, 128); + _memoryNodes[INDEX_COUNT - 1].Insert(memoryNode0, 128); } - uint index = unitsToIndex[unitCount - 1]; - if (indexToUnits[index] != unitCount) + uint index = UNITS_TO_INDEX[unitCount - 1]; + if (INDEX_TO_UNITS[index] != unitCount) { - uint unitCountDifference = unitCount - indexToUnits[--index]; - MemoryNodes[unitCountDifference - 1].Insert(memoryNode0 + (unitCount - unitCountDifference), + uint unitCountDifference = unitCount - INDEX_TO_UNITS[--index]; + _memoryNodes[unitCountDifference - 1].Insert(memoryNode0 + (unitCount - unitCountDifference), unitCountDifference); } - MemoryNodes[index].Insert(memoryNode0, indexToUnits[index]); + _memoryNodes[index].Insert(memoryNode0, INDEX_TO_UNITS[index]); } } - GlueCount = 1 << 13; + _glueCount = 1 << 13; } private void CopyUnits(Pointer target, Pointer source, uint unitCount) @@ -478,8 +478,8 @@ namespace SharpCompress.Compressors.PPMd.I1 target[9] = source[9]; target[10] = source[10]; target[11] = source[11]; - target += UnitSize; - source += UnitSize; + target += UNIT_SIZE; + source += UNIT_SIZE; } while (--unitCount != 0); } diff --git a/src/SharpCompress/Compressors/PPMd/I1/Coder.cs b/src/SharpCompress/Compressors/PPMd/I1/Coder.cs index 9dbe9cfc..a5c0d862 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Coder.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Coder.cs @@ -11,94 +11,94 @@ namespace SharpCompress.Compressors.PPMd.I1 /// /// /// Note that in most cases fields are used rather than properties for performance reasons (for example, - /// is a field rather than a property). + /// is a field rather than a property). /// internal class Coder { - private const uint RangeTop = 1 << 24; - private const uint RangeBottom = 1 << 15; - private uint low; - private uint code; - private uint range; + private const uint RANGE_TOP = 1 << 24; + private const uint RANGE_BOTTOM = 1 << 15; + private uint _low; + private uint _code; + private uint _range; - public uint LowCount; - public uint HighCount; - public uint Scale; + public uint _lowCount; + public uint _highCount; + public uint _scale; public void RangeEncoderInitialize() { - low = 0; - range = uint.MaxValue; + _low = 0; + _range = uint.MaxValue; } public void RangeEncoderNormalize(Stream stream) { - while ((low ^ (low + range)) < RangeTop || - range < RangeBottom && ((range = (uint)-low & (RangeBottom - 1)) != 0 || true)) + while ((_low ^ (_low + _range)) < RANGE_TOP || + _range < RANGE_BOTTOM && ((_range = (uint)-_low & (RANGE_BOTTOM - 1)) != 0 || true)) { - stream.WriteByte((byte)(low >> 24)); - range <<= 8; - low <<= 8; + stream.WriteByte((byte)(_low >> 24)); + _range <<= 8; + _low <<= 8; } } public void RangeEncodeSymbol() { - low += LowCount * (range /= Scale); - range *= HighCount - LowCount; + _low += _lowCount * (_range /= _scale); + _range *= _highCount - _lowCount; } public void RangeShiftEncodeSymbol(int rangeShift) { - low += LowCount * (range >>= rangeShift); - range *= HighCount - LowCount; + _low += _lowCount * (_range >>= rangeShift); + _range *= _highCount - _lowCount; } public void RangeEncoderFlush(Stream stream) { for (uint index = 0; index < 4; index++) { - stream.WriteByte((byte)(low >> 24)); - low <<= 8; + stream.WriteByte((byte)(_low >> 24)); + _low <<= 8; } } public void RangeDecoderInitialize(Stream stream) { - low = 0; - code = 0; - range = uint.MaxValue; + _low = 0; + _code = 0; + _range = uint.MaxValue; for (uint index = 0; index < 4; index++) { - code = (code << 8) | (byte)stream.ReadByte(); + _code = (_code << 8) | (byte)stream.ReadByte(); } } public void RangeDecoderNormalize(Stream stream) { - while ((low ^ (low + range)) < RangeTop || - range < RangeBottom && ((range = (uint)-low & (RangeBottom - 1)) != 0 || true)) + while ((_low ^ (_low + _range)) < RANGE_TOP || + _range < RANGE_BOTTOM && ((_range = (uint)-_low & (RANGE_BOTTOM - 1)) != 0 || true)) { - code = (code << 8) | (byte)stream.ReadByte(); - range <<= 8; - low <<= 8; + _code = (_code << 8) | (byte)stream.ReadByte(); + _range <<= 8; + _low <<= 8; } } public uint RangeGetCurrentCount() { - return (code - low) / (range /= Scale); + return (_code - _low) / (_range /= _scale); } public uint RangeGetCurrentShiftCount(int rangeShift) { - return (code - low) / (range >>= rangeShift); + return (_code - _low) / (_range >>= rangeShift); } public void RangeRemoveSubrange() { - low += range * LowCount; - range *= HighCount - LowCount; + _low += _range * _lowCount; + _range *= _highCount - _lowCount; } } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs b/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs index 23a08643..cded381e 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/MemoryNode.cs @@ -7,9 +7,9 @@ namespace SharpCompress.Compressors.PPMd.I1 { /// - /// A structure containing a single address. The address represents a location in the - /// array. That location in the array contains information itself describing a section - /// of the array (ie. a block of memory). + /// A structure containing a single address. The address represents a location in the + /// array. That location in the array contains information itself describing a section + /// of the array (ie. a block of memory). /// /// /// @@ -24,23 +24,23 @@ namespace SharpCompress.Compressors.PPMd.I1 /// 4 UnitCount /// /// - /// Note that is a field rather than a property for performance reasons. + /// Note that is a field rather than a property for performance reasons. /// /// internal struct MemoryNode { - public uint Address; - public byte[] Memory; - public static readonly MemoryNode Zero = new MemoryNode(0, null); - public const int Size = 12; + public uint _address; + public byte[] _memory; + public static readonly MemoryNode ZERO = new MemoryNode(0, null); + public const int SIZE = 12; /// /// Initializes a new instance of the structure. /// public MemoryNode(uint address, byte[] memory) { - Address = address; - Memory = memory; + _address = address; + _memory = memory; } /// @@ -48,14 +48,14 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public uint Stamp { - get => Memory[Address] | ((uint)Memory[Address + 1]) << 8 | ((uint)Memory[Address + 2]) << 16 | - ((uint)Memory[Address + 3]) << 24; + get => _memory[_address] | ((uint)_memory[_address + 1]) << 8 | ((uint)_memory[_address + 2]) << 16 | + ((uint)_memory[_address + 3]) << 24; set { - Memory[Address] = (byte)value; - Memory[Address + 1] = (byte)(value >> 8); - Memory[Address + 2] = (byte)(value >> 16); - Memory[Address + 3] = (byte)(value >> 24); + _memory[_address] = (byte)value; + _memory[_address + 1] = (byte)(value >> 8); + _memory[_address + 2] = (byte)(value >> 16); + _memory[_address + 3] = (byte)(value >> 24); } } @@ -65,14 +65,14 @@ namespace SharpCompress.Compressors.PPMd.I1 public MemoryNode Next { get => new MemoryNode( - Memory[Address + 4] | ((uint)Memory[Address + 5]) << 8 | - ((uint)Memory[Address + 6]) << 16 | ((uint)Memory[Address + 7]) << 24, Memory); + _memory[_address + 4] | ((uint)_memory[_address + 5]) << 8 | + ((uint)_memory[_address + 6]) << 16 | ((uint)_memory[_address + 7]) << 24, _memory); set { - Memory[Address + 4] = (byte)value.Address; - Memory[Address + 5] = (byte)(value.Address >> 8); - Memory[Address + 6] = (byte)(value.Address >> 16); - Memory[Address + 7] = (byte)(value.Address >> 24); + _memory[_address + 4] = (byte)value._address; + _memory[_address + 5] = (byte)(value._address >> 8); + _memory[_address + 6] = (byte)(value._address >> 16); + _memory[_address + 7] = (byte)(value._address >> 24); } } @@ -81,21 +81,21 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public uint UnitCount { - get => Memory[Address + 8] | ((uint)Memory[Address + 9]) << 8 | - ((uint)Memory[Address + 10]) << 16 | ((uint)Memory[Address + 11]) << 24; + get => _memory[_address + 8] | ((uint)_memory[_address + 9]) << 8 | + ((uint)_memory[_address + 10]) << 16 | ((uint)_memory[_address + 11]) << 24; set { - Memory[Address + 8] = (byte)value; - Memory[Address + 9] = (byte)(value >> 8); - Memory[Address + 10] = (byte)(value >> 16); - Memory[Address + 11] = (byte)(value >> 24); + _memory[_address + 8] = (byte)value; + _memory[_address + 9] = (byte)(value >> 8); + _memory[_address + 10] = (byte)(value >> 16); + _memory[_address + 11] = (byte)(value >> 24); } } /// /// Gets whether there is a next memory node available. /// - public bool Available => Next.Address != 0; + public bool Available => Next._address != 0; /// /// Link in the provided memory node. @@ -147,7 +147,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static implicit operator MemoryNode(Pointer pointer) { - return new MemoryNode(pointer.Address, pointer.Memory); + return new MemoryNode(pointer._address, pointer._memory); } /// @@ -158,7 +158,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static MemoryNode operator +(MemoryNode memoryNode, int offset) { - memoryNode.Address = (uint)(memoryNode.Address + offset * Size); + memoryNode._address = (uint)(memoryNode._address + offset * SIZE); return memoryNode; } @@ -170,7 +170,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static MemoryNode operator +(MemoryNode memoryNode, uint offset) { - memoryNode.Address += offset * Size; + memoryNode._address += offset * SIZE; return memoryNode; } @@ -182,7 +182,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static MemoryNode operator -(MemoryNode memoryNode, int offset) { - memoryNode.Address = (uint)(memoryNode.Address - offset * Size); + memoryNode._address = (uint)(memoryNode._address - offset * SIZE); return memoryNode; } @@ -194,7 +194,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static MemoryNode operator -(MemoryNode memoryNode, uint offset) { - memoryNode.Address -= offset * Size; + memoryNode._address -= offset * SIZE; return memoryNode; } @@ -206,7 +206,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator ==(MemoryNode memoryNode1, MemoryNode memoryNode2) { - return memoryNode1.Address == memoryNode2.Address; + return memoryNode1._address == memoryNode2._address; } /// @@ -217,7 +217,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator !=(MemoryNode memoryNode1, MemoryNode memoryNode2) { - return memoryNode1.Address != memoryNode2.Address; + return memoryNode1._address != memoryNode2._address; } /// @@ -230,7 +230,7 @@ namespace SharpCompress.Compressors.PPMd.I1 if (obj is MemoryNode) { MemoryNode memoryNode = (MemoryNode)obj; - return memoryNode.Address == Address; + return memoryNode._address == _address; } return base.Equals(obj); } @@ -241,7 +241,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// A 32-bit signed integer that is the hash code for this instance. public override int GetHashCode() { - return Address.GetHashCode(); + return _address.GetHashCode(); } } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/PPMd/I1/Model.cs b/src/SharpCompress/Compressors/PPMd/I1/Model.cs index a71939cd..11d065c1 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Model.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Model.cs @@ -15,50 +15,50 @@ namespace SharpCompress.Compressors.PPMd.I1 /// internal partial class Model { - public const uint Signature = 0x84acaf8fU; - public const char Variant = 'I'; - public const int MaximumOrder = 16; // maximum allowed model order + public const uint SIGNATURE = 0x84acaf8fU; + public const char VARIANT = 'I'; + public const int MAXIMUM_ORDER = 16; // maximum allowed model order - private const byte UpperFrequency = 5; - private const byte IntervalBitCount = 7; - private const byte PeriodBitCount = 7; - private const byte TotalBitCount = IntervalBitCount + PeriodBitCount; - private const uint Interval = 1 << IntervalBitCount; - private const uint BinaryScale = 1 << TotalBitCount; - private const uint MaximumFrequency = 124; - private const uint OrderBound = 9; + private const byte UPPER_FREQUENCY = 5; + private const byte INTERVAL_BIT_COUNT = 7; + private const byte PERIOD_BIT_COUNT = 7; + private const byte TOTAL_BIT_COUNT = INTERVAL_BIT_COUNT + PERIOD_BIT_COUNT; + private const uint INTERVAL = 1 << INTERVAL_BIT_COUNT; + private const uint BINARY_SCALE = 1 << TOTAL_BIT_COUNT; + private const uint MAXIMUM_FREQUENCY = 124; + private const uint ORDER_BOUND = 9; - private readonly See2Context[,] see2Contexts; - private readonly See2Context emptySee2Context; - private PpmContext maximumContext; - private readonly ushort[,] binarySummary = new ushort[25, 64]; // binary SEE-contexts - private readonly byte[] numberStatisticsToBinarySummaryIndex = new byte[256]; - private readonly byte[] probabilities = new byte[260]; - private readonly byte[] characterMask = new byte[256]; - private byte escapeCount; - private int modelOrder; - private int orderFall; - private int initialEscape; - private int initialRunLength; - private int runLength; - private byte previousSuccess; - private byte numberMasked; - private ModelRestorationMethod method; - private PpmState foundState; // found next state transition + private readonly See2Context[,] _see2Contexts; + private readonly See2Context _emptySee2Context; + private PpmContext _maximumContext; + private readonly ushort[,] _binarySummary = new ushort[25, 64]; // binary SEE-contexts + private readonly byte[] _numberStatisticsToBinarySummaryIndex = new byte[256]; + private readonly byte[] _probabilities = new byte[260]; + private readonly byte[] _characterMask = new byte[256]; + private byte _escapeCount; + private int _modelOrder; + private int _orderFall; + private int _initialEscape; + private int _initialRunLength; + private int _runLength; + private byte _previousSuccess; + private byte _numberMasked; + private ModelRestorationMethod _method; + private PpmState _foundState; // found next state transition - private Allocator Allocator; - private Coder Coder; - private PpmContext minimumContext; - private byte numberStatistics; - private readonly PpmState[] decodeStates = new PpmState[256]; + private Allocator _allocator; + private Coder _coder; + private PpmContext _minimumContext; + private byte _numberStatistics; + private readonly PpmState[] _decodeStates = new PpmState[256]; - private static readonly ushort[] InitialBinaryEscapes = + private static readonly ushort[] INITIAL_BINARY_ESCAPES = { 0x3CDD, 0x1F3F, 0x59BF, 0x48F3, 0x64A1, 0x5ABC, 0x6632, 0x6051 }; - private static readonly byte[] ExponentialEscapes = {25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2}; + private static readonly byte[] EXPONENTIAL_ESCAPES = {25, 14, 9, 7, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 2}; #region Public Methods @@ -75,15 +75,15 @@ namespace SharpCompress.Compressors.PPMd.I1 // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 // 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 - numberStatisticsToBinarySummaryIndex[0] = 2 * 0; - numberStatisticsToBinarySummaryIndex[1] = 2 * 1; + _numberStatisticsToBinarySummaryIndex[0] = 2 * 0; + _numberStatisticsToBinarySummaryIndex[1] = 2 * 1; for (int index = 2; index < 11; index++) { - numberStatisticsToBinarySummaryIndex[index] = 2 * 2; + _numberStatisticsToBinarySummaryIndex[index] = 2 * 2; } for (int index = 11; index < 256; index++) { - numberStatisticsToBinarySummaryIndex[index] = 2 * 3; + _numberStatisticsToBinarySummaryIndex[index] = 2 * 3; } // Construct the probability table. Initially it will contain the following values (depending on the value of @@ -101,16 +101,16 @@ namespace SharpCompress.Compressors.PPMd.I1 uint count = 1; uint step = 1; - uint probability = UpperFrequency; + uint probability = UPPER_FREQUENCY; - for (int index = 0; index < UpperFrequency; index++) + for (int index = 0; index < UPPER_FREQUENCY; index++) { - probabilities[index] = (byte)index; + _probabilities[index] = (byte)index; } - for (int index = UpperFrequency; index < 260; index++) + for (int index = UPPER_FREQUENCY; index < 260; index++) { - probabilities[index] = (byte)probability; + _probabilities[index] = (byte)probability; count--; if (count == 0) { @@ -122,21 +122,21 @@ namespace SharpCompress.Compressors.PPMd.I1 // Create the context array. - see2Contexts = new See2Context[24, 32]; + _see2Contexts = new See2Context[24, 32]; for (int index1 = 0; index1 < 24; index1++) { for (int index2 = 0; index2 < 32; index2++) { - see2Contexts[index1, index2] = new See2Context(); + _see2Contexts[index1, index2] = new See2Context(); } } // Set the signature (identifying the algorithm). - emptySee2Context = new See2Context(); - emptySee2Context.Summary = (ushort)(Signature & 0x0000ffff); - emptySee2Context.Shift = (byte)((Signature >> 16) & 0x000000ff); - emptySee2Context.Count = (byte)(Signature >> 24); + _emptySee2Context = new See2Context(); + _emptySee2Context._summary = (ushort)(SIGNATURE & 0x0000ffff); + _emptySee2Context._shift = (byte)((SIGNATURE >> 16) & 0x000000ff); + _emptySee2Context._count = (byte)(SIGNATURE >> 24); } /// @@ -160,19 +160,19 @@ namespace SharpCompress.Compressors.PPMd.I1 internal Coder EncodeStart(PpmdProperties properties) { - Allocator = properties.Allocator; - Coder = new Coder(); - Coder.RangeEncoderInitialize(); - StartModel(properties.ModelOrder, properties.ModelRestorationMethod); - return Coder; + _allocator = properties._allocator; + _coder = new Coder(); + _coder.RangeEncoderInitialize(); + StartModel(properties.ModelOrder, properties.RestorationMethod); + return _coder; } internal void EncodeBlock(Stream target, Stream source, bool final) { while (true) { - minimumContext = maximumContext; - numberStatistics = minimumContext.NumberStatistics; + _minimumContext = _maximumContext; + _numberStatistics = _minimumContext.NumberStatistics; int c = source.ReadByte(); if (c < 0 && !final) @@ -180,52 +180,52 @@ namespace SharpCompress.Compressors.PPMd.I1 return; } - if (numberStatistics != 0) + if (_numberStatistics != 0) { - EncodeSymbol1(c, minimumContext); - Coder.RangeEncodeSymbol(); + EncodeSymbol1(c, _minimumContext); + _coder.RangeEncodeSymbol(); } else { - EncodeBinarySymbol(c, minimumContext); - Coder.RangeShiftEncodeSymbol(TotalBitCount); + EncodeBinarySymbol(c, _minimumContext); + _coder.RangeShiftEncodeSymbol(TOTAL_BIT_COUNT); } - while (foundState == PpmState.Zero) + while (_foundState == PpmState.ZERO) { - Coder.RangeEncoderNormalize(target); + _coder.RangeEncoderNormalize(target); do { - orderFall++; - minimumContext = minimumContext.Suffix; - if (minimumContext == PpmContext.Zero) + _orderFall++; + _minimumContext = _minimumContext.Suffix; + if (_minimumContext == PpmContext.ZERO) { goto StopEncoding; } } - while (minimumContext.NumberStatistics == numberMasked); - EncodeSymbol2(c, minimumContext); - Coder.RangeEncodeSymbol(); + while (_minimumContext.NumberStatistics == _numberMasked); + EncodeSymbol2(c, _minimumContext); + _coder.RangeEncodeSymbol(); } - if (orderFall == 0 && (Pointer)foundState.Successor >= Allocator.BaseUnit) + if (_orderFall == 0 && (Pointer)_foundState.Successor >= _allocator._baseUnit) { - maximumContext = foundState.Successor; + _maximumContext = _foundState.Successor; } else { - UpdateModel(minimumContext); - if (escapeCount == 0) + UpdateModel(_minimumContext); + if (_escapeCount == 0) { ClearMask(); } } - Coder.RangeEncoderNormalize(target); + _coder.RangeEncoderNormalize(target); } StopEncoding: - Coder.RangeEncoderFlush(target); + _coder.RangeEncoderFlush(target); } /// @@ -254,18 +254,18 @@ namespace SharpCompress.Compressors.PPMd.I1 internal Coder DecodeStart(Stream source, PpmdProperties properties) { - Allocator = properties.Allocator; - Coder = new Coder(); - Coder.RangeDecoderInitialize(source); - StartModel(properties.ModelOrder, properties.ModelRestorationMethod); - minimumContext = maximumContext; - numberStatistics = minimumContext.NumberStatistics; - return Coder; + _allocator = properties._allocator; + _coder = new Coder(); + _coder.RangeDecoderInitialize(source); + StartModel(properties.ModelOrder, properties.RestorationMethod); + _minimumContext = _maximumContext; + _numberStatistics = _minimumContext.NumberStatistics; + return _coder; } internal int DecodeBlock(Stream source, byte[] buffer, int offset, int count) { - if (minimumContext == PpmContext.Zero) + if (_minimumContext == PpmContext.ZERO) { return 0; } @@ -273,54 +273,54 @@ namespace SharpCompress.Compressors.PPMd.I1 int total = 0; while (total < count) { - if (numberStatistics != 0) + if (_numberStatistics != 0) { - DecodeSymbol1(minimumContext); + DecodeSymbol1(_minimumContext); } else { - DecodeBinarySymbol(minimumContext); + DecodeBinarySymbol(_minimumContext); } - Coder.RangeRemoveSubrange(); + _coder.RangeRemoveSubrange(); - while (foundState == PpmState.Zero) + while (_foundState == PpmState.ZERO) { - Coder.RangeDecoderNormalize(source); + _coder.RangeDecoderNormalize(source); do { - orderFall++; - minimumContext = minimumContext.Suffix; - if (minimumContext == PpmContext.Zero) + _orderFall++; + _minimumContext = _minimumContext.Suffix; + if (_minimumContext == PpmContext.ZERO) { goto StopDecoding; } } - while (minimumContext.NumberStatistics == numberMasked); - DecodeSymbol2(minimumContext); - Coder.RangeRemoveSubrange(); + while (_minimumContext.NumberStatistics == _numberMasked); + DecodeSymbol2(_minimumContext); + _coder.RangeRemoveSubrange(); } - buffer[offset] = foundState.Symbol; + buffer[offset] = _foundState.Symbol; offset++; total++; - if (orderFall == 0 && (Pointer)foundState.Successor >= Allocator.BaseUnit) + if (_orderFall == 0 && (Pointer)_foundState.Successor >= _allocator._baseUnit) { - maximumContext = foundState.Successor; + _maximumContext = _foundState.Successor; } else { - UpdateModel(minimumContext); - if (escapeCount == 0) + UpdateModel(_minimumContext); + if (_escapeCount == 0) { ClearMask(); } } - minimumContext = maximumContext; - numberStatistics = minimumContext.NumberStatistics; - Coder.RangeDecoderNormalize(source); + _minimumContext = _maximumContext; + _numberStatistics = _minimumContext.NumberStatistics; + _coder.RangeDecoderNormalize(source); } StopDecoding: @@ -337,65 +337,65 @@ namespace SharpCompress.Compressors.PPMd.I1 /// private void StartModel(int modelOrder, ModelRestorationMethod modelRestorationMethod) { - Array.Clear(characterMask, 0, characterMask.Length); - escapeCount = 1; + Array.Clear(_characterMask, 0, _characterMask.Length); + _escapeCount = 1; // Compress in "solid" mode if the model order value is set to 1 (this will examine the current PPM context // structures to determine the value of orderFall). if (modelOrder < 2) { - orderFall = this.modelOrder; - for (PpmContext context = maximumContext; context.Suffix != PpmContext.Zero; context = context.Suffix) + _orderFall = _modelOrder; + for (PpmContext context = _maximumContext; context.Suffix != PpmContext.ZERO; context = context.Suffix) { - orderFall--; + _orderFall--; } return; } - this.modelOrder = modelOrder; - orderFall = modelOrder; - method = modelRestorationMethod; - Allocator.Initialize(); - initialRunLength = -((modelOrder < 12) ? modelOrder : 12) - 1; - runLength = initialRunLength; + _modelOrder = modelOrder; + _orderFall = modelOrder; + _method = modelRestorationMethod; + _allocator.Initialize(); + _initialRunLength = -((modelOrder < 12) ? modelOrder : 12) - 1; + _runLength = _initialRunLength; // Allocate the context structure. - maximumContext = Allocator.AllocateContext(); - maximumContext.Suffix = PpmContext.Zero; - maximumContext.NumberStatistics = 255; - maximumContext.SummaryFrequency = (ushort)(maximumContext.NumberStatistics + 2); - maximumContext.Statistics = Allocator.AllocateUnits(256 / 2); + _maximumContext = _allocator.AllocateContext(); + _maximumContext.Suffix = PpmContext.ZERO; + _maximumContext.NumberStatistics = 255; + _maximumContext.SummaryFrequency = (ushort)(_maximumContext.NumberStatistics + 2); + _maximumContext.Statistics = _allocator.AllocateUnits(256 / 2); // allocates enough space for 256 PPM states (each is 6 bytes) - previousSuccess = 0; + _previousSuccess = 0; for (int index = 0; index < 256; index++) { - PpmState state = maximumContext.Statistics[index]; + PpmState state = _maximumContext.Statistics[index]; state.Symbol = (byte)index; state.Frequency = 1; - state.Successor = PpmContext.Zero; + state.Successor = PpmContext.ZERO; } uint probability = 0; for (int index1 = 0; probability < 25; probability++) { - while (probabilities[index1] == probability) + while (_probabilities[index1] == probability) { index1++; } for (int index2 = 0; index2 < 8; index2++) { - binarySummary[probability, index2] = - (ushort)(BinaryScale - InitialBinaryEscapes[index2] / (index1 + 1)); + _binarySummary[probability, index2] = + (ushort)(BINARY_SCALE - INITIAL_BINARY_ESCAPES[index2] / (index1 + 1)); } for (int index2 = 8; index2 < 64; index2 += 8) { for (int index3 = 0; index3 < 8; index3++) { - binarySummary[probability, index2 + index3] = binarySummary[probability, index3]; + _binarySummary[probability, index2 + index3] = _binarySummary[probability, index3]; } } } @@ -403,36 +403,36 @@ namespace SharpCompress.Compressors.PPMd.I1 probability = 0; for (uint index1 = 0; probability < 24; probability++) { - while (probabilities[index1 + 3] == probability + 3) + while (_probabilities[index1 + 3] == probability + 3) { index1++; } for (int index2 = 0; index2 < 32; index2++) { - see2Contexts[probability, index2].Initialize(2 * index1 + 5); + _see2Contexts[probability, index2].Initialize(2 * index1 + 5); } } } private void UpdateModel(PpmContext minimumContext) { - PpmState state = PpmState.Zero; - PpmContext Successor; - PpmContext currentContext = maximumContext; + PpmState state = PpmState.ZERO; + PpmContext successor; + PpmContext currentContext = _maximumContext; uint numberStatistics; uint ns1; uint cf; uint sf; uint s0; - uint foundStateFrequency = foundState.Frequency; - byte foundStateSymbol = foundState.Symbol; + uint foundStateFrequency = _foundState.Frequency; + byte foundStateSymbol = _foundState.Symbol; byte symbol; byte flag; - PpmContext foundStateSuccessor = foundState.Successor; + PpmContext foundStateSuccessor = _foundState.Successor; PpmContext context = minimumContext.Suffix; - if ((foundStateFrequency < MaximumFrequency / 4) && (context != PpmContext.Zero)) + if ((foundStateFrequency < MAXIMUM_FREQUENCY / 4) && (context != PpmContext.ZERO)) { if (context.NumberStatistics != 0) { @@ -451,7 +451,7 @@ namespace SharpCompress.Compressors.PPMd.I1 state--; } } - cf = (uint)((state.Frequency < MaximumFrequency - 9) ? 2 : 0); + cf = (uint)((state.Frequency < MAXIMUM_FREQUENCY - 9) ? 2 : 0); state.Frequency += (byte)cf; context.SummaryFrequency += (byte)cf; } @@ -462,29 +462,29 @@ namespace SharpCompress.Compressors.PPMd.I1 } } - if (orderFall == 0 && foundStateSuccessor != PpmContext.Zero) + if (_orderFall == 0 && foundStateSuccessor != PpmContext.ZERO) { - foundState.Successor = CreateSuccessors(true, state, minimumContext); - if (foundState.Successor == PpmContext.Zero) + _foundState.Successor = CreateSuccessors(true, state, minimumContext); + if (_foundState.Successor == PpmContext.ZERO) { goto RestartModel; } - maximumContext = foundState.Successor; + _maximumContext = _foundState.Successor; return; } - Allocator.Text[0] = foundStateSymbol; - Allocator.Text++; - Successor = Allocator.Text; + _allocator._text[0] = foundStateSymbol; + _allocator._text++; + successor = _allocator._text; - if (Allocator.Text >= Allocator.BaseUnit) + if (_allocator._text >= _allocator._baseUnit) { goto RestartModel; } - if (foundStateSuccessor != PpmContext.Zero) + if (foundStateSuccessor != PpmContext.ZERO) { - if (foundStateSuccessor < Allocator.BaseUnit) + if (foundStateSuccessor < _allocator._baseUnit) { foundStateSuccessor = CreateSuccessors(false, state, minimumContext); } @@ -494,21 +494,21 @@ namespace SharpCompress.Compressors.PPMd.I1 foundStateSuccessor = ReduceOrder(state, minimumContext); } - if (foundStateSuccessor == PpmContext.Zero) + if (foundStateSuccessor == PpmContext.ZERO) { goto RestartModel; } - if (--orderFall == 0) + if (--_orderFall == 0) { - Successor = foundStateSuccessor; - Allocator.Text -= (maximumContext != minimumContext) ? 1 : 0; + successor = foundStateSuccessor; + _allocator._text -= (_maximumContext != minimumContext) ? 1 : 0; } - else if (method > ModelRestorationMethod.Freeze) + else if (_method > ModelRestorationMethod.Freeze) { - Successor = foundStateSuccessor; - Allocator.Text = Allocator.Heap; - orderFall = 0; + successor = foundStateSuccessor; + _allocator._text = _allocator._heap; + _orderFall = 0; } numberStatistics = minimumContext.NumberStatistics; @@ -521,8 +521,8 @@ namespace SharpCompress.Compressors.PPMd.I1 { if ((ns1 & 1) != 0) { - state = Allocator.ExpandUnits(currentContext.Statistics, (ns1 + 1) >> 1); - if (state == PpmState.Zero) + state = _allocator.ExpandUnits(currentContext.Statistics, (ns1 + 1) >> 1); + if (state == PpmState.ZERO) { goto RestartModel; } @@ -532,23 +532,23 @@ namespace SharpCompress.Compressors.PPMd.I1 } else { - state = Allocator.AllocateUnits(1); - if (state == PpmState.Zero) + state = _allocator.AllocateUnits(1); + if (state == PpmState.ZERO) { goto RestartModel; } Copy(state, currentContext.FirstState); currentContext.Statistics = state; - if (state.Frequency < MaximumFrequency / 4 - 1) + if (state.Frequency < MAXIMUM_FREQUENCY / 4 - 1) { state.Frequency += state.Frequency; } else { - state.Frequency = (byte)(MaximumFrequency - 4); + state.Frequency = (byte)(MAXIMUM_FREQUENCY - 4); } currentContext.SummaryFrequency = - (ushort)(state.Frequency + initialEscape + ((numberStatistics > 2) ? 1 : 0)); + (ushort)(state.Frequency + _initialEscape + ((numberStatistics > 2) ? 1 : 0)); } cf = (uint)(2 * foundStateFrequency * (currentContext.SummaryFrequency + 6)); @@ -566,13 +566,13 @@ namespace SharpCompress.Compressors.PPMd.I1 } state = currentContext.Statistics + (++currentContext.NumberStatistics); - state.Successor = Successor; + state.Successor = successor; state.Symbol = foundStateSymbol; state.Frequency = (byte)cf; currentContext.Flags |= flag; } - maximumContext = foundStateSuccessor; + _maximumContext = foundStateSuccessor; return; RestartModel: @@ -581,22 +581,22 @@ namespace SharpCompress.Compressors.PPMd.I1 private PpmContext CreateSuccessors(bool skip, PpmState state, PpmContext context) { - PpmContext upBranch = foundState.Successor; - PpmState[] states = new PpmState[MaximumOrder]; + PpmContext upBranch = _foundState.Successor; + PpmState[] states = new PpmState[MAXIMUM_ORDER]; uint stateIndex = 0; - byte symbol = foundState.Symbol; + byte symbol = _foundState.Symbol; if (!skip) { - states[stateIndex++] = foundState; - if (context.Suffix == PpmContext.Zero) + states[stateIndex++] = _foundState; + if (context.Suffix == PpmContext.ZERO) { goto NoLoop; } } bool gotoLoopEntry = false; - if (state != PpmState.Zero) + if (state != PpmState.ZERO) { context = context.Suffix; gotoLoopEntry = true; @@ -624,7 +624,7 @@ namespace SharpCompress.Compressors.PPMd.I1 } while (temporary != symbol); } - temporary = (byte)((state.Frequency < MaximumFrequency - 9) ? 1 : 0); + temporary = (byte)((state.Frequency < MAXIMUM_FREQUENCY - 9) ? 1 : 0); state.Frequency += temporary; context.SummaryFrequency += temporary; } @@ -643,7 +643,7 @@ namespace SharpCompress.Compressors.PPMd.I1 } states[stateIndex++] = state; } - while (context.Suffix != PpmContext.Zero); + while (context.Suffix != PpmContext.ZERO); NoLoop: if (stateIndex == 0) @@ -683,10 +683,10 @@ namespace SharpCompress.Compressors.PPMd.I1 do { - PpmContext currentContext = Allocator.AllocateContext(); - if (currentContext == PpmContext.Zero) + PpmContext currentContext = _allocator.AllocateContext(); + if (currentContext == PpmContext.ZERO) { - return PpmContext.Zero; + return PpmContext.ZERO; } currentContext.NumberStatistics = localNumberStatistics; currentContext.Flags = localFlags; @@ -705,19 +705,19 @@ namespace SharpCompress.Compressors.PPMd.I1 private PpmContext ReduceOrder(PpmState state, PpmContext context) { PpmState currentState; - PpmState[] states = new PpmState[MaximumOrder]; + PpmState[] states = new PpmState[MAXIMUM_ORDER]; uint stateIndex = 0; PpmContext currentContext = context; - PpmContext UpBranch = Allocator.Text; + PpmContext upBranch = _allocator._text; byte temporary; - byte symbol = foundState.Symbol; + byte symbol = _foundState.Symbol; - states[stateIndex++] = foundState; - foundState.Successor = UpBranch; - orderFall++; + states[stateIndex++] = _foundState; + _foundState.Successor = upBranch; + _orderFall++; bool gotoLoopEntry = false; - if (state != PpmState.Zero) + if (state != PpmState.ZERO) { context = context.Suffix; gotoLoopEntry = true; @@ -731,17 +731,17 @@ namespace SharpCompress.Compressors.PPMd.I1 goto LoopEntry; } - if (context.Suffix == PpmContext.Zero) + if (context.Suffix == PpmContext.ZERO) { - if (method > ModelRestorationMethod.Freeze) + if (_method > ModelRestorationMethod.Freeze) { do { states[--stateIndex].Successor = context; } while (stateIndex != 0); - Allocator.Text = Allocator.Heap + 1; - orderFall = 1; + _allocator._text = _allocator._heap + 1; + _orderFall = 1; } return context; } @@ -759,7 +759,7 @@ namespace SharpCompress.Compressors.PPMd.I1 } while (temporary != symbol); } - temporary = (byte)((state.Frequency < MaximumFrequency - 9) ? 2 : 0); + temporary = (byte)((state.Frequency < MAXIMUM_FREQUENCY - 9) ? 2 : 0); state.Frequency += temporary; context.SummaryFrequency += temporary; } @@ -770,16 +770,16 @@ namespace SharpCompress.Compressors.PPMd.I1 } LoopEntry: - if (state.Successor != PpmContext.Zero) + if (state.Successor != PpmContext.ZERO) { break; } states[stateIndex++] = state; - state.Successor = UpBranch; - orderFall++; + state.Successor = upBranch; + _orderFall++; } - if (method > ModelRestorationMethod.Freeze) + if (_method > ModelRestorationMethod.Freeze) { context = state.Successor; do @@ -787,22 +787,22 @@ namespace SharpCompress.Compressors.PPMd.I1 states[--stateIndex].Successor = context; } while (stateIndex != 0); - Allocator.Text = Allocator.Heap + 1; - orderFall = 1; + _allocator._text = _allocator._heap + 1; + _orderFall = 1; return context; } - if (state.Successor <= UpBranch) + if (state.Successor <= upBranch) { - currentState = foundState; - foundState = state; - state.Successor = CreateSuccessors(false, PpmState.Zero, context); - foundState = currentState; + currentState = _foundState; + _foundState = state; + state.Successor = CreateSuccessors(false, PpmState.ZERO, context); + _foundState = currentState; } - if (orderFall == 1 && currentContext == maximumContext) + if (_orderFall == 1 && currentContext == _maximumContext) { - foundState.Successor = state.Successor; - Allocator.Text--; + _foundState.Successor = state.Successor; + _allocator._text--; } return state.Successor; @@ -812,8 +812,8 @@ namespace SharpCompress.Compressors.PPMd.I1 { PpmContext currentContext; - Allocator.Text = Allocator.Heap; - for (currentContext = maximumContext; currentContext != context; currentContext = currentContext.Suffix) + _allocator._text = _allocator._heap; + for (currentContext = _maximumContext; currentContext != context; currentContext = currentContext.Suffix) { if (--currentContext.NumberStatistics == 0) { @@ -822,7 +822,7 @@ namespace SharpCompress.Compressors.PPMd.I1 ((currentContext.Flags & 0x10) + ((currentContext.Statistics.Symbol >= 0x40) ? 0x08 : 0x00)); PpmState state = currentContext.Statistics; Copy(currentContext.FirstState, state); - Allocator.SpecialFreeUnits(state); + _allocator.SpecialFreeUnits(state); currentContext.FirstStateFrequency = (byte)((currentContext.FirstStateFrequency + 11) >> 3); } else @@ -843,45 +843,45 @@ namespace SharpCompress.Compressors.PPMd.I1 } } - if (method > ModelRestorationMethod.Freeze) + if (_method > ModelRestorationMethod.Freeze) { - maximumContext = foundStateSuccessor; - Allocator.GlueCount += (uint)(((Allocator.MemoryNodes[1].Stamp & 1) == 0) ? 1 : 0); + _maximumContext = foundStateSuccessor; + _allocator._glueCount += (uint)(((_allocator._memoryNodes[1].Stamp & 1) == 0) ? 1 : 0); } - else if (method == ModelRestorationMethod.Freeze) + else if (_method == ModelRestorationMethod.Freeze) { - while (maximumContext.Suffix != PpmContext.Zero) + while (_maximumContext.Suffix != PpmContext.ZERO) { - maximumContext = maximumContext.Suffix; + _maximumContext = _maximumContext.Suffix; } - RemoveBinaryContexts(0, maximumContext); - method = method + 1; - Allocator.GlueCount = 0; - orderFall = modelOrder; + RemoveBinaryContexts(0, _maximumContext); + _method = _method + 1; + _allocator._glueCount = 0; + _orderFall = _modelOrder; } - else if (method == ModelRestorationMethod.Restart || - Allocator.GetMemoryUsed() < (Allocator.AllocatorSize >> 1)) + else if (_method == ModelRestorationMethod.Restart || + _allocator.GetMemoryUsed() < (_allocator._allocatorSize >> 1)) { - StartModel(modelOrder, method); - escapeCount = 0; + StartModel(_modelOrder, _method); + _escapeCount = 0; } else { - while (maximumContext.Suffix != PpmContext.Zero) + while (_maximumContext.Suffix != PpmContext.ZERO) { - maximumContext = maximumContext.Suffix; + _maximumContext = _maximumContext.Suffix; } do { - CutOff(0, maximumContext); - Allocator.ExpandText(); + CutOff(0, _maximumContext); + _allocator.ExpandText(); } - while (Allocator.GetMemoryUsed() > 3 * (Allocator.AllocatorSize >> 2)); + while (_allocator.GetMemoryUsed() > 3 * (_allocator._allocatorSize >> 2)); - Allocator.GlueCount = 0; - orderFall = modelOrder; + _allocator._glueCount = 0; + _orderFall = _modelOrder; } } @@ -914,8 +914,8 @@ namespace SharpCompress.Compressors.PPMd.I1 private void ClearMask() { - escapeCount = 1; - Array.Clear(characterMask, 0, characterMask.Length); + _escapeCount = 1; + Array.Clear(_characterMask, 0, _characterMask.Length); } #endregion diff --git a/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs b/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs index d868540a..e95574e7 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/Pointer.cs @@ -7,7 +7,7 @@ using System; namespace SharpCompress.Compressors.PPMd.I1 { /// - /// A structure containing a single address representing a position in the array. This + /// A structure containing a single address representing a position in the array. This /// is intended to mimic the behaviour of a pointer in C/C++. /// /// @@ -17,23 +17,23 @@ namespace SharpCompress.Compressors.PPMd.I1 /// instance rather than just copying a reference to the same instance). /// /// - /// Note that is a field rather than a property for performance reasons. + /// Note that is a field rather than a property for performance reasons. /// /// internal struct Pointer { - public uint Address; - public byte[] Memory; - public static readonly Pointer Zero = new Pointer(0, null); - public const int Size = 1; + public uint _address; + public byte[] _memory; + public static readonly Pointer ZERO = new Pointer(0, null); + public const int SIZE = 1; /// /// Initializes a new instance of the structure. /// public Pointer(uint address, byte[] memory) { - Address = address; - Memory = memory; + _address = address; + _memory = memory; } /// @@ -46,22 +46,22 @@ namespace SharpCompress.Compressors.PPMd.I1 get { #if DEBUG - if (Address == 0) + if (_address == 0) { throw new InvalidOperationException("The pointer being indexed is a null pointer."); } #endif - return Memory[Address + offset]; + return _memory[_address + offset]; } set { #if DEBUG - if (Address == 0) + if (_address == 0) { throw new InvalidOperationException("The pointer being indexed is a null pointer."); } #endif - Memory[Address + offset] = value; + _memory[_address + offset] = value; } } @@ -72,7 +72,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static implicit operator Pointer(MemoryNode memoryNode) { - return new Pointer(memoryNode.Address, memoryNode.Memory); + return new Pointer(memoryNode._address, memoryNode._memory); } /// @@ -82,7 +82,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static implicit operator Pointer(Model.PpmContext context) { - return new Pointer(context.Address, context.Memory); + return new Pointer(context._address, context._memory); } /// @@ -92,7 +92,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static implicit operator Pointer(PpmState state) { - return new Pointer(state.Address, state.Memory); + return new Pointer(state._address, state._memory); } /// @@ -104,12 +104,12 @@ namespace SharpCompress.Compressors.PPMd.I1 public static Pointer operator +(Pointer pointer, int offset) { #if DEBUG - if (pointer.Address == 0) + if (pointer._address == 0) { throw new InvalidOperationException("The pointer is a null pointer."); } #endif - pointer.Address = (uint)(pointer.Address + offset); + pointer._address = (uint)(pointer._address + offset); return pointer; } @@ -122,12 +122,12 @@ namespace SharpCompress.Compressors.PPMd.I1 public static Pointer operator +(Pointer pointer, uint offset) { #if DEBUG - if (pointer.Address == 0) + if (pointer._address == 0) { throw new InvalidOperationException("The pointer is a null pointer."); } #endif - pointer.Address += offset; + pointer._address += offset; return pointer; } @@ -139,12 +139,12 @@ namespace SharpCompress.Compressors.PPMd.I1 public static Pointer operator ++(Pointer pointer) { #if DEBUG - if (pointer.Address == 0) + if (pointer._address == 0) { throw new InvalidOperationException("The pointer being incremented is a null pointer."); } #endif - pointer.Address++; + pointer._address++; return pointer; } @@ -157,12 +157,12 @@ namespace SharpCompress.Compressors.PPMd.I1 public static Pointer operator -(Pointer pointer, int offset) { #if DEBUG - if (pointer.Address == 0) + if (pointer._address == 0) { throw new InvalidOperationException("The pointer is a null pointer."); } #endif - pointer.Address = (uint)(pointer.Address - offset); + pointer._address = (uint)(pointer._address - offset); return pointer; } @@ -175,12 +175,12 @@ namespace SharpCompress.Compressors.PPMd.I1 public static Pointer operator -(Pointer pointer, uint offset) { #if DEBUG - if (pointer.Address == 0) + if (pointer._address == 0) { throw new InvalidOperationException("The pointer is a null pointer."); } #endif - pointer.Address -= offset; + pointer._address -= offset; return pointer; } @@ -192,12 +192,12 @@ namespace SharpCompress.Compressors.PPMd.I1 public static Pointer operator --(Pointer pointer) { #if DEBUG - if (pointer.Address == 0) + if (pointer._address == 0) { throw new InvalidOperationException("The pointer being decremented is a null pointer."); } #endif - pointer.Address--; + pointer._address--; return pointer; } @@ -210,18 +210,18 @@ namespace SharpCompress.Compressors.PPMd.I1 public static uint operator -(Pointer pointer1, Pointer pointer2) { #if DEBUG - if (pointer1.Address == 0) + if (pointer1._address == 0) { throw new InvalidOperationException( "The pointer to the left of the subtraction operator is a null pointer."); } - if (pointer2.Address == 0) + if (pointer2._address == 0) { throw new InvalidOperationException( "The pointer to the right of the subtraction operator is a null pointer."); } #endif - return pointer1.Address - pointer2.Address; + return pointer1._address - pointer2._address; } /// @@ -233,18 +233,18 @@ namespace SharpCompress.Compressors.PPMd.I1 public static bool operator <(Pointer pointer1, Pointer pointer2) { #if DEBUG - if (pointer1.Address == 0) + if (pointer1._address == 0) { throw new InvalidOperationException( "The pointer to the left of the less than operator is a null pointer."); } - if (pointer2.Address == 0) + if (pointer2._address == 0) { throw new InvalidOperationException( "The pointer to the right of the less than operator is a null pointer."); } #endif - return pointer1.Address < pointer2.Address; + return pointer1._address < pointer2._address; } /// @@ -256,18 +256,18 @@ namespace SharpCompress.Compressors.PPMd.I1 public static bool operator <=(Pointer pointer1, Pointer pointer2) { #if DEBUG - if (pointer1.Address == 0) + if (pointer1._address == 0) { throw new InvalidOperationException( "The pointer to the left of the less than or equal to operator is a null pointer."); } - if (pointer2.Address == 0) + if (pointer2._address == 0) { throw new InvalidOperationException( "The pointer to the right of the less than or equal to operator is a null pointer."); } #endif - return pointer1.Address <= pointer2.Address; + return pointer1._address <= pointer2._address; } /// @@ -279,18 +279,18 @@ namespace SharpCompress.Compressors.PPMd.I1 public static bool operator >(Pointer pointer1, Pointer pointer2) { #if DEBUG - if (pointer1.Address == 0) + if (pointer1._address == 0) { throw new InvalidOperationException( "The pointer to the left of the greater than operator is a null pointer."); } - if (pointer2.Address == 0) + if (pointer2._address == 0) { throw new InvalidOperationException( "The pointer to the right of the greater than operator is a null pointer."); } #endif - return pointer1.Address > pointer2.Address; + return pointer1._address > pointer2._address; } /// @@ -302,18 +302,18 @@ namespace SharpCompress.Compressors.PPMd.I1 public static bool operator >=(Pointer pointer1, Pointer pointer2) { #if DEBUG - if (pointer1.Address == 0) + if (pointer1._address == 0) { throw new InvalidOperationException( "The pointer to the left of the greater than or equal to operator is a null pointer."); } - if (pointer2.Address == 0) + if (pointer2._address == 0) { throw new InvalidOperationException( "The pointer to the right of the greater than or equal to operator is a null pointer."); } #endif - return pointer1.Address >= pointer2.Address; + return pointer1._address >= pointer2._address; } /// @@ -324,7 +324,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator ==(Pointer pointer1, Pointer pointer2) { - return pointer1.Address == pointer2.Address; + return pointer1._address == pointer2._address; } /// @@ -335,7 +335,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator !=(Pointer pointer1, Pointer pointer2) { - return pointer1.Address != pointer2.Address; + return pointer1._address != pointer2._address; } /// @@ -348,7 +348,7 @@ namespace SharpCompress.Compressors.PPMd.I1 if (obj is Pointer) { Pointer pointer = (Pointer)obj; - return pointer.Address == Address; + return pointer._address == _address; } return base.Equals(obj); } @@ -359,7 +359,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// A 32-bit signed integer that is the hash code for this instance. public override int GetHashCode() { - return Address.GetHashCode(); + return _address.GetHashCode(); } } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs b/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs index 371385fe..6ec1a7b8 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/PpmContext.cs @@ -17,40 +17,40 @@ namespace SharpCompress.Compressors.PPMd.I1 /// internal struct PpmContext { - public uint Address; - public byte[] Memory; - public static readonly PpmContext Zero = new PpmContext(0, null); - public const int Size = 12; + public uint _address; + public byte[] _memory; + public static readonly PpmContext ZERO = new PpmContext(0, null); + public const int SIZE = 12; /// /// Initializes a new instance of the structure. /// public PpmContext(uint address, byte[] memory) { - Address = address; - Memory = memory; + _address = address; + _memory = memory; } /// /// Gets or sets the number statistics. /// - public byte NumberStatistics { get => Memory[Address]; set => Memory[Address] = value; } + public byte NumberStatistics { get => _memory[_address]; set => _memory[_address] = value; } /// /// Gets or sets the flags. /// - public byte Flags { get => Memory[Address + 1]; set => Memory[Address + 1] = value; } + public byte Flags { get => _memory[_address + 1]; set => _memory[_address + 1] = value; } /// /// Gets or sets the summary frequency. /// public ushort SummaryFrequency { - get => (ushort)(Memory[Address + 2] | Memory[Address + 3] << 8); + get => (ushort)(_memory[_address + 2] | _memory[_address + 3] << 8); set { - Memory[Address + 2] = (byte)value; - Memory[Address + 3] = (byte)(value >> 8); + _memory[_address + 2] = (byte)value; + _memory[_address + 3] = (byte)(value >> 8); } } @@ -60,14 +60,14 @@ namespace SharpCompress.Compressors.PPMd.I1 public PpmState Statistics { get => new PpmState( - Memory[Address + 4] | ((uint)Memory[Address + 5]) << 8 | - ((uint)Memory[Address + 6]) << 16 | ((uint)Memory[Address + 7]) << 24, Memory); + _memory[_address + 4] | ((uint)_memory[_address + 5]) << 8 | + ((uint)_memory[_address + 6]) << 16 | ((uint)_memory[_address + 7]) << 24, _memory); set { - Memory[Address + 4] = (byte)value.Address; - Memory[Address + 5] = (byte)(value.Address >> 8); - Memory[Address + 6] = (byte)(value.Address >> 16); - Memory[Address + 7] = (byte)(value.Address >> 24); + _memory[_address + 4] = (byte)value._address; + _memory[_address + 5] = (byte)(value._address >> 8); + _memory[_address + 6] = (byte)(value._address >> 16); + _memory[_address + 7] = (byte)(value._address >> 24); } } @@ -77,14 +77,14 @@ namespace SharpCompress.Compressors.PPMd.I1 public PpmContext Suffix { get => new PpmContext( - Memory[Address + 8] | ((uint)Memory[Address + 9]) << 8 | - ((uint)Memory[Address + 10]) << 16 | ((uint)Memory[Address + 11]) << 24, Memory); + _memory[_address + 8] | ((uint)_memory[_address + 9]) << 8 | + ((uint)_memory[_address + 10]) << 16 | ((uint)_memory[_address + 11]) << 24, _memory); set { - Memory[Address + 8] = (byte)value.Address; - Memory[Address + 9] = (byte)(value.Address >> 8); - Memory[Address + 10] = (byte)(value.Address >> 16); - Memory[Address + 11] = (byte)(value.Address >> 24); + _memory[_address + 8] = (byte)value._address; + _memory[_address + 9] = (byte)(value._address >> 8); + _memory[_address + 10] = (byte)(value._address >> 16); + _memory[_address + 11] = (byte)(value._address >> 24); } } @@ -113,21 +113,21 @@ namespace SharpCompress.Compressors.PPMd.I1 /// /// /// - public PpmState FirstState => new PpmState(Address + 2, Memory); + public PpmState FirstState => new PpmState(_address + 2, _memory); /// /// Gets or sets the symbol of the first PPM state. This is provided for convenience. The same /// information can be obtained using the Symbol property on the PPM state provided by the /// property. /// - public byte FirstStateSymbol { get => Memory[Address + 2]; set => Memory[Address + 2] = value; } + public byte FirstStateSymbol { get => _memory[_address + 2]; set => _memory[_address + 2] = value; } /// /// Gets or sets the frequency of the first PPM state. This is provided for convenience. The same /// information can be obtained using the Frequency property on the PPM state provided by the ///context.FirstState property. /// - public byte FirstStateFrequency { get => Memory[Address + 3]; set => Memory[Address + 3] = value; } + public byte FirstStateFrequency { get => _memory[_address + 3]; set => _memory[_address + 3] = value; } /// /// Gets or sets the successor of the first PPM state. This is provided for convenience. The same @@ -136,14 +136,14 @@ namespace SharpCompress.Compressors.PPMd.I1 public PpmContext FirstStateSuccessor { get => new PpmContext( - Memory[Address + 4] | ((uint)Memory[Address + 5]) << 8 | - ((uint)Memory[Address + 6]) << 16 | ((uint)Memory[Address + 7]) << 24, Memory); + _memory[_address + 4] | ((uint)_memory[_address + 5]) << 8 | + ((uint)_memory[_address + 6]) << 16 | ((uint)_memory[_address + 7]) << 24, _memory); set { - Memory[Address + 4] = (byte)value.Address; - Memory[Address + 5] = (byte)(value.Address >> 8); - Memory[Address + 6] = (byte)(value.Address >> 16); - Memory[Address + 7] = (byte)(value.Address >> 24); + _memory[_address + 4] = (byte)value._address; + _memory[_address + 5] = (byte)(value._address >> 8); + _memory[_address + 6] = (byte)(value._address >> 16); + _memory[_address + 7] = (byte)(value._address >> 24); } } @@ -154,7 +154,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static implicit operator PpmContext(Pointer pointer) { - return new PpmContext(pointer.Address, pointer.Memory); + return new PpmContext(pointer._address, pointer._memory); } /// @@ -165,7 +165,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static PpmContext operator +(PpmContext context, int offset) { - context.Address = (uint)(context.Address + offset * Size); + context._address = (uint)(context._address + offset * SIZE); return context; } @@ -177,7 +177,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static PpmContext operator -(PpmContext context, int offset) { - context.Address = (uint)(context.Address - offset * Size); + context._address = (uint)(context._address - offset * SIZE); return context; } @@ -189,7 +189,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator <=(PpmContext context1, PpmContext context2) { - return context1.Address <= context2.Address; + return context1._address <= context2._address; } /// @@ -200,7 +200,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator >=(PpmContext context1, PpmContext context2) { - return context1.Address >= context2.Address; + return context1._address >= context2._address; } /// @@ -211,7 +211,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator ==(PpmContext context1, PpmContext context2) { - return context1.Address == context2.Address; + return context1._address == context2._address; } /// @@ -222,7 +222,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator !=(PpmContext context1, PpmContext context2) { - return context1.Address != context2.Address; + return context1._address != context2._address; } /// @@ -235,7 +235,7 @@ namespace SharpCompress.Compressors.PPMd.I1 if (obj is PpmContext) { PpmContext context = (PpmContext)obj; - return context.Address == Address; + return context._address == _address; } return base.Equals(obj); } @@ -246,38 +246,38 @@ namespace SharpCompress.Compressors.PPMd.I1 /// A 32-bit signed integer that is the hash code for this instance. public override int GetHashCode() { - return Address.GetHashCode(); + return _address.GetHashCode(); } } private void EncodeBinarySymbol(int symbol, PpmContext context) { PpmState state = context.FirstState; - int index1 = probabilities[state.Frequency - 1]; - int index2 = numberStatisticsToBinarySummaryIndex[context.Suffix.NumberStatistics] + previousSuccess + - context.Flags + ((runLength >> 26) & 0x20); + int index1 = _probabilities[state.Frequency - 1]; + int index2 = _numberStatisticsToBinarySummaryIndex[context.Suffix.NumberStatistics] + _previousSuccess + + context.Flags + ((_runLength >> 26) & 0x20); if (state.Symbol == symbol) { - foundState = state; + _foundState = state; state.Frequency += (byte)((state.Frequency < 196) ? 1 : 0); - Coder.LowCount = 0; - Coder.HighCount = binarySummary[index1, index2]; - binarySummary[index1, index2] += - (ushort)(Interval - Mean(binarySummary[index1, index2], PeriodBitCount, 2)); - previousSuccess = 1; - runLength++; + _coder._lowCount = 0; + _coder._highCount = _binarySummary[index1, index2]; + _binarySummary[index1, index2] += + (ushort)(INTERVAL - Mean(_binarySummary[index1, index2], PERIOD_BIT_COUNT, 2)); + _previousSuccess = 1; + _runLength++; } else { - Coder.LowCount = binarySummary[index1, index2]; - binarySummary[index1, index2] -= (ushort)Mean(binarySummary[index1, index2], PeriodBitCount, 2); - Coder.HighCount = BinaryScale; - initialEscape = ExponentialEscapes[binarySummary[index1, index2] >> 10]; - characterMask[state.Symbol] = escapeCount; - previousSuccess = 0; - numberMasked = 0; - foundState = PpmState.Zero; + _coder._lowCount = _binarySummary[index1, index2]; + _binarySummary[index1, index2] -= (ushort)Mean(_binarySummary[index1, index2], PERIOD_BIT_COUNT, 2); + _coder._highCount = BINARY_SCALE; + _initialEscape = EXPONENTIAL_ESCAPES[_binarySummary[index1, index2] >> 10]; + _characterMask[state.Symbol] = _escapeCount; + _previousSuccess = 0; + _numberMasked = 0; + _foundState = PpmState.ZERO; } } @@ -286,46 +286,46 @@ namespace SharpCompress.Compressors.PPMd.I1 uint lowCount; uint index = context.Statistics.Symbol; PpmState state = context.Statistics; - Coder.Scale = context.SummaryFrequency; + _coder._scale = context.SummaryFrequency; if (index == symbol) { - Coder.HighCount = state.Frequency; - previousSuccess = (byte)((2 * Coder.HighCount >= Coder.Scale) ? 1 : 0); - foundState = state; - foundState.Frequency += 4; + _coder._highCount = state.Frequency; + _previousSuccess = (byte)((2 * _coder._highCount >= _coder._scale) ? 1 : 0); + _foundState = state; + _foundState.Frequency += 4; context.SummaryFrequency += 4; - runLength += previousSuccess; - if (state.Frequency > MaximumFrequency) + _runLength += _previousSuccess; + if (state.Frequency > MAXIMUM_FREQUENCY) { Rescale(context); } - Coder.LowCount = 0; + _coder._lowCount = 0; return; } lowCount = state.Frequency; index = context.NumberStatistics; - previousSuccess = 0; + _previousSuccess = 0; while ((++state).Symbol != symbol) { lowCount += state.Frequency; if (--index == 0) { - Coder.LowCount = lowCount; - characterMask[state.Symbol] = escapeCount; - numberMasked = context.NumberStatistics; + _coder._lowCount = lowCount; + _characterMask[state.Symbol] = _escapeCount; + _numberMasked = context.NumberStatistics; index = context.NumberStatistics; - foundState = PpmState.Zero; + _foundState = PpmState.ZERO; do { - characterMask[(--state).Symbol] = escapeCount; + _characterMask[(--state).Symbol] = _escapeCount; } while (--index != 0); - Coder.HighCount = Coder.Scale; + _coder._highCount = _coder._scale; return; } } - Coder.HighCount = (Coder.LowCount = lowCount) + state.Frequency; + _coder._highCount = (_coder._lowCount = lowCount) + state.Frequency; Update1(state, context); } @@ -334,7 +334,7 @@ namespace SharpCompress.Compressors.PPMd.I1 See2Context see2Context = MakeEscapeFrequency(context); uint currentSymbol; uint lowCount = 0; - uint index = (uint)(context.NumberStatistics - numberMasked); + uint index = (uint)(context.NumberStatistics - _numberMasked); PpmState state = context.Statistics - 1; do @@ -344,8 +344,8 @@ namespace SharpCompress.Compressors.PPMd.I1 currentSymbol = state[1].Symbol; state++; } - while (characterMask[currentSymbol] == escapeCount); - characterMask[currentSymbol] = escapeCount; + while (_characterMask[currentSymbol] == _escapeCount); + _characterMask[currentSymbol] = _escapeCount; if (currentSymbol == symbol) { goto SymbolFound; @@ -354,17 +354,17 @@ namespace SharpCompress.Compressors.PPMd.I1 } while (--index != 0); - Coder.LowCount = lowCount; - Coder.Scale += Coder.LowCount; - Coder.HighCount = Coder.Scale; - see2Context.Summary += (ushort)Coder.Scale; - numberMasked = context.NumberStatistics; + _coder._lowCount = lowCount; + _coder._scale += _coder._lowCount; + _coder._highCount = _coder._scale; + see2Context._summary += (ushort)_coder._scale; + _numberMasked = context.NumberStatistics; return; SymbolFound: - Coder.LowCount = lowCount; + _coder._lowCount = lowCount; lowCount += state.Frequency; - Coder.HighCount = lowCount; + _coder._highCount = lowCount; for (PpmState p1 = state; --index != 0;) { do @@ -372,10 +372,10 @@ namespace SharpCompress.Compressors.PPMd.I1 currentSymbol = p1[1].Symbol; p1++; } - while (characterMask[currentSymbol] == escapeCount); + while (_characterMask[currentSymbol] == _escapeCount); lowCount += p1.Frequency; } - Coder.Scale += lowCount; + _coder._scale += lowCount; see2Context.Update(); Update2(state, context); } @@ -383,31 +383,31 @@ namespace SharpCompress.Compressors.PPMd.I1 private void DecodeBinarySymbol(PpmContext context) { PpmState state = context.FirstState; - int index1 = probabilities[state.Frequency - 1]; - int index2 = numberStatisticsToBinarySummaryIndex[context.Suffix.NumberStatistics] + previousSuccess + - context.Flags + ((runLength >> 26) & 0x20); + int index1 = _probabilities[state.Frequency - 1]; + int index2 = _numberStatisticsToBinarySummaryIndex[context.Suffix.NumberStatistics] + _previousSuccess + + context.Flags + ((_runLength >> 26) & 0x20); - if (Coder.RangeGetCurrentShiftCount(TotalBitCount) < binarySummary[index1, index2]) + if (_coder.RangeGetCurrentShiftCount(TOTAL_BIT_COUNT) < _binarySummary[index1, index2]) { - foundState = state; + _foundState = state; state.Frequency += (byte)((state.Frequency < 196) ? 1 : 0); - Coder.LowCount = 0; - Coder.HighCount = binarySummary[index1, index2]; - binarySummary[index1, index2] += - (ushort)(Interval - Mean(binarySummary[index1, index2], PeriodBitCount, 2)); - previousSuccess = 1; - runLength++; + _coder._lowCount = 0; + _coder._highCount = _binarySummary[index1, index2]; + _binarySummary[index1, index2] += + (ushort)(INTERVAL - Mean(_binarySummary[index1, index2], PERIOD_BIT_COUNT, 2)); + _previousSuccess = 1; + _runLength++; } else { - Coder.LowCount = binarySummary[index1, index2]; - binarySummary[index1, index2] -= (ushort)Mean(binarySummary[index1, index2], PeriodBitCount, 2); - Coder.HighCount = BinaryScale; - initialEscape = ExponentialEscapes[binarySummary[index1, index2] >> 10]; - characterMask[state.Symbol] = escapeCount; - previousSuccess = 0; - numberMasked = 0; - foundState = PpmState.Zero; + _coder._lowCount = _binarySummary[index1, index2]; + _binarySummary[index1, index2] -= (ushort)Mean(_binarySummary[index1, index2], PERIOD_BIT_COUNT, 2); + _coder._highCount = BINARY_SCALE; + _initialEscape = EXPONENTIAL_ESCAPES[_binarySummary[index1, index2] >> 10]; + _characterMask[state.Symbol] = _escapeCount; + _previousSuccess = 0; + _numberMasked = 0; + _foundState = PpmState.ZERO; } } @@ -417,48 +417,48 @@ namespace SharpCompress.Compressors.PPMd.I1 uint count; uint highCount = context.Statistics.Frequency; PpmState state = context.Statistics; - Coder.Scale = context.SummaryFrequency; + _coder._scale = context.SummaryFrequency; - count = Coder.RangeGetCurrentCount(); + count = _coder.RangeGetCurrentCount(); if (count < highCount) { - Coder.HighCount = highCount; - previousSuccess = (byte)((2 * Coder.HighCount >= Coder.Scale) ? 1 : 0); - foundState = state; + _coder._highCount = highCount; + _previousSuccess = (byte)((2 * _coder._highCount >= _coder._scale) ? 1 : 0); + _foundState = state; highCount += 4; - foundState.Frequency = (byte)highCount; + _foundState.Frequency = (byte)highCount; context.SummaryFrequency += 4; - runLength += previousSuccess; - if (highCount > MaximumFrequency) + _runLength += _previousSuccess; + if (highCount > MAXIMUM_FREQUENCY) { Rescale(context); } - Coder.LowCount = 0; + _coder._lowCount = 0; return; } index = context.NumberStatistics; - previousSuccess = 0; + _previousSuccess = 0; while ((highCount += (++state).Frequency) <= count) { if (--index == 0) { - Coder.LowCount = highCount; - characterMask[state.Symbol] = escapeCount; - numberMasked = context.NumberStatistics; + _coder._lowCount = highCount; + _characterMask[state.Symbol] = _escapeCount; + _numberMasked = context.NumberStatistics; index = context.NumberStatistics; - foundState = PpmState.Zero; + _foundState = PpmState.ZERO; do { - characterMask[(--state).Symbol] = escapeCount; + _characterMask[(--state).Symbol] = _escapeCount; } while (--index != 0); - Coder.HighCount = Coder.Scale; + _coder._highCount = _coder._scale; return; } } - Coder.HighCount = highCount; - Coder.LowCount = Coder.HighCount - state.Frequency; + _coder._highCount = highCount; + _coder._lowCount = _coder._highCount - state.Frequency; Update1(state, context); } @@ -468,7 +468,7 @@ namespace SharpCompress.Compressors.PPMd.I1 uint currentSymbol; uint count; uint highCount = 0; - uint index = (uint)(context.NumberStatistics - numberMasked); + uint index = (uint)(context.NumberStatistics - _numberMasked); uint stateIndex = 0; PpmState state = context.Statistics - 1; @@ -479,56 +479,56 @@ namespace SharpCompress.Compressors.PPMd.I1 currentSymbol = state[1].Symbol; state++; } - while (characterMask[currentSymbol] == escapeCount); + while (_characterMask[currentSymbol] == _escapeCount); highCount += state.Frequency; - decodeStates[stateIndex++] = state; + _decodeStates[stateIndex++] = state; // note that decodeStates is a static array that is re-used on each call to this method (for performance reasons) } while (--index != 0); - Coder.Scale += highCount; - count = Coder.RangeGetCurrentCount(); + _coder._scale += highCount; + count = _coder.RangeGetCurrentCount(); stateIndex = 0; - state = decodeStates[stateIndex]; + state = _decodeStates[stateIndex]; if (count < highCount) { highCount = 0; while ((highCount += state.Frequency) <= count) { - state = decodeStates[++stateIndex]; + state = _decodeStates[++stateIndex]; } - Coder.HighCount = highCount; - Coder.LowCount = Coder.HighCount - state.Frequency; + _coder._highCount = highCount; + _coder._lowCount = _coder._highCount - state.Frequency; see2Context.Update(); Update2(state, context); } else { - Coder.LowCount = highCount; - Coder.HighCount = Coder.Scale; - index = (uint)(context.NumberStatistics - numberMasked); - numberMasked = context.NumberStatistics; + _coder._lowCount = highCount; + _coder._highCount = _coder._scale; + index = (uint)(context.NumberStatistics - _numberMasked); + _numberMasked = context.NumberStatistics; do { - characterMask[decodeStates[stateIndex].Symbol] = escapeCount; + _characterMask[_decodeStates[stateIndex].Symbol] = _escapeCount; stateIndex++; } while (--index != 0); - see2Context.Summary += (ushort)Coder.Scale; + see2Context._summary += (ushort)_coder._scale; } } private void Update1(PpmState state, PpmContext context) { - foundState = state; - foundState.Frequency += 4; + _foundState = state; + _foundState.Frequency += 4; context.SummaryFrequency += 4; if (state[0].Frequency > state[-1].Frequency) { Swap(state[0], state[-1]); - foundState = --state; - if (state.Frequency > MaximumFrequency) + _foundState = --state; + if (state.Frequency > MAXIMUM_FREQUENCY) { Rescale(context); } @@ -537,15 +537,15 @@ namespace SharpCompress.Compressors.PPMd.I1 private void Update2(PpmState state, PpmContext context) { - foundState = state; - foundState.Frequency += 4; + _foundState = state; + _foundState.Frequency += 4; context.SummaryFrequency += 4; - if (state.Frequency > MaximumFrequency) + if (state.Frequency > MAXIMUM_FREQUENCY) { Rescale(context); } - escapeCount++; - runLength = initialRunLength; + _escapeCount++; + _runLength = _initialRunLength; } private See2Context MakeEscapeFrequency(PpmContext context) @@ -559,16 +559,16 @@ namespace SharpCompress.Compressors.PPMd.I1 // dimension of the see2Contexts array is always in the range 0 .. 31). numberStatistics = context.Suffix.NumberStatistics; - int index1 = probabilities[context.NumberStatistics + 2] - 3; + int index1 = _probabilities[context.NumberStatistics + 2] - 3; int index2 = ((context.SummaryFrequency > 11 * (context.NumberStatistics + 1)) ? 1 : 0) + - ((2 * context.NumberStatistics < numberStatistics + numberMasked) ? 2 : 0) + context.Flags; - see2Context = see2Contexts[index1, index2]; - Coder.Scale = see2Context.Mean(); + ((2 * context.NumberStatistics < numberStatistics + _numberMasked) ? 2 : 0) + context.Flags; + see2Context = _see2Contexts[index1, index2]; + _coder._scale = see2Context.Mean(); } else { - see2Context = emptySee2Context; - Coder.Scale = 1; + see2Context = _emptySee2Context; + _coder._scale = 1; } return see2Context; @@ -587,7 +587,7 @@ namespace SharpCompress.Compressors.PPMd.I1 PpmState p1; PpmState state; - for (state = foundState; state != context.Statistics; state--) + for (state = _foundState; state != context.Statistics; state--) { Swap(state[0], state[-1]); } @@ -595,7 +595,7 @@ namespace SharpCompress.Compressors.PPMd.I1 state.Frequency += 4; context.SummaryFrequency += 4; escapeFrequency = (uint)(context.SummaryFrequency - state.Frequency); - adder = (orderFall != 0 || method > ModelRestorationMethod.Freeze) ? 1 : 0; + adder = (_orderFall != 0 || _method > ModelRestorationMethod.Freeze) ? 1 : 0; state.Frequency = (byte)((state.Frequency + adder) >> 1); context.SummaryFrequency = state.Frequency; @@ -639,20 +639,20 @@ namespace SharpCompress.Compressors.PPMd.I1 localFrequency = context.Statistics.Frequency; localSuccessor = context.Statistics.Successor; localFrequency = (byte)((2 * localFrequency + escapeFrequency - 1) / escapeFrequency); - if (localFrequency > MaximumFrequency / 3) + if (localFrequency > MAXIMUM_FREQUENCY / 3) { - localFrequency = (byte)(MaximumFrequency / 3); + localFrequency = (byte)(MAXIMUM_FREQUENCY / 3); } - Allocator.FreeUnits(context.Statistics, oldUnitCount); + _allocator.FreeUnits(context.Statistics, oldUnitCount); context.FirstStateSymbol = localSymbol; context.FirstStateFrequency = localFrequency; context.FirstStateSuccessor = localSuccessor; context.Flags = (byte)((context.Flags & 0x10) + ((localSymbol >= 0x40) ? 0x08 : 0x00)); - foundState = context.FirstState; + _foundState = context.FirstState; return; } - context.Statistics = Allocator.ShrinkUnits(context.Statistics, oldUnitCount, + context.Statistics = _allocator.ShrinkUnits(context.Statistics, oldUnitCount, (uint)((context.NumberStatistics + 2) >> 1)); context.Flags &= 0xf7; index = context.NumberStatistics; @@ -668,7 +668,7 @@ namespace SharpCompress.Compressors.PPMd.I1 escapeFrequency -= (escapeFrequency >> 1); context.SummaryFrequency += (ushort)escapeFrequency; context.Flags |= 0x04; - foundState = context.Statistics; + _foundState = context.Statistics; } private void Refresh(uint oldUnitCount, bool scale, PpmContext context) @@ -677,7 +677,7 @@ namespace SharpCompress.Compressors.PPMd.I1 int escapeFrequency; int scaleValue = (scale ? 1 : 0); - context.Statistics = Allocator.ShrinkUnits(context.Statistics, oldUnitCount, (uint)((index + 2) >> 1)); + context.Statistics = _allocator.ShrinkUnits(context.Statistics, oldUnitCount, (uint)((index + 2) >> 1)); PpmState statistics = context.Statistics; context.Flags = (byte)((context.Flags & (0x10 + (scale ? 0x04 : 0x00))) + ((statistics.Symbol >= 0x40) ? 0x08 : 0x00)); @@ -706,46 +706,46 @@ namespace SharpCompress.Compressors.PPMd.I1 if (context.NumberStatistics == 0) { state = context.FirstState; - if ((Pointer)state.Successor >= Allocator.BaseUnit) + if ((Pointer)state.Successor >= _allocator._baseUnit) { - if (order < modelOrder) + if (order < _modelOrder) { state.Successor = CutOff(order + 1, state.Successor); } else { - state.Successor = PpmContext.Zero; + state.Successor = PpmContext.ZERO; } - if (state.Successor == PpmContext.Zero && order > OrderBound) + if (state.Successor == PpmContext.ZERO && order > ORDER_BOUND) { - Allocator.SpecialFreeUnits(context); - return PpmContext.Zero; + _allocator.SpecialFreeUnits(context); + return PpmContext.ZERO; } return context; } - Allocator.SpecialFreeUnits(context); - return PpmContext.Zero; + _allocator.SpecialFreeUnits(context); + return PpmContext.ZERO; } uint unitCount = (uint)((context.NumberStatistics + 2) >> 1); - context.Statistics = Allocator.MoveUnitsUp(context.Statistics, unitCount); + context.Statistics = _allocator.MoveUnitsUp(context.Statistics, unitCount); index = context.NumberStatistics; for (state = context.Statistics + index; state >= context.Statistics; state--) { - if (state.Successor < Allocator.BaseUnit) + if (state.Successor < _allocator._baseUnit) { - state.Successor = PpmContext.Zero; + state.Successor = PpmContext.ZERO; Swap(state, context.Statistics[index--]); } - else if (order < modelOrder) + else if (order < _modelOrder) { state.Successor = CutOff(order + 1, state.Successor); } else { - state.Successor = PpmContext.Zero; + state.Successor = PpmContext.ZERO; } } @@ -755,15 +755,15 @@ namespace SharpCompress.Compressors.PPMd.I1 state = context.Statistics; if (index < 0) { - Allocator.FreeUnits(state, unitCount); - Allocator.SpecialFreeUnits(context); - return PpmContext.Zero; + _allocator.FreeUnits(state, unitCount); + _allocator.SpecialFreeUnits(context); + return PpmContext.ZERO; } if (index == 0) { context.Flags = (byte)((context.Flags & 0x10) + ((state.Symbol >= 0x40) ? 0x08 : 0x00)); Copy(context.FirstState, state); - Allocator.FreeUnits(state, unitCount); + _allocator.FreeUnits(state, unitCount); context.FirstStateFrequency = (byte)((context.FirstStateFrequency + 11) >> 3); } else @@ -780,32 +780,32 @@ namespace SharpCompress.Compressors.PPMd.I1 if (context.NumberStatistics == 0) { PpmState state = context.FirstState; - if ((Pointer)state.Successor >= Allocator.BaseUnit && order < modelOrder) + if ((Pointer)state.Successor >= _allocator._baseUnit && order < _modelOrder) { state.Successor = RemoveBinaryContexts(order + 1, state.Successor); } else { - state.Successor = PpmContext.Zero; + state.Successor = PpmContext.ZERO; } - if ((state.Successor == PpmContext.Zero) && + if ((state.Successor == PpmContext.ZERO) && (context.Suffix.NumberStatistics == 0 || context.Suffix.Flags == 0xff)) { - Allocator.FreeUnits(context, 1); - return PpmContext.Zero; + _allocator.FreeUnits(context, 1); + return PpmContext.ZERO; } return context; } for (PpmState state = context.Statistics + context.NumberStatistics; state >= context.Statistics; state--) { - if ((Pointer)state.Successor >= Allocator.BaseUnit && order < modelOrder) + if ((Pointer)state.Successor >= _allocator._baseUnit && order < _modelOrder) { state.Successor = RemoveBinaryContexts(order + 1, state.Successor); } else { - state.Successor = PpmContext.Zero; + state.Successor = PpmContext.ZERO; } } diff --git a/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs b/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs index d6c2d39f..be5f752b 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/PpmState.cs @@ -16,34 +16,34 @@ namespace SharpCompress.Compressors.PPMd.I1 /// instance rather than just copying a reference to the same instance). /// /// - /// Note that is a field rather than a property for performance reasons. + /// Note that is a field rather than a property for performance reasons. /// /// internal struct PpmState { - public uint Address; - public byte[] Memory; - public static readonly PpmState Zero = new PpmState(0, null); - public const int Size = 6; + public uint _address; + public byte[] _memory; + public static readonly PpmState ZERO = new PpmState(0, null); + public const int SIZE = 6; /// /// Initializes a new instance of the structure. /// public PpmState(uint address, byte[] memory) { - Address = address; - Memory = memory; + _address = address; + _memory = memory; } /// /// Gets or sets the symbol. /// - public byte Symbol { get => Memory[Address]; set => Memory[Address] = value; } + public byte Symbol { get => _memory[_address]; set => _memory[_address] = value; } /// /// Gets or sets the frequency. /// - public byte Frequency { get => Memory[Address + 1]; set => Memory[Address + 1] = value; } + public byte Frequency { get => _memory[_address + 1]; set => _memory[_address + 1] = value; } /// /// Gets or sets the successor. @@ -51,14 +51,14 @@ namespace SharpCompress.Compressors.PPMd.I1 public Model.PpmContext Successor { get => new Model.PpmContext( - Memory[Address + 2] | ((uint)Memory[Address + 3]) << 8 | - ((uint)Memory[Address + 4]) << 16 | ((uint)Memory[Address + 5]) << 24, Memory); + _memory[_address + 2] | ((uint)_memory[_address + 3]) << 8 | + ((uint)_memory[_address + 4]) << 16 | ((uint)_memory[_address + 5]) << 24, _memory); set { - Memory[Address + 2] = (byte)value.Address; - Memory[Address + 3] = (byte)(value.Address >> 8); - Memory[Address + 4] = (byte)(value.Address >> 16); - Memory[Address + 5] = (byte)(value.Address >> 24); + _memory[_address + 2] = (byte)value._address; + _memory[_address + 3] = (byte)(value._address >> 8); + _memory[_address + 4] = (byte)(value._address >> 16); + _memory[_address + 5] = (byte)(value._address >> 24); } } @@ -68,7 +68,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// /// /// - public PpmState this[int offset] => new PpmState((uint)(Address + offset * Size), Memory); + public PpmState this[int offset] => new PpmState((uint)(_address + offset * SIZE), _memory); /// /// Allow a pointer to be implicitly converted to a PPM state. @@ -77,7 +77,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static implicit operator PpmState(Pointer pointer) { - return new PpmState(pointer.Address, pointer.Memory); + return new PpmState(pointer._address, pointer._memory); } /// @@ -88,7 +88,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static PpmState operator +(PpmState state, int offset) { - state.Address = (uint)(state.Address + offset * Size); + state._address = (uint)(state._address + offset * SIZE); return state; } @@ -99,7 +99,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static PpmState operator ++(PpmState state) { - state.Address += Size; + state._address += SIZE; return state; } @@ -111,7 +111,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static PpmState operator -(PpmState state, int offset) { - state.Address = (uint)(state.Address - offset * Size); + state._address = (uint)(state._address - offset * SIZE); return state; } @@ -122,7 +122,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static PpmState operator --(PpmState state) { - state.Address -= Size; + state._address -= SIZE; return state; } @@ -134,7 +134,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator <=(PpmState state1, PpmState state2) { - return state1.Address <= state2.Address; + return state1._address <= state2._address; } /// @@ -145,7 +145,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator >=(PpmState state1, PpmState state2) { - return state1.Address >= state2.Address; + return state1._address >= state2._address; } /// @@ -156,7 +156,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator ==(PpmState state1, PpmState state2) { - return state1.Address == state2.Address; + return state1._address == state2._address; } /// @@ -167,7 +167,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// public static bool operator !=(PpmState state1, PpmState state2) { - return state1.Address != state2.Address; + return state1._address != state2._address; } /// @@ -180,7 +180,7 @@ namespace SharpCompress.Compressors.PPMd.I1 if (obj is PpmState) { PpmState state = (PpmState)obj; - return state.Address == Address; + return state._address == _address; } return base.Equals(obj); } @@ -191,7 +191,7 @@ namespace SharpCompress.Compressors.PPMd.I1 /// A 32-bit signed integer that is the hash code for this instance. public override int GetHashCode() { - return Address.GetHashCode(); + return _address.GetHashCode(); } } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/PPMd/I1/See2Context.cs b/src/SharpCompress/Compressors/PPMd/I1/See2Context.cs index cc59a123..3029d85b 100644 --- a/src/SharpCompress/Compressors/PPMd/I1/See2Context.cs +++ b/src/SharpCompress/Compressors/PPMd/I1/See2Context.cs @@ -18,37 +18,37 @@ namespace SharpCompress.Compressors.PPMd.I1 /// /// /// Note that in most cases fields are used rather than properties for performance reasons (for example, - /// is a field rather than a property). + /// is a field rather than a property). /// /// internal class See2Context { - private const byte PeriodBitCount = 7; + private const byte PERIOD_BIT_COUNT = 7; - public ushort Summary; - public byte Shift; - public byte Count; + public ushort _summary; + public byte _shift; + public byte _count; public void Initialize(uint initialValue) { - Shift = PeriodBitCount - 4; - Summary = (ushort)(initialValue << Shift); - Count = 7; + _shift = PERIOD_BIT_COUNT - 4; + _summary = (ushort)(initialValue << _shift); + _count = 7; } public uint Mean() { - uint value = (uint)(Summary >> Shift); - Summary = (ushort)(Summary - value); + uint value = (uint)(_summary >> _shift); + _summary = (ushort)(_summary - value); return (uint)(value + ((value == 0) ? 1 : 0)); } public void Update() { - if (Shift < PeriodBitCount && --Count == 0) + if (_shift < PERIOD_BIT_COUNT && --_count == 0) { - Summary += Summary; - Count = (byte)(3 << Shift++); + _summary += _summary; + _count = (byte)(3 << _shift++); } } } diff --git a/src/SharpCompress/Compressors/PPMd/PpmdProperties.cs b/src/SharpCompress/Compressors/PPMd/PpmdProperties.cs index bd8b43bc..2ad8d4d9 100644 --- a/src/SharpCompress/Compressors/PPMd/PpmdProperties.cs +++ b/src/SharpCompress/Compressors/PPMd/PpmdProperties.cs @@ -5,12 +5,9 @@ namespace SharpCompress.Compressors.PPMd { public class PpmdProperties { - public PpmdVersion Version = PpmdVersion.I1; - public int ModelOrder; - internal ModelRestorationMethod ModelRestorationMethod; - private int allocatorSize; - internal Allocator Allocator; + private int _allocatorSize; + internal Allocator _allocator; public PpmdProperties() : this(16 << 20, 6) @@ -26,8 +23,12 @@ namespace SharpCompress.Compressors.PPMd { AllocatorSize = allocatorSize; ModelOrder = modelOrder; - ModelRestorationMethod = modelRestorationMethod; + RestorationMethod = modelRestorationMethod; } + + public int ModelOrder { get; } + public PpmdVersion Version { get; } = PpmdVersion.I1; + internal ModelRestorationMethod RestorationMethod { get; } public PpmdProperties(byte[] properties) { @@ -36,11 +37,11 @@ namespace SharpCompress.Compressors.PPMd ushort props = DataConverter.LittleEndian.GetUInt16(properties, 0); AllocatorSize = (((props >> 4) & 0xff) + 1) << 20; ModelOrder = (props & 0x0f) + 1; - ModelRestorationMethod = (ModelRestorationMethod)(props >> 12); + RestorationMethod = (ModelRestorationMethod)(props >> 12); } else if (properties.Length == 5) { - Version = PpmdVersion.H7z; + Version = PpmdVersion.H7Z; AllocatorSize = DataConverter.LittleEndian.GetInt32(properties, 1); ModelOrder = properties[0]; } @@ -48,23 +49,23 @@ namespace SharpCompress.Compressors.PPMd public int AllocatorSize { - get => allocatorSize; + get => _allocatorSize; set { - allocatorSize = value; + _allocatorSize = value; if (Version == PpmdVersion.I1) { - if (Allocator == null) + if (_allocator == null) { - Allocator = new Allocator(); + _allocator = new Allocator(); } - Allocator.Start(allocatorSize); + _allocator.Start(_allocatorSize); } } } public byte[] Properties => DataConverter.LittleEndian.GetBytes( (ushort) - ((ModelOrder - 1) + (((AllocatorSize >> 20) - 1) << 4) + ((ushort)ModelRestorationMethod << 12))); + ((ModelOrder - 1) + (((AllocatorSize >> 20) - 1) << 4) + ((ushort)RestorationMethod << 12))); } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/PPMd/PpmdStream.cs b/src/SharpCompress/Compressors/PPMd/PpmdStream.cs index 1169ed52..b9629d58 100644 --- a/src/SharpCompress/Compressors/PPMd/PpmdStream.cs +++ b/src/SharpCompress/Compressors/PPMd/PpmdStream.cs @@ -8,60 +8,60 @@ namespace SharpCompress.Compressors.PPMd { public class PpmdStream : Stream { - private readonly PpmdProperties properties; - private readonly Stream stream; - private readonly bool compress; - private readonly Model model; - private readonly ModelPPM modelH; - private readonly Decoder decoder; - private long position; - private bool isDisposed; + private readonly PpmdProperties _properties; + private readonly Stream _stream; + private readonly bool _compress; + private readonly Model _model; + private readonly ModelPpm _modelH; + private readonly Decoder _decoder; + private long _position; + private bool _isDisposed; public PpmdStream(PpmdProperties properties, Stream stream, bool compress) { - this.properties = properties; - this.stream = stream; - this.compress = compress; + _properties = properties; + _stream = stream; + _compress = compress; if (properties.Version == PpmdVersion.I1) { - model = new Model(); + _model = new Model(); if (compress) { - model.EncodeStart(properties); + _model.EncodeStart(properties); } else { - model.DecodeStart(stream, properties); + _model.DecodeStart(stream, properties); } } if (properties.Version == PpmdVersion.H) { - modelH = new ModelPPM(); + _modelH = new ModelPpm(); if (compress) { throw new NotImplementedException(); } - modelH.decodeInit(stream, properties.ModelOrder, properties.AllocatorSize); + _modelH.DecodeInit(stream, properties.ModelOrder, properties.AllocatorSize); } - if (properties.Version == PpmdVersion.H7z) + if (properties.Version == PpmdVersion.H7Z) { - modelH = new ModelPPM(); + _modelH = new ModelPpm(); if (compress) { throw new NotImplementedException(); } - modelH.decodeInit(null, properties.ModelOrder, properties.AllocatorSize); - decoder = new Decoder(); - decoder.Init(stream); + _modelH.DecodeInit(null, properties.ModelOrder, properties.AllocatorSize); + _decoder = new Decoder(); + _decoder.Init(stream); } } - public override bool CanRead => !compress; + public override bool CanRead => !_compress; public override bool CanSeek => false; - public override bool CanWrite => compress; + public override bool CanWrite => _compress; public override void Flush() { @@ -69,16 +69,16 @@ namespace SharpCompress.Compressors.PPMd protected override void Dispose(bool isDisposing) { - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; + _isDisposed = true; if (isDisposing) { - if (compress) + if (_compress) { - model.EncodeBlock(stream, new MemoryStream(), true); + _model.EncodeBlock(_stream, new MemoryStream(), true); } } base.Dispose(isDisposing); @@ -86,38 +86,38 @@ namespace SharpCompress.Compressors.PPMd public override long Length => throw new NotSupportedException(); - public override long Position { get => position; set => throw new NotSupportedException(); } + public override long Position { get => _position; set => throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { - if (compress) + if (_compress) { return 0; } int size = 0; - if (properties.Version == PpmdVersion.I1) + if (_properties.Version == PpmdVersion.I1) { - size = model.DecodeBlock(stream, buffer, offset, count); + size = _model.DecodeBlock(_stream, buffer, offset, count); } - if (properties.Version == PpmdVersion.H) + if (_properties.Version == PpmdVersion.H) { int c; - while (size < count && (c = modelH.decodeChar()) >= 0) + while (size < count && (c = _modelH.DecodeChar()) >= 0) { buffer[offset++] = (byte)c; size++; } } - if (properties.Version == PpmdVersion.H7z) + if (_properties.Version == PpmdVersion.H7Z) { int c; - while (size < count && (c = modelH.decodeChar(decoder)) >= 0) + while (size < count && (c = _modelH.DecodeChar(_decoder)) >= 0) { buffer[offset++] = (byte)c; size++; } } - position += size; + _position += size; return size; } @@ -133,9 +133,9 @@ namespace SharpCompress.Compressors.PPMd public override void Write(byte[] buffer, int offset, int count) { - if (compress) + if (_compress) { - model.EncodeBlock(stream, new MemoryStream(buffer, offset, count), false); + _model.EncodeBlock(_stream, new MemoryStream(buffer, offset, count), false); } } } diff --git a/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs b/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs index c4d17015..8ab1aa38 100644 --- a/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs +++ b/src/SharpCompress/Compressors/PPMd/PpmdVersion.cs @@ -3,7 +3,7 @@ public enum PpmdVersion { H, - H7z, + H7Z, I1 } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs index 96798e46..ca81b905 100644 --- a/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs +++ b/src/SharpCompress/Compressors/Rar/UnpackV1/Unpack.cs @@ -51,7 +51,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 public int PpmEscChar { get; set; } - private readonly ModelPPM ppm = new ModelPPM(); + private readonly ModelPpm ppm = new ModelPpm(); private readonly RarVM rarVM = new RarVM(); @@ -242,7 +242,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 } if (unpBlockType == BlockTypes.BLOCK_PPM) { - int Ch = ppm.decodeChar(); + int Ch = ppm.DecodeChar(); if (Ch == -1) { ppmError = true; @@ -250,7 +250,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 } if (Ch == PpmEscChar) { - int NextCh = ppm.decodeChar(); + int NextCh = ppm.DecodeChar(); if (NextCh == 0) { if (!ReadTables()) @@ -277,7 +277,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 bool failed = false; for (int I = 0; I < 4 && !failed; I++) { - int ch = ppm.decodeChar(); + int ch = ppm.DecodeChar(); if (ch == -1) { failed = true; @@ -305,7 +305,7 @@ namespace SharpCompress.Compressors.Rar.UnpackV1 } if (NextCh == 5) { - int Length = ppm.decodeChar(); + int Length = ppm.DecodeChar(); if (Length == -1) { break; @@ -828,7 +828,7 @@ WriteBorder=Math.Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; if ((bitField & 0x8000) != 0) { unpBlockType = BlockTypes.BLOCK_PPM; - return (ppm.decodeInit(this, PpmEscChar)); + return (ppm.DecodeInit(this, PpmEscChar)); } unpBlockType = BlockTypes.BLOCK_LZ; @@ -973,7 +973,7 @@ WriteBorder=Math.Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; private bool ReadVMCodePPM() { - int FirstByte = ppm.decodeChar(); + int FirstByte = ppm.DecodeChar(); if (FirstByte == -1) { return (false); @@ -981,7 +981,7 @@ WriteBorder=Math.Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; int Length = (FirstByte & 7) + 1; if (Length == 7) { - int B1 = ppm.decodeChar(); + int B1 = ppm.DecodeChar(); if (B1 == -1) { return (false); @@ -990,12 +990,12 @@ WriteBorder=Math.Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; } else if (Length == 8) { - int B1 = ppm.decodeChar(); + int B1 = ppm.DecodeChar(); if (B1 == -1) { return (false); } - int B2 = ppm.decodeChar(); + int B2 = ppm.DecodeChar(); if (B2 == -1) { return (false); @@ -1006,7 +1006,7 @@ WriteBorder=Math.Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; List vmCode = new List(); for (int I = 0; I < Length; I++) { - int Ch = ppm.decodeChar(); + int Ch = ppm.DecodeChar(); if (Ch == -1) { return (false); @@ -1262,7 +1262,7 @@ WriteBorder=Math.Min(MaxWinSize,UNPACK_MAX_WRITE)&MaxWinMask; SubAllocator allocator = ppm.SubAlloc; if (allocator != null) { - allocator.stopSubAllocator(); + allocator.StopSubAllocator(); } } } diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 58cf4dd3..2d34941d 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -28,12 +28,12 @@ $(DefineConstants);NETCORE - + $(DefineConstants);NETCORE - + \ No newline at end of file From 8dfc4a2ffba08d14cb512c82d7123140880fbf93 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 6 May 2018 08:59:00 +0100 Subject: [PATCH 112/124] more variable naming --- .../Archives/SevenZip/SevenZipArchive.cs | 6 +- src/SharpCompress/Common/ArchiveEncoding.cs | 4 +- src/SharpCompress/Common/GZip/GZipEntry.cs | 10 +- src/SharpCompress/Common/GZip/GZipFilePart.cs | 2 +- src/SharpCompress/Common/IEntry.Extensions.cs | 2 +- .../Common/Rar/Headers/AVHeader.cs | 4 +- .../Common/SevenZip/ArchiveDatabase.cs | 102 +++---- .../Common/SevenZip/ArchiveReader.cs | 250 +++++++++--------- .../Common/SevenZip/CBindPair.cs | 4 +- .../Common/SevenZip/CCoderInfo.cs | 8 +- src/SharpCompress/Common/SevenZip/CFolder.cs | 72 ++--- .../Common/SevenZip/CMethodId.cs | 30 +-- .../Common/SevenZip/SevenZipFilePart.cs | 36 +-- .../Common/Tar/Headers/TarHeader.cs | 32 +-- src/SharpCompress/Common/Tar/TarEntry.cs | 16 +- .../Common/Tar/TarReadOnlySubStream.cs | 12 +- src/SharpCompress/Common/Volume.cs | 14 +- .../Zip/Headers/DirectoryEntryHeader.cs | 2 +- .../Common/Zip/Headers/HeaderFlags.cs | 2 +- .../Common/Zip/Headers/LocalEntryHeader.cs | 2 +- .../Headers/LocalEntryHeaderExtraFactory.cs | 2 +- .../Zip/Headers/Zip64DirectoryEndHeader.cs | 4 +- .../Zip/PkwareTraditionalCryptoStream.cs | 36 +-- .../Zip/PkwareTraditionalEncryptionData.cs | 24 +- .../Common/Zip/StreamingZipFilePart.cs | 18 +- .../Common/Zip/StreamingZipHeaderFactory.cs | 16 +- .../Common/Zip/WinzipAesCryptoStream.cs | 60 ++--- .../Common/Zip/WinzipAesEncryptionData.cs | 30 +-- src/SharpCompress/Common/Zip/ZipEntry.cs | 20 +- .../Common/Zip/ZipHeaderFactory.cs | 36 +-- .../Compressors/Deflate/ZlibBaseStream.cs | 2 +- .../Compressors/Deflate64/Deflate64Stream.cs | 6 +- .../Deflate64/FastEncoderStatus.cs | 52 ++-- .../Compressors/Deflate64/HuffmanTree.cs | 24 +- .../Compressors/Deflate64/InflaterManaged.cs | 50 ++-- .../Compressors/Deflate64/OutputWindow.cs | 42 +-- .../Compressors/LZMA/DecoderStream.cs | 32 +-- .../Compressors/LZMA/Registry.cs | 4 +- src/SharpCompress/Writers/Tar/TarWriter.cs | 2 +- .../Writers/Zip/ZipCentralDirectoryEntry.cs | 2 +- src/SharpCompress/Writers/Zip/ZipWriter.cs | 2 +- 41 files changed, 537 insertions(+), 537 deletions(-) diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index dbc3df8b..9cf64142 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -104,9 +104,9 @@ namespace SharpCompress.Archives.SevenZip { var stream = volumes.Single().Stream; LoadFactory(stream); - for (int i = 0; i < database.Files.Count; i++) + for (int i = 0; i < database._files.Count; i++) { - var file = database.Files[i]; + var file = database._files[i]; yield return new SevenZipArchiveEntry(this, new SevenZipFilePart(stream, database, i, file, ReaderOptions.ArchiveEncoding)); } } @@ -155,7 +155,7 @@ namespace SharpCompress.Archives.SevenZip get { int i = Entries.Count; - return database.PackSizes.Aggregate(0L, (total, packSize) => total + packSize); + return database._packSizes.Aggregate(0L, (total, packSize) => total + packSize); } } diff --git a/src/SharpCompress/Common/ArchiveEncoding.cs b/src/SharpCompress/Common/ArchiveEncoding.cs index a8712115..0b4328e7 100644 --- a/src/SharpCompress/Common/ArchiveEncoding.cs +++ b/src/SharpCompress/Common/ArchiveEncoding.cs @@ -49,8 +49,8 @@ namespace SharpCompress.Common #if NETSTANDARD1_0 return Decode(bytes, 0, bytes.Length); #else - var extendedASCIIEncoding = Encoding.GetEncoding(437); - return extendedASCIIEncoding.GetString(bytes, 0, bytes.Length); + var extendedAsciiEncoding = Encoding.GetEncoding(437); + return extendedAsciiEncoding.GetString(bytes, 0, bytes.Length); #endif } diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs index 849d4741..0b43cd45 100644 --- a/src/SharpCompress/Common/GZip/GZipEntry.cs +++ b/src/SharpCompress/Common/GZip/GZipEntry.cs @@ -7,24 +7,24 @@ namespace SharpCompress.Common.GZip { public class GZipEntry : Entry { - private readonly GZipFilePart filePart; + private readonly GZipFilePart _filePart; internal GZipEntry(GZipFilePart filePart) { - this.filePart = filePart; + _filePart = filePart; } public override CompressionType CompressionType => CompressionType.GZip; public override long Crc => 0; - public override string Key => filePart.FilePartName; + public override string Key => _filePart.FilePartName; public override long CompressedSize => 0; public override long Size => 0; - public override DateTime? LastModifiedTime => filePart.DateModified; + public override DateTime? LastModifiedTime => _filePart.DateModified; public override DateTime? CreatedTime => null; @@ -38,7 +38,7 @@ namespace SharpCompress.Common.GZip public override bool IsSplitAfter => false; - internal override IEnumerable Parts => filePart.AsEnumerable(); + internal override IEnumerable Parts => _filePart.AsEnumerable(); internal static IEnumerable GetEntries(Stream stream, OptionsBase options) { diff --git a/src/SharpCompress/Common/GZip/GZipFilePart.cs b/src/SharpCompress/Common/GZip/GZipFilePart.cs index 5859a242..1f942e74 100644 --- a/src/SharpCompress/Common/GZip/GZipFilePart.cs +++ b/src/SharpCompress/Common/GZip/GZipFilePart.cs @@ -61,7 +61,7 @@ namespace SharpCompress.Common.GZip } Int32 timet = DataConverter.LittleEndian.GetInt32(header, 4); - DateModified = TarHeader.Epoch.AddSeconds(timet); + DateModified = TarHeader.EPOCH.AddSeconds(timet); if ((header[3] & 0x04) == 0x04) { // read and discard extra field diff --git a/src/SharpCompress/Common/IEntry.Extensions.cs b/src/SharpCompress/Common/IEntry.Extensions.cs index 70a634ac..76d75a8f 100644 --- a/src/SharpCompress/Common/IEntry.Extensions.cs +++ b/src/SharpCompress/Common/IEntry.Extensions.cs @@ -5,7 +5,7 @@ using SharpCompress.Readers; namespace SharpCompress.Common { - internal static class IEntryExtensions + internal static class EntryExtensions { internal static void PreserveExtractionOptions(this IEntry entry, string destinationFileName, ExtractionOptions options) diff --git a/src/SharpCompress/Common/Rar/Headers/AVHeader.cs b/src/SharpCompress/Common/Rar/Headers/AVHeader.cs index 4b521ca8..51dda2cf 100644 --- a/src/SharpCompress/Common/Rar/Headers/AVHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/AVHeader.cs @@ -2,9 +2,9 @@ namespace SharpCompress.Common.Rar.Headers { - internal class AVHeader : RarHeader + internal class AvHeader : RarHeader { - public AVHeader(RarHeader header, RarCrcBinaryReader reader) + public AvHeader(RarHeader header, RarCrcBinaryReader reader) : base(header, reader, HeaderType.Av) { if (IsRar5) diff --git a/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs index c6da3cda..e827eb20 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveDatabase.cs @@ -8,20 +8,20 @@ namespace SharpCompress.Common.SevenZip { internal class ArchiveDatabase { - internal byte MajorVersion; - internal byte MinorVersion; - internal long StartPositionAfterHeader; - internal long DataStartPosition; + internal byte _majorVersion; + internal byte _minorVersion; + internal long _startPositionAfterHeader; + internal long _dataStartPosition; - internal List PackSizes = new List(); - internal List PackCRCs = new List(); - internal List Folders = new List(); - internal List NumUnpackStreamsVector; - internal List Files = new List(); + internal List _packSizes = new List(); + internal List _packCrCs = new List(); + internal List _folders = new List(); + internal List _numUnpackStreamsVector; + internal List _files = new List(); - internal List PackStreamStartPositions = new List(); - internal List FolderStartFileIndex = new List(); - internal List FileIndexToFolderIndexMap = new List(); + internal List _packStreamStartPositions = new List(); + internal List _folderStartFileIndex = new List(); + internal List _fileIndexToFolderIndexMap = new List(); internal IPasswordProvider PasswordProvider { get; } @@ -32,54 +32,54 @@ namespace SharpCompress.Common.SevenZip internal void Clear() { - PackSizes.Clear(); - PackCRCs.Clear(); - Folders.Clear(); - NumUnpackStreamsVector = null; - Files.Clear(); + _packSizes.Clear(); + _packCrCs.Clear(); + _folders.Clear(); + _numUnpackStreamsVector = null; + _files.Clear(); - PackStreamStartPositions.Clear(); - FolderStartFileIndex.Clear(); - FileIndexToFolderIndexMap.Clear(); + _packStreamStartPositions.Clear(); + _folderStartFileIndex.Clear(); + _fileIndexToFolderIndexMap.Clear(); } internal bool IsEmpty() { - return PackSizes.Count == 0 - && PackCRCs.Count == 0 - && Folders.Count == 0 - && NumUnpackStreamsVector.Count == 0 - && Files.Count == 0; + return _packSizes.Count == 0 + && _packCrCs.Count == 0 + && _folders.Count == 0 + && _numUnpackStreamsVector.Count == 0 + && _files.Count == 0; } private void FillStartPos() { - PackStreamStartPositions.Clear(); + _packStreamStartPositions.Clear(); long startPos = 0; - for (int i = 0; i < PackSizes.Count; i++) + for (int i = 0; i < _packSizes.Count; i++) { - PackStreamStartPositions.Add(startPos); - startPos += PackSizes[i]; + _packStreamStartPositions.Add(startPos); + startPos += _packSizes[i]; } } private void FillFolderStartFileIndex() { - FolderStartFileIndex.Clear(); - FileIndexToFolderIndexMap.Clear(); + _folderStartFileIndex.Clear(); + _fileIndexToFolderIndexMap.Clear(); int folderIndex = 0; int indexInFolder = 0; - for (int i = 0; i < Files.Count; i++) + for (int i = 0; i < _files.Count; i++) { - CFileItem file = Files[i]; + CFileItem file = _files[i]; bool emptyStream = !file.HasStream; if (emptyStream && indexInFolder == 0) { - FileIndexToFolderIndexMap.Add(-1); + _fileIndexToFolderIndexMap.Add(-1); continue; } @@ -89,14 +89,14 @@ namespace SharpCompress.Common.SevenZip // v4.07: Loop for skipping empty folders for (;;) { - if (folderIndex >= Folders.Count) + if (folderIndex >= _folders.Count) { throw new InvalidOperationException(); } - FolderStartFileIndex.Add(i); // check it + _folderStartFileIndex.Add(i); // check it - if (NumUnpackStreamsVector[folderIndex] != 0) + if (_numUnpackStreamsVector[folderIndex] != 0) { break; } @@ -105,7 +105,7 @@ namespace SharpCompress.Common.SevenZip } } - FileIndexToFolderIndexMap.Add(folderIndex); + _fileIndexToFolderIndexMap.Add(folderIndex); if (emptyStream) { @@ -114,7 +114,7 @@ namespace SharpCompress.Common.SevenZip indexInFolder++; - if (indexInFolder >= NumUnpackStreamsVector[folderIndex]) + if (indexInFolder >= _numUnpackStreamsVector[folderIndex]) { folderIndex++; indexInFolder = 0; @@ -130,19 +130,19 @@ namespace SharpCompress.Common.SevenZip internal long GetFolderStreamPos(CFolder folder, int indexInFolder) { - int index = folder.FirstPackStreamId + indexInFolder; - return DataStartPosition + PackStreamStartPositions[index]; + int index = folder._firstPackStreamId + indexInFolder; + return _dataStartPosition + _packStreamStartPositions[index]; } internal long GetFolderFullPackSize(int folderIndex) { - int packStreamIndex = Folders[folderIndex].FirstPackStreamId; - CFolder folder = Folders[folderIndex]; + int packStreamIndex = _folders[folderIndex]._firstPackStreamId; + CFolder folder = _folders[folderIndex]; long size = 0; - for (int i = 0; i < folder.PackStreams.Count; i++) + for (int i = 0; i < folder._packStreams.Count; i++) { - size += PackSizes[packStreamIndex + i]; + size += _packSizes[packStreamIndex + i]; } return size; @@ -150,12 +150,12 @@ namespace SharpCompress.Common.SevenZip internal Stream GetFolderStream(Stream stream, CFolder folder, IPasswordProvider pw) { - int packStreamIndex = folder.FirstPackStreamId; + int packStreamIndex = folder._firstPackStreamId; long folderStartPackPos = GetFolderStreamPos(folder, 0); List packSizes = new List(); - for (int j = 0; j < folder.PackStreams.Count; j++) + for (int j = 0; j < folder._packStreams.Count; j++) { - packSizes.Add(PackSizes[packStreamIndex + j]); + packSizes.Add(_packSizes[packStreamIndex + j]); } return DecoderStreamHelper.CreateDecoderStream(stream, folderStartPackPos, packSizes.ToArray(), folder, pw); @@ -163,15 +163,15 @@ namespace SharpCompress.Common.SevenZip private long GetFolderPackStreamSize(int folderIndex, int streamIndex) { - return PackSizes[Folders[folderIndex].FirstPackStreamId + streamIndex]; + return _packSizes[_folders[folderIndex]._firstPackStreamId + streamIndex]; } private long GetFilePackSize(int fileIndex) { - int folderIndex = FileIndexToFolderIndexMap[fileIndex]; + int folderIndex = _fileIndexToFolderIndexMap[fileIndex]; if (folderIndex != -1) { - if (FolderStartFileIndex[folderIndex] == fileIndex) + if (_folderStartFileIndex[folderIndex] == fileIndex) { return GetFolderFullPackSize(folderIndex); } diff --git a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs index 2041fd3f..2093a831 100644 --- a/src/SharpCompress/Common/SevenZip/ArchiveReader.cs +++ b/src/SharpCompress/Common/SevenZip/ArchiveReader.cs @@ -230,7 +230,7 @@ namespace SharpCompress.Common.SevenZip #if DEBUG Log.WriteLine("NumCoders: " + numCoders); #endif - folder.Coders = new List(numCoders); + folder._coders = new List(numCoders); int numInStreams = 0; int numOutStreams = 0; for (int i = 0; i < numCoders; i++) @@ -242,14 +242,14 @@ namespace SharpCompress.Common.SevenZip try { CCoderInfo coder = new CCoderInfo(); - folder.Coders.Add(coder); + folder._coders.Add(coder); byte mainByte = ReadByte(); int idSize = (mainByte & 0xF); - byte[] longID = new byte[idSize]; - ReadBytes(longID, 0, idSize); + byte[] longId = new byte[idSize]; + ReadBytes(longId, 0, idSize); #if DEBUG - Log.WriteLine("MethodId: " + String.Join("", Enumerable.Range(0, idSize).Select(x => longID[x].ToString("x2")).ToArray())); + Log.WriteLine("MethodId: " + String.Join("", Enumerable.Range(0, idSize).Select(x => longId[x].ToString("x2")).ToArray())); #endif if (idSize > 8) { @@ -258,16 +258,16 @@ namespace SharpCompress.Common.SevenZip ulong id = 0; for (int j = 0; j < idSize; j++) { - id |= (ulong)longID[idSize - 1 - j] << (8 * j); + id |= (ulong)longId[idSize - 1 - j] << (8 * j); } - coder.MethodId = new CMethodId(id); + coder._methodId = new CMethodId(id); if ((mainByte & 0x10) != 0) { - coder.NumInStreams = ReadNum(); - coder.NumOutStreams = ReadNum(); + coder._numInStreams = ReadNum(); + coder._numOutStreams = ReadNum(); #if DEBUG - Log.WriteLine("Complex Stream (In: " + coder.NumInStreams + " - Out: " + coder.NumOutStreams + ")"); + Log.WriteLine("Complex Stream (In: " + coder._numInStreams + " - Out: " + coder._numOutStreams + ")"); #endif } else @@ -275,17 +275,17 @@ namespace SharpCompress.Common.SevenZip #if DEBUG Log.WriteLine("Simple Stream (In: 1 - Out: 1)"); #endif - coder.NumInStreams = 1; - coder.NumOutStreams = 1; + coder._numInStreams = 1; + coder._numOutStreams = 1; } if ((mainByte & 0x20) != 0) { int propsSize = ReadNum(); - coder.Props = new byte[propsSize]; - ReadBytes(coder.Props, 0, propsSize); + coder._props = new byte[propsSize]; + ReadBytes(coder._props, 0, propsSize); #if DEBUG - Log.WriteLine("Settings: " + String.Join("", coder.Props.Select(bt => bt.ToString("x2")).ToArray())); + Log.WriteLine("Settings: " + String.Join("", coder._props.Select(bt => bt.ToString("x2")).ToArray())); #endif } @@ -294,8 +294,8 @@ namespace SharpCompress.Common.SevenZip throw new NotSupportedException(); } - numInStreams += coder.NumInStreams; - numOutStreams += coder.NumOutStreams; + numInStreams += coder._numInStreams; + numOutStreams += coder._numOutStreams; } finally { @@ -306,7 +306,7 @@ namespace SharpCompress.Common.SevenZip } int numBindPairs = numOutStreams - 1; - folder.BindPairs = new List(numBindPairs); + folder._bindPairs = new List(numBindPairs); #if DEBUG Log.WriteLine("BindPairs: " + numBindPairs); Log.PushIndent(); @@ -314,11 +314,11 @@ namespace SharpCompress.Common.SevenZip for (int i = 0; i < numBindPairs; i++) { CBindPair bp = new CBindPair(); - bp.InIndex = ReadNum(); - bp.OutIndex = ReadNum(); - folder.BindPairs.Add(bp); + bp._inIndex = ReadNum(); + bp._outIndex = ReadNum(); + folder._bindPairs.Add(bp); #if DEBUG - Log.WriteLine("#" + i + " - In: " + bp.InIndex + " - Out: " + bp.OutIndex); + Log.WriteLine("#" + i + " - In: " + bp._inIndex + " - Out: " + bp._outIndex); #endif } #if DEBUG @@ -342,12 +342,12 @@ namespace SharpCompress.Common.SevenZip #if DEBUG Log.WriteLine("Single PackStream: #" + i); #endif - folder.PackStreams.Add(i); + folder._packStreams.Add(i); break; } } - if (folder.PackStreams.Count != 1) + if (folder._packStreams.Count != 1) { throw new NotSupportedException(); } @@ -364,7 +364,7 @@ namespace SharpCompress.Common.SevenZip #if DEBUG Log.WriteLine("#" + i + " - " + num); #endif - folder.PackStreams.Add(num); + folder._packStreams.Add(num); } #if DEBUG Log.PopIndent(); @@ -412,7 +412,7 @@ namespace SharpCompress.Common.SevenZip return digests; } - private void ReadPackInfo(out long dataOffset, out List packSizes, out List packCRCs) + private void ReadPackInfo(out long dataOffset, out List packSizes, out List packCrCs) { #if DEBUG Log.WriteLine("-- ReadPackInfo --"); @@ -420,7 +420,7 @@ namespace SharpCompress.Common.SevenZip #endif try { - packCRCs = null; + packCrCs = null; dataOffset = checked((long)ReadNumber()); #if DEBUG @@ -459,18 +459,18 @@ namespace SharpCompress.Common.SevenZip } if (type == BlockType.Crc) { - packCRCs = ReadHashDigests(numPackStreams); + packCrCs = ReadHashDigests(numPackStreams); continue; } SkipData(); } - if (packCRCs == null) + if (packCrCs == null) { - packCRCs = new List(numPackStreams); + packCrCs = new List(numPackStreams); for (int i = 0; i < numPackStreams; i++) { - packCRCs.Add(null); + packCrCs.Add(null); } } } @@ -506,10 +506,10 @@ namespace SharpCompress.Common.SevenZip int index = 0; for (int i = 0; i < numFolders; i++) { - var f = new CFolder {FirstPackStreamId = index}; + var f = new CFolder {_firstPackStreamId = index}; folders.Add(f); GetNextFolderItem(f); - index += f.PackStreams.Count; + index += f._packStreams.Count; } } @@ -530,7 +530,7 @@ namespace SharpCompress.Common.SevenZip #if DEBUG Log.Write(" " + size); #endif - folder.UnpackSizes.Add(size); + folder._unpackSizes.Add(size); } #if DEBUG Log.WriteLine(); @@ -550,7 +550,7 @@ namespace SharpCompress.Common.SevenZip List crcs = ReadHashDigests(numFolders); for (int i = 0; i < numFolders; i++) { - folders[i].UnpackCRC = crcs[i]; + folders[i]._unpackCrc = crcs[i]; } continue; } @@ -661,7 +661,7 @@ namespace SharpCompress.Common.SevenZip for (int i = 0; i < folders.Count; i++) { int numSubstreams = numUnpackStreamsInFolders[i]; - if (numSubstreams != 1 || !folders[i].UnpackCRCDefined) + if (numSubstreams != 1 || !folders[i].UnpackCrcDefined) { numDigests += numSubstreams; } @@ -683,9 +683,9 @@ namespace SharpCompress.Common.SevenZip { int numSubstreams = numUnpackStreamsInFolders[i]; CFolder folder = folders[i]; - if (numSubstreams == 1 && folder.UnpackCRCDefined) + if (numSubstreams == 1 && folder.UnpackCrcDefined) { - digests.Add(folder.UnpackCRC.Value); + digests.Add(folder._unpackCrc.Value); } else { @@ -733,7 +733,7 @@ namespace SharpCompress.Common.SevenZip List dataVector, out long dataOffset, out List packSizes, - out List packCRCs, + out List packCrCs, out List folders, out List numUnpackStreamsInFolders, out List unpackSizes, @@ -747,7 +747,7 @@ namespace SharpCompress.Common.SevenZip { dataOffset = long.MinValue; packSizes = null; - packCRCs = null; + packCrCs = null; folders = null; numUnpackStreamsInFolders = null; unpackSizes = null; @@ -760,7 +760,7 @@ namespace SharpCompress.Common.SevenZip case BlockType.End: return; case BlockType.PackInfo: - ReadPackInfo(out dataOffset, out packSizes, out packCRCs); + ReadPackInfo(out dataOffset, out packSizes, out packCrCs); break; case BlockType.UnpackInfo: ReadUnpackInfo(dataVector, out folders); @@ -791,7 +791,7 @@ namespace SharpCompress.Common.SevenZip { long dataStartPos; List packSizes; - List packCRCs; + List packCrCs; List folders; List numUnpackStreamsInFolders; List unpackSizes; @@ -800,7 +800,7 @@ namespace SharpCompress.Common.SevenZip ReadStreamsInfo(null, out dataStartPos, out packSizes, - out packCRCs, + out packCrCs, out folders, out numUnpackStreamsInFolders, out unpackSizes, @@ -813,7 +813,7 @@ namespace SharpCompress.Common.SevenZip foreach (var folder in folders) { long oldDataStartPos = dataStartPos; - long[] myPackSizes = new long[folder.PackStreams.Count]; + long[] myPackSizes = new long[folder._packStreams.Count]; for (int i = 0; i < myPackSizes.Length; i++) { long packSize = packSizes[packIndex + i]; @@ -833,9 +833,9 @@ namespace SharpCompress.Common.SevenZip } dataVector.Add(data); - if (folder.UnpackCRCDefined) + if (folder.UnpackCrcDefined) { - if (Crc.Finish(Crc.Update(Crc.INIT_CRC, data, 0, unpackSize)) != folder.UnpackCRC) + if (Crc.Finish(Crc.Update(Crc.INIT_CRC, data, 0, unpackSize)) != folder._unpackCrc) { throw new InvalidOperationException("Decoded stream does not match expected CRC."); } @@ -870,7 +870,7 @@ namespace SharpCompress.Common.SevenZip List dataVector = null; if (type == BlockType.AdditionalStreamsInfo) { - dataVector = ReadAndDecodePackedStreams(db.StartPositionAfterHeader, getTextPassword); + dataVector = ReadAndDecodePackedStreams(db._startPositionAfterHeader, getTextPassword); type = ReadId(); } @@ -880,32 +880,32 @@ namespace SharpCompress.Common.SevenZip if (type == BlockType.MainStreamsInfo) { ReadStreamsInfo(dataVector, - out db.DataStartPosition, - out db.PackSizes, - out db.PackCRCs, - out db.Folders, - out db.NumUnpackStreamsVector, + out db._dataStartPosition, + out db._packSizes, + out db._packCrCs, + out db._folders, + out db._numUnpackStreamsVector, out unpackSizes, out digests); - db.DataStartPosition += db.StartPositionAfterHeader; + db._dataStartPosition += db._startPositionAfterHeader; type = ReadId(); } else { - unpackSizes = new List(db.Folders.Count); - digests = new List(db.Folders.Count); - db.NumUnpackStreamsVector = new List(db.Folders.Count); - for (int i = 0; i < db.Folders.Count; i++) + unpackSizes = new List(db._folders.Count); + digests = new List(db._folders.Count); + db._numUnpackStreamsVector = new List(db._folders.Count); + for (int i = 0; i < db._folders.Count; i++) { - var folder = db.Folders[i]; + var folder = db._folders[i]; unpackSizes.Add(folder.GetUnpackSize()); - digests.Add(folder.UnpackCRC); - db.NumUnpackStreamsVector.Add(1); + digests.Add(folder._unpackCrc); + db._numUnpackStreamsVector.Add(1); } } - db.Files.Clear(); + db._files.Clear(); if (type == BlockType.End) { @@ -921,10 +921,10 @@ namespace SharpCompress.Common.SevenZip #if DEBUG Log.WriteLine("NumFiles: " + numFiles); #endif - db.Files = new List(numFiles); + db._files = new List(numFiles); for (int i = 0; i < numFiles; i++) { - db.Files.Add(new CFileItem()); + db._files.Add(new CFileItem()); } BitVector emptyStreamVector = new BitVector(numFiles); @@ -951,11 +951,11 @@ namespace SharpCompress.Common.SevenZip #if DEBUG Log.Write("FileNames:"); #endif - for (int i = 0; i < db.Files.Count; i++) + for (int i = 0; i < db._files.Count; i++) { - db.Files[i].Name = _currentReader.ReadString(); + db._files[i].Name = _currentReader.ReadString(); #if DEBUG - Log.Write(" " + db.Files[i].Name); + Log.Write(" " + db._files[i].Name); #endif } #if DEBUG @@ -991,7 +991,7 @@ namespace SharpCompress.Common.SevenZip attr = attr.Value & 0x7FFFu; } - db.Files[i].Attrib = attr; + db._files[i].Attrib = attr; #if DEBUG Log.Write(" " + (attr.HasValue ? attr.Value.ToString("x8") : "n/a")); #endif @@ -1057,7 +1057,7 @@ namespace SharpCompress.Common.SevenZip #endif ReadNumberVector(dataVector, numFiles, delegate(int i, long? startPos) { - db.Files[i].StartPos = startPos; + db._files[i].StartPos = startPos; #if DEBUG Log.Write(" " + (startPos.HasValue ? startPos.Value.ToString() : "n/a")); #endif @@ -1072,7 +1072,7 @@ namespace SharpCompress.Common.SevenZip #endif ReadDateTimeVector(dataVector, numFiles, delegate(int i, DateTime? time) { - db.Files[i].CTime = time; + db._files[i].CTime = time; #if DEBUG Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a")); #endif @@ -1087,7 +1087,7 @@ namespace SharpCompress.Common.SevenZip #endif ReadDateTimeVector(dataVector, numFiles, delegate(int i, DateTime? time) { - db.Files[i].ATime = time; + db._files[i].ATime = time; #if DEBUG Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a")); #endif @@ -1102,7 +1102,7 @@ namespace SharpCompress.Common.SevenZip #endif ReadDateTimeVector(dataVector, numFiles, delegate(int i, DateTime? time) { - db.Files[i].MTime = time; + db._files[i].MTime = time; #if DEBUG Log.Write(" " + (time.HasValue ? time.Value.ToString() : "n/a")); #endif @@ -1129,7 +1129,7 @@ namespace SharpCompress.Common.SevenZip } // since 0.3 record sizes must be correct - bool checkRecordsSize = (db.MajorVersion > 0 || db.MinorVersion > 2); + bool checkRecordsSize = (db._majorVersion > 0 || db._minorVersion > 2); if (checkRecordsSize && _currentReader.Offset - oldPos != size) { throw new InvalidOperationException(); @@ -1140,7 +1140,7 @@ namespace SharpCompress.Common.SevenZip int sizeIndex = 0; for (int i = 0; i < numFiles; i++) { - CFileItem file = db.Files[i]; + CFileItem file = db._files[i]; file.HasStream = !emptyStreamVector[i]; if (file.HasStream) { @@ -1214,10 +1214,10 @@ namespace SharpCompress.Common.SevenZip var db = new ArchiveDatabase(pass); db.Clear(); - db.MajorVersion = _header[6]; - db.MinorVersion = _header[7]; + db._majorVersion = _header[6]; + db._minorVersion = _header[7]; - if (db.MajorVersion != 0) + if (db._majorVersion != 0) { throw new InvalidOperationException(); } @@ -1238,7 +1238,7 @@ namespace SharpCompress.Common.SevenZip throw new InvalidOperationException(); } - db.StartPositionAfterHeader = _streamOrigin + 0x20; + db._startPositionAfterHeader = _streamOrigin + 0x20; // empty header is ok if (nextHeaderSize == 0) @@ -1252,7 +1252,7 @@ namespace SharpCompress.Common.SevenZip throw new InvalidOperationException(); } - if (nextHeaderOffset > _streamEnding - db.StartPositionAfterHeader) + if (nextHeaderOffset > _streamEnding - db._startPositionAfterHeader) { throw new IndexOutOfRangeException(); } @@ -1279,7 +1279,7 @@ namespace SharpCompress.Common.SevenZip throw new InvalidOperationException(); } - var dataVector = ReadAndDecodePackedStreams(db.StartPositionAfterHeader, db.PasswordProvider); + var dataVector = ReadAndDecodePackedStreams(db._startPositionAfterHeader, db.PasswordProvider); // compressed header without content is odd but ok if (dataVector.Count == 0) @@ -1309,17 +1309,17 @@ namespace SharpCompress.Common.SevenZip internal class CExtractFolderInfo { - internal int FileIndex; - internal int FolderIndex; - internal List ExtractStatuses = new List(); + internal int _fileIndex; + internal int _folderIndex; + internal List _extractStatuses = new List(); internal CExtractFolderInfo(int fileIndex, int folderIndex) { - FileIndex = fileIndex; - FolderIndex = folderIndex; + _fileIndex = fileIndex; + _folderIndex = folderIndex; if (fileIndex != -1) { - ExtractStatuses.Add(true); + _extractStatuses.Add(true); } } } @@ -1375,7 +1375,7 @@ namespace SharpCompress.Common.SevenZip private void ProcessEmptyFiles() { - while (_currentIndex < _extractStatuses.Count && _db.Files[_startIndex + _currentIndex].Size == 0) + while (_currentIndex < _extractStatuses.Count && _db._files[_startIndex + _currentIndex].Size == 0) { OpenFile(); _stream.Dispose(); @@ -1388,17 +1388,17 @@ namespace SharpCompress.Common.SevenZip { int index = _startIndex + _currentIndex; #if DEBUG - Log.WriteLine(_db.Files[index].Name); + Log.WriteLine(_db._files[index].Name); #endif - if (_db.Files[index].CrcDefined) + if (_db._files[index].CrcDefined) { - _stream = new CrcCheckStream(_db.Files[index].Crc.Value); + _stream = new CrcCheckStream(_db._files[index].Crc.Value); } else { _stream = new MemoryStream(); } - _rem = _db.Files[index].Size; + _rem = _db._files[index].Size; } public override void Write(byte[] buffer, int offset, int count) @@ -1441,32 +1441,32 @@ namespace SharpCompress.Common.SevenZip #endregion } - private Stream GetCachedDecoderStream(ArchiveDatabase _db, int folderIndex) + private Stream GetCachedDecoderStream(ArchiveDatabase db, int folderIndex) { Stream s; if (!_cachedStreams.TryGetValue(folderIndex, out s)) { - CFolder folderInfo = _db.Folders[folderIndex]; - int packStreamIndex = _db.Folders[folderIndex].FirstPackStreamId; - long folderStartPackPos = _db.GetFolderStreamPos(folderInfo, 0); + CFolder folderInfo = db._folders[folderIndex]; + int packStreamIndex = db._folders[folderIndex]._firstPackStreamId; + long folderStartPackPos = db.GetFolderStreamPos(folderInfo, 0); List packSizes = new List(); - for (int j = 0; j < folderInfo.PackStreams.Count; j++) + for (int j = 0; j < folderInfo._packStreams.Count; j++) { - packSizes.Add(_db.PackSizes[packStreamIndex + j]); + packSizes.Add(db._packSizes[packStreamIndex + j]); } s = DecoderStreamHelper.CreateDecoderStream(_stream, folderStartPackPos, packSizes.ToArray(), folderInfo, - _db.PasswordProvider); + db.PasswordProvider); _cachedStreams.Add(folderIndex, s); } return s; } - public Stream OpenStream(ArchiveDatabase _db, int fileIndex) + public Stream OpenStream(ArchiveDatabase db, int fileIndex) { - int folderIndex = _db.FileIndexToFolderIndexMap[fileIndex]; - int numFilesInFolder = _db.NumUnpackStreamsVector[folderIndex]; - int firstFileIndex = _db.FolderStartFileIndex[folderIndex]; + int folderIndex = db._fileIndexToFolderIndexMap[fileIndex]; + int numFilesInFolder = db._numUnpackStreamsVector[folderIndex]; + int firstFileIndex = db._folderStartFileIndex[folderIndex]; if (firstFileIndex > fileIndex || fileIndex - firstFileIndex >= numFilesInFolder) { throw new InvalidOperationException(); @@ -1476,21 +1476,21 @@ namespace SharpCompress.Common.SevenZip long skipSize = 0; for (int i = 0; i < skipCount; i++) { - skipSize += _db.Files[firstFileIndex + i].Size; + skipSize += db._files[firstFileIndex + i].Size; } - Stream s = GetCachedDecoderStream(_db, folderIndex); + Stream s = GetCachedDecoderStream(db, folderIndex); s.Position = skipSize; - return new ReadOnlySubStream(s, _db.Files[fileIndex].Size); + return new ReadOnlySubStream(s, db._files[fileIndex].Size); } - public void Extract(ArchiveDatabase _db, int[] indices) + public void Extract(ArchiveDatabase db, int[] indices) { int numItems; bool allFilesMode = (indices == null); if (allFilesMode) { - numItems = _db.Files.Count; + numItems = db._files.Count; } else { @@ -1507,62 +1507,62 @@ namespace SharpCompress.Common.SevenZip { int fileIndex = allFilesMode ? i : indices[i]; - int folderIndex = _db.FileIndexToFolderIndexMap[fileIndex]; + int folderIndex = db._fileIndexToFolderIndexMap[fileIndex]; if (folderIndex == -1) { extractFolderInfoVector.Add(new CExtractFolderInfo(fileIndex, -1)); continue; } - if (extractFolderInfoVector.Count == 0 || folderIndex != extractFolderInfoVector.Last().FolderIndex) + if (extractFolderInfoVector.Count == 0 || folderIndex != extractFolderInfoVector.Last()._folderIndex) { extractFolderInfoVector.Add(new CExtractFolderInfo(-1, folderIndex)); } CExtractFolderInfo efi = extractFolderInfoVector.Last(); - int startIndex = _db.FolderStartFileIndex[folderIndex]; - for (int index = efi.ExtractStatuses.Count; index <= fileIndex - startIndex; index++) + int startIndex = db._folderStartFileIndex[folderIndex]; + for (int index = efi._extractStatuses.Count; index <= fileIndex - startIndex; index++) { - efi.ExtractStatuses.Add(index == fileIndex - startIndex); + efi._extractStatuses.Add(index == fileIndex - startIndex); } } foreach (CExtractFolderInfo efi in extractFolderInfoVector) { int startIndex; - if (efi.FileIndex != -1) + if (efi._fileIndex != -1) { - startIndex = efi.FileIndex; + startIndex = efi._fileIndex; } else { - startIndex = _db.FolderStartFileIndex[efi.FolderIndex]; + startIndex = db._folderStartFileIndex[efi._folderIndex]; } - var outStream = new FolderUnpackStream(_db, 0, startIndex, efi.ExtractStatuses); + var outStream = new FolderUnpackStream(db, 0, startIndex, efi._extractStatuses); - if (efi.FileIndex != -1) + if (efi._fileIndex != -1) { continue; } - int folderIndex = efi.FolderIndex; - CFolder folderInfo = _db.Folders[folderIndex]; + int folderIndex = efi._folderIndex; + CFolder folderInfo = db._folders[folderIndex]; - int packStreamIndex = _db.Folders[folderIndex].FirstPackStreamId; - long folderStartPackPos = _db.GetFolderStreamPos(folderInfo, 0); + int packStreamIndex = db._folders[folderIndex]._firstPackStreamId; + long folderStartPackPos = db.GetFolderStreamPos(folderInfo, 0); List packSizes = new List(); - for (int j = 0; j < folderInfo.PackStreams.Count; j++) + for (int j = 0; j < folderInfo._packStreams.Count; j++) { - packSizes.Add(_db.PackSizes[packStreamIndex + j]); + packSizes.Add(db._packSizes[packStreamIndex + j]); } // TODO: If the decoding fails the last file may be extracted incompletely. Delete it? Stream s = DecoderStreamHelper.CreateDecoderStream(_stream, folderStartPackPos, packSizes.ToArray(), - folderInfo, _db.PasswordProvider); + folderInfo, db.PasswordProvider); byte[] buffer = new byte[4 << 10]; for (;;) { @@ -1578,12 +1578,12 @@ namespace SharpCompress.Common.SevenZip public IEnumerable GetFiles(ArchiveDatabase db) { - return db.Files; + return db._files; } public int GetFileIndex(ArchiveDatabase db, CFileItem item) { - return db.Files.IndexOf(item); + return db._files.IndexOf(item); } #endregion diff --git a/src/SharpCompress/Common/SevenZip/CBindPair.cs b/src/SharpCompress/Common/SevenZip/CBindPair.cs index ca52cdbe..ad947967 100644 --- a/src/SharpCompress/Common/SevenZip/CBindPair.cs +++ b/src/SharpCompress/Common/SevenZip/CBindPair.cs @@ -2,7 +2,7 @@ { internal class CBindPair { - internal int InIndex; - internal int OutIndex; + internal int _inIndex; + internal int _outIndex; } } \ No newline at end of file diff --git a/src/SharpCompress/Common/SevenZip/CCoderInfo.cs b/src/SharpCompress/Common/SevenZip/CCoderInfo.cs index 75ae4781..03572299 100644 --- a/src/SharpCompress/Common/SevenZip/CCoderInfo.cs +++ b/src/SharpCompress/Common/SevenZip/CCoderInfo.cs @@ -2,9 +2,9 @@ { internal class CCoderInfo { - internal CMethodId MethodId; - internal byte[] Props; - internal int NumInStreams; - internal int NumOutStreams; + internal CMethodId _methodId; + internal byte[] _props; + internal int _numInStreams; + internal int _numOutStreams; } } \ No newline at end of file diff --git a/src/SharpCompress/Common/SevenZip/CFolder.cs b/src/SharpCompress/Common/SevenZip/CFolder.cs index 4606d9dc..4a68cf50 100644 --- a/src/SharpCompress/Common/SevenZip/CFolder.cs +++ b/src/SharpCompress/Common/SevenZip/CFolder.cs @@ -6,27 +6,27 @@ namespace SharpCompress.Common.SevenZip { internal class CFolder { - internal List Coders = new List(); - internal List BindPairs = new List(); - internal List PackStreams = new List(); - internal int FirstPackStreamId; - internal List UnpackSizes = new List(); - internal uint? UnpackCRC; + internal List _coders = new List(); + internal List _bindPairs = new List(); + internal List _packStreams = new List(); + internal int _firstPackStreamId; + internal List _unpackSizes = new List(); + internal uint? _unpackCrc; - internal bool UnpackCRCDefined => UnpackCRC != null; + internal bool UnpackCrcDefined => _unpackCrc != null; public long GetUnpackSize() { - if (UnpackSizes.Count == 0) + if (_unpackSizes.Count == 0) { return 0; } - for (int i = UnpackSizes.Count - 1; i >= 0; i--) + for (int i = _unpackSizes.Count - 1; i >= 0; i--) { if (FindBindPairForOutStream(i) < 0) { - return UnpackSizes[i]; + return _unpackSizes[i]; } } @@ -36,9 +36,9 @@ namespace SharpCompress.Common.SevenZip public int GetNumOutStreams() { int count = 0; - for (int i = 0; i < Coders.Count; i++) + for (int i = 0; i < _coders.Count; i++) { - count += Coders[i].NumOutStreams; + count += _coders[i]._numOutStreams; } return count; @@ -46,9 +46,9 @@ namespace SharpCompress.Common.SevenZip public int FindBindPairForInStream(int inStreamIndex) { - for (int i = 0; i < BindPairs.Count; i++) + for (int i = 0; i < _bindPairs.Count; i++) { - if (BindPairs[i].InIndex == inStreamIndex) + if (_bindPairs[i]._inIndex == inStreamIndex) { return i; } @@ -59,9 +59,9 @@ namespace SharpCompress.Common.SevenZip public int FindBindPairForOutStream(int outStreamIndex) { - for (int i = 0; i < BindPairs.Count; i++) + for (int i = 0; i < _bindPairs.Count; i++) { - if (BindPairs[i].OutIndex == outStreamIndex) + if (_bindPairs[i]._outIndex == outStreamIndex) { return i; } @@ -72,9 +72,9 @@ namespace SharpCompress.Common.SevenZip public int FindPackStreamArrayIndex(int inStreamIndex) { - for (int i = 0; i < PackStreams.Count; i++) + for (int i = 0; i < _packStreams.Count; i++) { - if (PackStreams[i] == inStreamIndex) + if (_packStreams[i] == inStreamIndex) { return i; } @@ -85,9 +85,9 @@ namespace SharpCompress.Common.SevenZip public bool IsEncrypted() { - for (int i = Coders.Count - 1; i >= 0; i--) + for (int i = _coders.Count - 1; i >= 0; i--) { - if (Coders[i].MethodId == CMethodId.kAES) + if (_coders[i]._methodId == CMethodId.K_AES) { return true; } @@ -102,25 +102,25 @@ namespace SharpCompress.Common.SevenZip const int kMaskSize = 32; // it must be >= kNumCodersMax const int kNumBindsMax = 32; - if (Coders.Count > kNumCodersMax || BindPairs.Count > kNumBindsMax) + if (_coders.Count > kNumCodersMax || _bindPairs.Count > kNumBindsMax) { return false; } { - var v = new BitVector(BindPairs.Count + PackStreams.Count); + var v = new BitVector(_bindPairs.Count + _packStreams.Count); - for (int i = 0; i < BindPairs.Count; i++) + for (int i = 0; i < _bindPairs.Count; i++) { - if (v.GetAndSet(BindPairs[i].InIndex)) + if (v.GetAndSet(_bindPairs[i]._inIndex)) { return false; } } - for (int i = 0; i < PackStreams.Count; i++) + for (int i = 0; i < _packStreams.Count; i++) { - if (v.GetAndSet(PackStreams[i])) + if (v.GetAndSet(_packStreams[i])) { return false; } @@ -128,10 +128,10 @@ namespace SharpCompress.Common.SevenZip } { - var v = new BitVector(UnpackSizes.Count); - for (int i = 0; i < BindPairs.Count; i++) + var v = new BitVector(_unpackSizes.Count); + for (int i = 0; i < _bindPairs.Count; i++) { - if (v.GetAndSet(BindPairs[i].OutIndex)) + if (v.GetAndSet(_bindPairs[i]._outIndex)) { return false; } @@ -143,23 +143,23 @@ namespace SharpCompress.Common.SevenZip { List inStreamToCoder = new List(); List outStreamToCoder = new List(); - for (int i = 0; i < Coders.Count; i++) + for (int i = 0; i < _coders.Count; i++) { - CCoderInfo coder = Coders[i]; - for (int j = 0; j < coder.NumInStreams; j++) + CCoderInfo coder = _coders[i]; + for (int j = 0; j < coder._numInStreams; j++) { inStreamToCoder.Add(i); } - for (int j = 0; j < coder.NumOutStreams; j++) + for (int j = 0; j < coder._numOutStreams; j++) { outStreamToCoder.Add(i); } } - for (int i = 0; i < BindPairs.Count; i++) + for (int i = 0; i < _bindPairs.Count; i++) { - CBindPair bp = BindPairs[i]; - mask[inStreamToCoder[bp.InIndex]] |= (1u << outStreamToCoder[bp.OutIndex]); + CBindPair bp = _bindPairs[i]; + mask[inStreamToCoder[bp._inIndex]] |= (1u << outStreamToCoder[bp._outIndex]); } } diff --git a/src/SharpCompress/Common/SevenZip/CMethodId.cs b/src/SharpCompress/Common/SevenZip/CMethodId.cs index 685d28bb..ee9e3bb3 100644 --- a/src/SharpCompress/Common/SevenZip/CMethodId.cs +++ b/src/SharpCompress/Common/SevenZip/CMethodId.cs @@ -2,26 +2,26 @@ { internal struct CMethodId { - public const ulong kCopyId = 0; - public const ulong kLzmaId = 0x030101; - public const ulong kLzma2Id = 0x21; - public const ulong kAESId = 0x06F10701; + public const ulong K_COPY_ID = 0; + public const ulong K_LZMA_ID = 0x030101; + public const ulong K_LZMA2_ID = 0x21; + public const ulong K_AES_ID = 0x06F10701; - public static readonly CMethodId kCopy = new CMethodId(kCopyId); - public static readonly CMethodId kLzma = new CMethodId(kLzmaId); - public static readonly CMethodId kLzma2 = new CMethodId(kLzma2Id); - public static readonly CMethodId kAES = new CMethodId(kAESId); + public static readonly CMethodId K_COPY = new CMethodId(K_COPY_ID); + public static readonly CMethodId K_LZMA = new CMethodId(K_LZMA_ID); + public static readonly CMethodId K_LZMA2 = new CMethodId(K_LZMA2_ID); + public static readonly CMethodId K_AES = new CMethodId(K_AES_ID); - public readonly ulong Id; + public readonly ulong _id; public CMethodId(ulong id) { - Id = id; + _id = id; } public override int GetHashCode() { - return Id.GetHashCode(); + return _id.GetHashCode(); } public override bool Equals(object obj) @@ -31,23 +31,23 @@ public bool Equals(CMethodId other) { - return Id == other.Id; + return _id == other._id; } public static bool operator ==(CMethodId left, CMethodId right) { - return left.Id == right.Id; + return left._id == right._id; } public static bool operator !=(CMethodId left, CMethodId right) { - return left.Id != right.Id; + return left._id != right._id; } public int GetLength() { int bytes = 0; - for (ulong value = Id; value != 0; value >>= 8) + for (ulong value = _id; value != 0; value >>= 8) { bytes++; } diff --git a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs index f62d9e32..3c5232b2 100644 --- a/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs +++ b/src/SharpCompress/Common/SevenZip/SevenZipFilePart.cs @@ -20,7 +20,7 @@ namespace SharpCompress.Common.SevenZip Header = fileEntry; if (Header.HasStream) { - Folder = database.Folders[database.FileIndexToFolderIndexMap[index]]; + Folder = database._folders[database._fileIndexToFolderIndexMap[index]]; } } @@ -43,12 +43,12 @@ namespace SharpCompress.Common.SevenZip } var folderStream = _database.GetFolderStream(_stream, Folder, _database.PasswordProvider); - int firstFileIndex = _database.FolderStartFileIndex[_database.Folders.IndexOf(Folder)]; + int firstFileIndex = _database._folderStartFileIndex[_database._folders.IndexOf(Folder)]; int skipCount = Index - firstFileIndex; long skipSize = 0; for (int i = 0; i < skipCount; i++) { - skipSize += _database.Files[firstFileIndex + i].Size; + skipSize += _database._files[firstFileIndex + i].Size; } if (skipSize > 0) { @@ -70,31 +70,31 @@ namespace SharpCompress.Common.SevenZip } //copied from DecoderRegistry - private const uint k_Copy = 0x0; - private const uint k_Delta = 3; - private const uint k_LZMA2 = 0x21; - private const uint k_LZMA = 0x030101; - private const uint k_PPMD = 0x030401; - private const uint k_BCJ = 0x03030103; - private const uint k_BCJ2 = 0x0303011B; - private const uint k_Deflate = 0x040108; - private const uint k_BZip2 = 0x040202; + private const uint K_COPY = 0x0; + private const uint K_DELTA = 3; + private const uint K_LZMA2 = 0x21; + private const uint K_LZMA = 0x030101; + private const uint K_PPMD = 0x030401; + private const uint K_BCJ = 0x03030103; + private const uint K_BCJ2 = 0x0303011B; + private const uint K_DEFLATE = 0x040108; + private const uint K_B_ZIP2 = 0x040202; internal CompressionType GetCompression() { - var coder = Folder.Coders.First(); - switch (coder.MethodId.Id) + var coder = Folder._coders.First(); + switch (coder._methodId._id) { - case k_LZMA: - case k_LZMA2: + case K_LZMA: + case K_LZMA2: { return CompressionType.LZMA; } - case k_PPMD: + case K_PPMD: { return CompressionType.PPMd; } - case k_BZip2: + case K_B_ZIP2: { return CompressionType.BZip2; } diff --git a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs index a03495f0..9f7f1536 100644 --- a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs +++ b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs @@ -7,7 +7,7 @@ namespace SharpCompress.Common.Tar.Headers { internal class TarHeader { - internal static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + internal static readonly DateTime EPOCH = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); public TarHeader(ArchiveEncoding archiveEncoding) { @@ -27,11 +27,11 @@ namespace SharpCompress.Common.Tar.Headers internal Stream PackedStream { get; set; } internal ArchiveEncoding ArchiveEncoding { get; } - internal const int BlockSize = 512; + internal const int BLOCK_SIZE = 512; internal void Write(Stream output) { - byte[] buffer = new byte[BlockSize]; + byte[] buffer = new byte[BLOCK_SIZE]; WriteOctalBytes(511, buffer, 100, 8); // file mode WriteOctalBytes(0, buffer, 108, 8); // owner ID @@ -49,7 +49,7 @@ namespace SharpCompress.Common.Tar.Headers { WriteStringBytes(Name, buffer, 0, 100); WriteOctalBytes(Size, buffer, 124, 12); - var time = (long)(LastModifiedTime.ToUniversalTime() - Epoch).TotalSeconds; + var time = (long)(LastModifiedTime.ToUniversalTime() - EPOCH).TotalSeconds; WriteOctalBytes(time, buffer, 136, 12); buffer[156] = (byte)EntryType; @@ -82,10 +82,10 @@ namespace SharpCompress.Common.Tar.Headers output.Write(nameBytes, 0, nameBytes.Length); // pad to multiple of BlockSize bytes, and make sure a terminating null is added - int numPaddingBytes = BlockSize - (nameBytes.Length % BlockSize); + int numPaddingBytes = BLOCK_SIZE - (nameBytes.Length % BLOCK_SIZE); if (numPaddingBytes == 0) { - numPaddingBytes = BlockSize; + numPaddingBytes = BLOCK_SIZE; } output.Write(new byte[numPaddingBytes], 0, numPaddingBytes); } @@ -114,8 +114,8 @@ namespace SharpCompress.Common.Tar.Headers //Mode = ReadASCIIInt32Base8(buffer, 100, 7); //UserId = ReadASCIIInt32Base8(buffer, 108, 7); //GroupId = ReadASCIIInt32Base8(buffer, 116, 7); - long unixTimeStamp = ReadASCIIInt64Base8(buffer, 136, 11); - LastModifiedTime = Epoch.AddSeconds(unixTimeStamp).ToLocalTime(); + long unixTimeStamp = ReadAsciiInt64Base8(buffer, 136, 11); + LastModifiedTime = EPOCH.AddSeconds(unixTimeStamp).ToLocalTime(); Magic = ArchiveEncoding.Decode(buffer, 257, 6).TrimNulls(); @@ -142,10 +142,10 @@ namespace SharpCompress.Common.Tar.Headers var size = ReadSize(buffer); var nameLength = (int)size; var nameBytes = reader.ReadBytes(nameLength); - var remainingBytesToRead = BlockSize - (nameLength % BlockSize); + var remainingBytesToRead = BLOCK_SIZE - (nameLength % BLOCK_SIZE); // Read the rest of the block and discard the data - if (remainingBytesToRead < BlockSize) + if (remainingBytesToRead < BLOCK_SIZE) { reader.ReadBytes(remainingBytesToRead); } @@ -163,14 +163,14 @@ namespace SharpCompress.Common.Tar.Headers { return DataConverter.BigEndian.GetInt64(buffer, 0x80); } - return ReadASCIIInt64Base8(buffer, 124, 11); + return ReadAsciiInt64Base8(buffer, 124, 11); } private static byte[] ReadBlock(BinaryReader reader) { - byte[] buffer = reader.ReadBytes(BlockSize); + byte[] buffer = reader.ReadBytes(BLOCK_SIZE); - if (buffer.Length != 0 && buffer.Length < BlockSize) + if (buffer.Length != 0 && buffer.Length < BLOCK_SIZE) { throw new InvalidOperationException("Buffer is invalid size"); } @@ -206,7 +206,7 @@ namespace SharpCompress.Common.Tar.Headers } } - private static int ReadASCIIInt32Base8(byte[] buffer, int offset, int count) + private static int ReadAsciiInt32Base8(byte[] buffer, int offset, int count) { string s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls(); if (string.IsNullOrEmpty(s)) @@ -216,7 +216,7 @@ namespace SharpCompress.Common.Tar.Headers return Convert.ToInt32(s, 8); } - private static long ReadASCIIInt64Base8(byte[] buffer, int offset, int count) + private static long ReadAsciiInt64Base8(byte[] buffer, int offset, int count) { string s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls(); if (string.IsNullOrEmpty(s)) @@ -226,7 +226,7 @@ namespace SharpCompress.Common.Tar.Headers return Convert.ToInt64(s, 8); } - private static long ReadASCIIInt64(byte[] buffer, int offset, int count) + private static long ReadAsciiInt64(byte[] buffer, int offset, int count) { string s = Encoding.UTF8.GetString(buffer, offset, count).TrimNulls(); if (string.IsNullOrEmpty(s)) diff --git a/src/SharpCompress/Common/Tar/TarEntry.cs b/src/SharpCompress/Common/Tar/TarEntry.cs index 6ff6e19f..82e707d0 100644 --- a/src/SharpCompress/Common/Tar/TarEntry.cs +++ b/src/SharpCompress/Common/Tar/TarEntry.cs @@ -9,11 +9,11 @@ namespace SharpCompress.Common.Tar { public class TarEntry : Entry { - private readonly TarFilePart filePart; + private readonly TarFilePart _filePart; internal TarEntry(TarFilePart filePart, CompressionType type) { - this.filePart = filePart; + this._filePart = filePart; CompressionType = type; } @@ -21,13 +21,13 @@ namespace SharpCompress.Common.Tar public override long Crc => 0; - public override string Key => filePart.Header.Name; + public override string Key => _filePart.Header.Name; - public override long CompressedSize => filePart.Header.Size; + public override long CompressedSize => _filePart.Header.Size; - public override long Size => filePart.Header.Size; + public override long Size => _filePart.Header.Size; - public override DateTime? LastModifiedTime => filePart.Header.LastModifiedTime; + public override DateTime? LastModifiedTime => _filePart.Header.LastModifiedTime; public override DateTime? CreatedTime => null; @@ -37,11 +37,11 @@ namespace SharpCompress.Common.Tar public override bool IsEncrypted => false; - public override bool IsDirectory => filePart.Header.EntryType == EntryType.Directory; + public override bool IsDirectory => _filePart.Header.EntryType == EntryType.Directory; public override bool IsSplitAfter => false; - internal override IEnumerable Parts => filePart.AsEnumerable(); + internal override IEnumerable Parts => _filePart.AsEnumerable(); internal static IEnumerable GetEntries(StreamingMode mode, Stream stream, CompressionType compressionType, ArchiveEncoding archiveEncoding) diff --git a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs index 337dad74..d7ea3040 100644 --- a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs +++ b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs @@ -5,8 +5,8 @@ namespace SharpCompress.Common.Tar { internal class TarReadOnlySubStream : Stream { - private bool isDisposed; - private long amountRead; + private bool _isDisposed; + private long _amountRead; public TarReadOnlySubStream(Stream stream, long bytesToRead) { @@ -16,14 +16,14 @@ namespace SharpCompress.Common.Tar protected override void Dispose(bool disposing) { - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; + _isDisposed = true; if (disposing) { - long skipBytes = amountRead % 512; + long skipBytes = _amountRead % 512; if (skipBytes == 0) { return; @@ -67,7 +67,7 @@ namespace SharpCompress.Common.Tar if (read > 0) { BytesLeftToRead -= read; - amountRead += read; + _amountRead += read; } return read; } diff --git a/src/SharpCompress/Common/Volume.cs b/src/SharpCompress/Common/Volume.cs index eab39d4d..86d968c0 100644 --- a/src/SharpCompress/Common/Volume.cs +++ b/src/SharpCompress/Common/Volume.cs @@ -6,7 +6,7 @@ namespace SharpCompress.Common { public abstract class Volume : IVolume { - private readonly Stream actualStream; + private readonly Stream _actualStream; internal Volume(Stream stream, ReaderOptions readerOptions) { @@ -15,10 +15,10 @@ namespace SharpCompress.Common { stream = new NonDisposingStream(stream); } - actualStream = stream; + _actualStream = stream; } - internal Stream Stream => actualStream; + internal Stream Stream => _actualStream; protected ReaderOptions ReaderOptions { get; } @@ -33,14 +33,14 @@ namespace SharpCompress.Common /// public virtual bool IsMultiVolume => true; - private bool disposed; + private bool _disposed; public void Dispose() { - if (!disposed) + if (!_disposed) { - actualStream.Dispose(); - disposed = true; + _actualStream.Dispose(); + _disposed = true; } } } diff --git a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs index c4002e22..a71d311d 100644 --- a/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/DirectoryEntryHeader.cs @@ -34,7 +34,7 @@ namespace SharpCompress.Common.Zip.Headers byte[] extra = reader.ReadBytes(extraLength); byte[] comment = reader.ReadBytes(commentLength); - if (Flags.HasFlag(HeaderFlags.EFS)) + if (Flags.HasFlag(HeaderFlags.Efs)) { Name = ArchiveEncoding.Decode(name); Comment = ArchiveEncoding.Decode(comment); diff --git a/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs b/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs index b5f363d7..92aaaca7 100644 --- a/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs +++ b/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs @@ -12,6 +12,6 @@ namespace SharpCompress.Common.Zip.Headers UsePostDataDescriptor = 8, EnhancedDeflate = 16, //Bit 11: Language encoding flag - EFS = 2048 + Efs = 2048 } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs index 4b9e8178..e56b6102 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs @@ -26,7 +26,7 @@ namespace SharpCompress.Common.Zip.Headers byte[] name = reader.ReadBytes(nameLength); byte[] extra = reader.ReadBytes(extraLength); - if (Flags.HasFlag(HeaderFlags.EFS)) + if (Flags.HasFlag(HeaderFlags.Efs)) { Name = ArchiveEncoding.Decode(name); } diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs index 23ae243a..b86d5052 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeaderExtraFactory.cs @@ -27,7 +27,7 @@ namespace SharpCompress.Common.Zip.Headers { internal byte Version => DataBytes[0]; - internal byte[] NameCRC32 + internal byte[] NameCrc32 { get { diff --git a/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs b/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs index 41a74ef2..443cbd8a 100644 --- a/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/Zip64DirectoryEndHeader.cs @@ -21,10 +21,10 @@ namespace SharpCompress.Common.Zip.Headers TotalNumberOfEntries = (long)reader.ReadUInt64(); DirectorySize = (long)reader.ReadUInt64(); DirectoryStartOffsetRelativeToDisk = (long)reader.ReadUInt64(); - DataSector = reader.ReadBytes((int)(SizeOfDirectoryEndRecord - SizeOfFixedHeaderDataExceptSignatureAndSizeFields)); + DataSector = reader.ReadBytes((int)(SizeOfDirectoryEndRecord - SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS)); } - private const int SizeOfFixedHeaderDataExceptSignatureAndSizeFields = 44; + private const int SIZE_OF_FIXED_HEADER_DATA_EXCEPT_SIGNATURE_AND_SIZE_FIELDS = 44; public long SizeOfDirectoryEndRecord { get; private set; } diff --git a/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs b/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs index 2d412242..2c3f1400 100644 --- a/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs +++ b/src/SharpCompress/Common/Zip/PkwareTraditionalCryptoStream.cs @@ -11,23 +11,23 @@ namespace SharpCompress.Common.Zip internal class PkwareTraditionalCryptoStream : Stream { - private readonly PkwareTraditionalEncryptionData encryptor; - private readonly CryptoMode mode; - private readonly Stream stream; - private bool isDisposed; + private readonly PkwareTraditionalEncryptionData _encryptor; + private readonly CryptoMode _mode; + private readonly Stream _stream; + private bool _isDisposed; public PkwareTraditionalCryptoStream(Stream stream, PkwareTraditionalEncryptionData encryptor, CryptoMode mode) { - this.encryptor = encryptor; - this.stream = stream; - this.mode = mode; + this._encryptor = encryptor; + this._stream = stream; + this._mode = mode; } - public override bool CanRead => (mode == CryptoMode.Decrypt); + public override bool CanRead => (_mode == CryptoMode.Decrypt); public override bool CanSeek => false; - public override bool CanWrite => (mode == CryptoMode.Encrypt); + public override bool CanWrite => (_mode == CryptoMode.Encrypt); public override long Length => throw new NotSupportedException(); @@ -35,7 +35,7 @@ namespace SharpCompress.Common.Zip public override int Read(byte[] buffer, int offset, int count) { - if (mode == CryptoMode.Encrypt) + if (_mode == CryptoMode.Encrypt) { throw new NotSupportedException("This stream does not encrypt via Read()"); } @@ -46,15 +46,15 @@ namespace SharpCompress.Common.Zip } byte[] temp = new byte[count]; - int readBytes = stream.Read(temp, 0, count); - byte[] decrypted = encryptor.Decrypt(temp, readBytes); + int readBytes = _stream.Read(temp, 0, count); + byte[] decrypted = _encryptor.Decrypt(temp, readBytes); Buffer.BlockCopy(decrypted, 0, buffer, offset, readBytes); return readBytes; } public override void Write(byte[] buffer, int offset, int count) { - if (mode == CryptoMode.Decrypt) + if (_mode == CryptoMode.Decrypt) { throw new NotSupportedException("This stream does not Decrypt via Write()"); } @@ -75,8 +75,8 @@ namespace SharpCompress.Common.Zip plaintext = buffer; } - byte[] encrypted = encryptor.Encrypt(plaintext, count); - stream.Write(encrypted, 0, encrypted.Length); + byte[] encrypted = _encryptor.Encrypt(plaintext, count); + _stream.Write(encrypted, 0, encrypted.Length); } public override void Flush() @@ -96,13 +96,13 @@ namespace SharpCompress.Common.Zip protected override void Dispose(bool disposing) { - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; + _isDisposed = true; base.Dispose(disposing); - stream.Dispose(); + _stream.Dispose(); } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs b/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs index a423176b..2a328003 100644 --- a/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs +++ b/src/SharpCompress/Common/Zip/PkwareTraditionalEncryptionData.cs @@ -7,8 +7,8 @@ namespace SharpCompress.Common.Zip { internal class PkwareTraditionalEncryptionData { - private static readonly CRC32 crc32 = new CRC32(); - private readonly UInt32[] _Keys = {0x12345678, 0x23456789, 0x34567890}; + private static readonly CRC32 CRC32 = new CRC32(); + private readonly UInt32[] _keys = {0x12345678, 0x23456789, 0x34567890}; private readonly ArchiveEncoding _archiveEncoding; private PkwareTraditionalEncryptionData(string password, ArchiveEncoding archiveEncoding) @@ -21,7 +21,7 @@ namespace SharpCompress.Common.Zip { get { - ushort t = (ushort)((ushort)(_Keys[2] & 0xFFFF) | 2); + ushort t = (ushort)((ushort)(_keys[2] & 0xFFFF) | 2); return (byte)((t * (t ^ 1)) >> 8); } } @@ -56,9 +56,9 @@ namespace SharpCompress.Common.Zip var plainText = new byte[length]; for (int i = 0; i < length; i++) { - var C = (byte)(cipherText[i] ^ MagicByte); - UpdateKeys(C); - plainText[i] = C; + var c = (byte)(cipherText[i] ^ MagicByte); + UpdateKeys(c); + plainText[i] = c; } return plainText; } @@ -79,9 +79,9 @@ namespace SharpCompress.Common.Zip var cipherText = new byte[length]; for (int i = 0; i < length; i++) { - byte C = plainText[i]; + byte c = plainText[i]; cipherText[i] = (byte)(plainText[i] ^ MagicByte); - UpdateKeys(C); + UpdateKeys(c); } return cipherText; } @@ -103,10 +103,10 @@ namespace SharpCompress.Common.Zip private void UpdateKeys(byte byteValue) { - _Keys[0] = (UInt32)crc32.ComputeCrc32((int)_Keys[0], byteValue); - _Keys[1] = _Keys[1] + (byte)_Keys[0]; - _Keys[1] = _Keys[1] * 0x08088405 + 1; - _Keys[2] = (UInt32)crc32.ComputeCrc32((int)_Keys[2], (byte)(_Keys[1] >> 24)); + _keys[0] = (UInt32)CRC32.ComputeCrc32((int)_keys[0], byteValue); + _keys[1] = _keys[1] + (byte)_keys[0]; + _keys[1] = _keys[1] * 0x08088405 + 1; + _keys[2] = (UInt32)CRC32.ComputeCrc32((int)_keys[2], (byte)(_keys[1] >> 24)); } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs b/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs index a528c137..60715413 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipFilePart.cs @@ -7,7 +7,7 @@ namespace SharpCompress.Common.Zip { internal class StreamingZipFilePart : ZipFilePart { - private Stream decompressionStream; + private Stream _decompressionStream; internal StreamingZipFilePart(ZipFileEntry header, Stream stream) : base(header, stream) @@ -25,12 +25,12 @@ namespace SharpCompress.Common.Zip { return Stream.Null; } - decompressionStream = CreateDecompressionStream(GetCryptoStream(CreateBaseStream()), Header.CompressionMethod); + _decompressionStream = CreateDecompressionStream(GetCryptoStream(CreateBaseStream()), Header.CompressionMethod); if (LeaveStreamOpen) { - return new NonDisposingStream(decompressionStream); + return new NonDisposingStream(_decompressionStream); } - return decompressionStream; + return _decompressionStream; } internal BinaryReader FixStreamedFileLocation(ref RewindableStream rewindableStream) @@ -41,13 +41,13 @@ namespace SharpCompress.Common.Zip } if (Header.HasData && !Skipped) { - if (decompressionStream == null) + if (_decompressionStream == null) { - decompressionStream = GetCompressedStream(); + _decompressionStream = GetCompressedStream(); } - decompressionStream.Skip(); + _decompressionStream.Skip(); - DeflateStream deflateStream = decompressionStream as DeflateStream; + DeflateStream deflateStream = _decompressionStream as DeflateStream; if (deflateStream != null) { rewindableStream.Rewind(deflateStream.InputBuffer); @@ -55,7 +55,7 @@ namespace SharpCompress.Common.Zip Skipped = true; } var reader = new BinaryReader(rewindableStream); - decompressionStream = null; + _decompressionStream = null; return reader; } } diff --git a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs index 1f4df93a..1148c53c 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs @@ -28,25 +28,25 @@ namespace SharpCompress.Common.Zip { ZipHeader header = null; BinaryReader reader = new BinaryReader(rewindableStream); - if (lastEntryHeader != null && - (FlagUtility.HasFlag(lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor) || lastEntryHeader.IsZip64)) + if (_lastEntryHeader != null && + (FlagUtility.HasFlag(_lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor) || _lastEntryHeader.IsZip64)) { - reader = (lastEntryHeader.Part as StreamingZipFilePart).FixStreamedFileLocation(ref rewindableStream); + reader = (_lastEntryHeader.Part as StreamingZipFilePart).FixStreamedFileLocation(ref rewindableStream); long? pos = rewindableStream.CanSeek ? (long?)rewindableStream.Position : null; uint crc = reader.ReadUInt32(); if (crc == POST_DATA_DESCRIPTOR) { crc = reader.ReadUInt32(); } - lastEntryHeader.Crc = crc; - lastEntryHeader.CompressedSize = reader.ReadUInt32(); - lastEntryHeader.UncompressedSize = reader.ReadUInt32(); + _lastEntryHeader.Crc = crc; + _lastEntryHeader.CompressedSize = reader.ReadUInt32(); + _lastEntryHeader.UncompressedSize = reader.ReadUInt32(); if (pos.HasValue) { - lastEntryHeader.DataStartPosition = pos - lastEntryHeader.CompressedSize; + _lastEntryHeader.DataStartPosition = pos - _lastEntryHeader.CompressedSize; } } - lastEntryHeader = null; + _lastEntryHeader = null; uint headerBytes = reader.ReadUInt32(); header = ReadHeader(headerBytes, reader); diff --git a/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs b/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs index e5618827..62e6cff2 100644 --- a/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs +++ b/src/SharpCompress/Common/Zip/WinzipAesCryptoStream.cs @@ -10,25 +10,25 @@ namespace SharpCompress.Common.Zip internal class WinzipAesCryptoStream : Stream { private const int BLOCK_SIZE_IN_BYTES = 16; - private readonly SymmetricAlgorithm cipher; - private readonly byte[] counter = new byte[BLOCK_SIZE_IN_BYTES]; - private readonly Stream stream; - private readonly ICryptoTransform transform; - private int nonce = 1; - private byte[] counterOut = new byte[BLOCK_SIZE_IN_BYTES]; - private bool isFinalBlock; - private long totalBytesLeftToRead; - private bool isDisposed; + private readonly SymmetricAlgorithm _cipher; + private readonly byte[] _counter = new byte[BLOCK_SIZE_IN_BYTES]; + private readonly Stream _stream; + private readonly ICryptoTransform _transform; + private int _nonce = 1; + private byte[] _counterOut = new byte[BLOCK_SIZE_IN_BYTES]; + private bool _isFinalBlock; + private long _totalBytesLeftToRead; + private bool _isDisposed; internal WinzipAesCryptoStream(Stream stream, WinzipAesEncryptionData winzipAesEncryptionData, long length) { - this.stream = stream; - totalBytesLeftToRead = length; + this._stream = stream; + _totalBytesLeftToRead = length; - cipher = CreateCipher(winzipAesEncryptionData); + _cipher = CreateCipher(winzipAesEncryptionData); var iv = new byte[BLOCK_SIZE_IN_BYTES]; - transform = cipher.CreateEncryptor(winzipAesEncryptionData.KeyBytes, iv); + _transform = _cipher.CreateEncryptor(winzipAesEncryptionData.KeyBytes, iv); } private SymmetricAlgorithm CreateCipher(WinzipAesEncryptionData winzipAesEncryptionData) @@ -69,17 +69,17 @@ namespace SharpCompress.Common.Zip protected override void Dispose(bool disposing) { - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; + _isDisposed = true; if (disposing) { //read out last 10 auth bytes var ten = new byte[10]; - stream.ReadFully(ten); - stream.Dispose(); + _stream.ReadFully(ten); + _stream.Dispose(); } } @@ -90,17 +90,17 @@ namespace SharpCompress.Common.Zip public override int Read(byte[] buffer, int offset, int count) { - if (totalBytesLeftToRead == 0) + if (_totalBytesLeftToRead == 0) { return 0; } int bytesToRead = count; - if (count > totalBytesLeftToRead) + if (count > _totalBytesLeftToRead) { - bytesToRead = (int)totalBytesLeftToRead; + bytesToRead = (int)_totalBytesLeftToRead; } - int read = stream.Read(buffer, offset, bytesToRead); - totalBytesLeftToRead -= read; + int read = _stream.Read(buffer, offset, bytesToRead); + _totalBytesLeftToRead -= read; ReadTransformBlocks(buffer, offset, read); @@ -109,7 +109,7 @@ namespace SharpCompress.Common.Zip private int ReadTransformOneBlock(byte[] buffer, int offset, int last) { - if (isFinalBlock) + if (_isFinalBlock) { throw new InvalidOperationException(); } @@ -120,22 +120,22 @@ namespace SharpCompress.Common.Zip : bytesRemaining; // update the counter - DataConverter.LittleEndian.PutBytes(counter, 0, nonce++); + DataConverter.LittleEndian.PutBytes(_counter, 0, _nonce++); // Determine if this is the final block - if ((bytesToRead == bytesRemaining) && (totalBytesLeftToRead == 0)) + if ((bytesToRead == bytesRemaining) && (_totalBytesLeftToRead == 0)) { - counterOut = transform.TransformFinalBlock(counter, + _counterOut = _transform.TransformFinalBlock(_counter, 0, BLOCK_SIZE_IN_BYTES); - isFinalBlock = true; + _isFinalBlock = true; } else { - transform.TransformBlock(counter, + _transform.TransformBlock(_counter, 0, // offset BLOCK_SIZE_IN_BYTES, - counterOut, + _counterOut, 0); // offset } @@ -148,7 +148,7 @@ namespace SharpCompress.Common.Zip { for (int i = 0; i < count; i++) { - buffer[offset + i] = (byte)(counterOut[i] ^ buffer[offset + i]); + buffer[offset + i] = (byte)(_counterOut[i] ^ buffer[offset + i]); } } diff --git a/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs b/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs index 0d85bafc..49f3dd24 100644 --- a/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs +++ b/src/SharpCompress/Common/Zip/WinzipAesEncryptionData.cs @@ -10,20 +10,20 @@ namespace SharpCompress.Common.Zip { private const int RFC2898_ITERATIONS = 1000; - private readonly byte[] salt; - private readonly WinzipAesKeySize keySize; - private readonly byte[] passwordVerifyValue; - private readonly string password; + private readonly byte[] _salt; + private readonly WinzipAesKeySize _keySize; + private readonly byte[] _passwordVerifyValue; + private readonly string _password; - private byte[] generatedVerifyValue; + private byte[] _generatedVerifyValue; internal WinzipAesEncryptionData(WinzipAesKeySize keySize, byte[] salt, byte[] passwordVerifyValue, string password) { - this.keySize = keySize; - this.salt = salt; - this.passwordVerifyValue = passwordVerifyValue; - this.password = password; + this._keySize = keySize; + this._salt = salt; + this._passwordVerifyValue = passwordVerifyValue; + this._password = password; Initialize(); } @@ -38,7 +38,7 @@ namespace SharpCompress.Common.Zip private int KeySizeInBytes { - get { return KeyLengthInBytes(keySize); + get { return KeyLengthInBytes(_keySize); } } @@ -58,16 +58,16 @@ namespace SharpCompress.Common.Zip private void Initialize() { - var rfc2898 = new Rfc2898DeriveBytes(password, salt, RFC2898_ITERATIONS); + var rfc2898 = new Rfc2898DeriveBytes(_password, _salt, RFC2898_ITERATIONS); KeyBytes = rfc2898.GetBytes(KeySizeInBytes); // 16 or 24 or 32 ??? IvBytes = rfc2898.GetBytes(KeySizeInBytes); - generatedVerifyValue = rfc2898.GetBytes(2); + _generatedVerifyValue = rfc2898.GetBytes(2); - short verify = DataConverter.LittleEndian.GetInt16(passwordVerifyValue, 0); - if (password != null) + short verify = DataConverter.LittleEndian.GetInt16(_passwordVerifyValue, 0); + if (_password != null) { - short generated = DataConverter.LittleEndian.GetInt16(generatedVerifyValue, 0); + short generated = DataConverter.LittleEndian.GetInt16(_generatedVerifyValue, 0); if (verify != generated) { throw new InvalidFormatException("bad password"); diff --git a/src/SharpCompress/Common/Zip/ZipEntry.cs b/src/SharpCompress/Common/Zip/ZipEntry.cs index 2db83216..379534ff 100644 --- a/src/SharpCompress/Common/Zip/ZipEntry.cs +++ b/src/SharpCompress/Common/Zip/ZipEntry.cs @@ -6,13 +6,13 @@ namespace SharpCompress.Common.Zip { public class ZipEntry : Entry { - private readonly ZipFilePart filePart; + private readonly ZipFilePart _filePart; internal ZipEntry(ZipFilePart filePart) { if (filePart != null) { - this.filePart = filePart; + this._filePart = filePart; LastModifiedTime = Utility.DosDateToDateTime(filePart.Header.LastModifiedDate, filePart.Header.LastModifiedTime); } @@ -22,7 +22,7 @@ namespace SharpCompress.Common.Zip { get { - switch (filePart.Header.CompressionMethod) + switch (_filePart.Header.CompressionMethod) { case ZipCompressionMethod.BZip2: { @@ -56,13 +56,13 @@ namespace SharpCompress.Common.Zip } } - public override long Crc => filePart.Header.Crc; + public override long Crc => _filePart.Header.Crc; - public override string Key => filePart.Header.Name; + public override string Key => _filePart.Header.Name; - public override long CompressedSize => filePart.Header.CompressedSize; + public override long CompressedSize => _filePart.Header.CompressedSize; - public override long Size => filePart.Header.UncompressedSize; + public override long Size => _filePart.Header.UncompressedSize; public override DateTime? LastModifiedTime { get; } @@ -72,12 +72,12 @@ namespace SharpCompress.Common.Zip public override DateTime? ArchivedTime => null; - public override bool IsEncrypted => FlagUtility.HasFlag(filePart.Header.Flags, HeaderFlags.Encrypted); + public override bool IsEncrypted => FlagUtility.HasFlag(_filePart.Header.Flags, HeaderFlags.Encrypted); - public override bool IsDirectory => filePart.Header.IsDirectory; + public override bool IsDirectory => _filePart.Header.IsDirectory; public override bool IsSplitAfter => false; - internal override IEnumerable Parts => filePart.AsEnumerable(); + internal override IEnumerable Parts => _filePart.AsEnumerable(); } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs index 9fcf9348..bc8187c2 100644 --- a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs @@ -21,16 +21,16 @@ namespace SharpCompress.Common.Zip internal const uint ZIP64_END_OF_CENTRAL_DIRECTORY = 0x06064b50; internal const uint ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR = 0x07064b50; - protected LocalEntryHeader lastEntryHeader; - private readonly string password; - private readonly StreamingMode mode; - private readonly ArchiveEncoding archiveEncoding; + protected LocalEntryHeader _lastEntryHeader; + private readonly string _password; + private readonly StreamingMode _mode; + private readonly ArchiveEncoding _archiveEncoding; protected ZipHeaderFactory(StreamingMode mode, string password, ArchiveEncoding archiveEncoding) { - this.mode = mode; - this.password = password; - this.archiveEncoding = archiveEncoding; + this._mode = mode; + this._password = password; + this._archiveEncoding = archiveEncoding; } protected ZipHeader ReadHeader(uint headerBytes, BinaryReader reader, bool zip64 = false) @@ -39,26 +39,26 @@ namespace SharpCompress.Common.Zip { case ENTRY_HEADER_BYTES: { - var entryHeader = new LocalEntryHeader(archiveEncoding); + var entryHeader = new LocalEntryHeader(_archiveEncoding); entryHeader.Read(reader); LoadHeader(entryHeader, reader.BaseStream); - lastEntryHeader = entryHeader; + _lastEntryHeader = entryHeader; return entryHeader; } case DIRECTORY_START_HEADER_BYTES: { - var entry = new DirectoryEntryHeader(archiveEncoding); + var entry = new DirectoryEntryHeader(_archiveEncoding); entry.Read(reader); return entry; } case POST_DATA_DESCRIPTOR: { - if (FlagUtility.HasFlag(lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) + if (FlagUtility.HasFlag(_lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) { - lastEntryHeader.Crc = reader.ReadUInt32(); - lastEntryHeader.CompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32(); - lastEntryHeader.UncompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32(); + _lastEntryHeader.Crc = reader.ReadUInt32(); + _lastEntryHeader.CompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32(); + _lastEntryHeader.UncompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32(); } else { @@ -123,12 +123,12 @@ namespace SharpCompress.Common.Zip throw new NotSupportedException("SharpCompress cannot currently read non-seekable Zip Streams with encrypted data that has been written in a non-seekable manner."); } - if (password == null) + if (_password == null) { throw new CryptographicException("No password supplied for encrypted zip."); } - entryHeader.Password = password; + entryHeader.Password = _password; if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes) { @@ -146,7 +146,7 @@ namespace SharpCompress.Common.Zip stream.Read(salt, 0, salt.Length); stream.Read(passwordVerifyValue, 0, 2); entryHeader.WinzipAesEncryptionData = - new WinzipAesEncryptionData(keySize, salt, passwordVerifyValue, password); + new WinzipAesEncryptionData(keySize, salt, passwordVerifyValue, _password); entryHeader.CompressedSize -= (uint)(salt.Length + 2); } @@ -165,7 +165,7 @@ namespace SharpCompress.Common.Zip //} //else //{ - switch (mode) + switch (_mode) { case StreamingMode.Seekable: { diff --git a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs index a7159122..e499eeb7 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs @@ -447,7 +447,7 @@ namespace SharpCompress.Compressors.Deflate } Int32 timet = DataConverter.LittleEndian.GetInt32(header, 4); - _GzipMtime = TarHeader.Epoch.AddSeconds(timet); + _GzipMtime = TarHeader.EPOCH.AddSeconds(timet); totalBytesRead += n; if ((header[3] & 0x04) == 0x04) { diff --git a/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs b/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs index 341dc6ff..be7300c2 100644 --- a/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs +++ b/src/SharpCompress/Compressors/Deflate64/Deflate64Stream.cs @@ -13,7 +13,7 @@ namespace SharpCompress.Compressors.Deflate64 { public sealed class Deflate64Stream : Stream { - private const int DefaultBufferSize = 8192; + private const int DEFAULT_BUFFER_SIZE = 8192; private Stream _stream; private CompressionMode _mode; @@ -35,7 +35,7 @@ namespace SharpCompress.Compressors.Deflate64 /// /// Sets up this DeflateManagedStream to be used for Inflation/Decompression /// - internal void InitializeInflater(Stream stream, ZipCompressionMethod method = ZipCompressionMethod.Deflate) + private void InitializeInflater(Stream stream, ZipCompressionMethod method = ZipCompressionMethod.Deflate) { Debug.Assert(stream != null); Debug.Assert(method == ZipCompressionMethod.Deflate || method == ZipCompressionMethod.Deflate64); @@ -46,7 +46,7 @@ namespace SharpCompress.Compressors.Deflate64 _stream = stream; _mode = CompressionMode.Decompress; - _buffer = new byte[DefaultBufferSize]; + _buffer = new byte[DEFAULT_BUFFER_SIZE]; } public override bool CanRead diff --git a/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs b/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs index 1e455a46..ecfae42b 100644 --- a/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs +++ b/src/SharpCompress/Compressors/Deflate64/FastEncoderStatus.cs @@ -10,7 +10,7 @@ namespace SharpCompress.Compressors.Deflate64 { // static information for encoding, DO NOT MODIFY - internal static readonly byte[] FastEncoderTreeStructureData = + internal static readonly byte[] FAST_ENCODER_TREE_STRUCTURE_DATA = { 0xec,0xbd,0x07,0x60,0x1c,0x49,0x96,0x25,0x26,0x2f,0x6d,0xca, 0x7b,0x7f,0x4a,0xf5,0x4a,0xd7,0xe0,0x74,0xa1,0x08,0x80,0x60, @@ -23,7 +23,7 @@ namespace SharpCompress.Compressors.Deflate64 0x1f,0x3f }; - internal static readonly byte[] BFinalFastEncoderTreeStructureData = + internal static readonly byte[] B_FINAL_FAST_ENCODER_TREE_STRUCTURE_DATA = { 0xed,0xbd,0x07,0x60,0x1c,0x49,0x96,0x25,0x26,0x2f,0x6d,0xca, 0x7b,0x7f,0x4a,0xf5,0x4a,0xd7,0xe0,0x74,0xa1,0x08,0x80,0x60, @@ -67,7 +67,7 @@ namespace SharpCompress.Compressors.Deflate64 // The least 5 significant bits are the length // and the rest is the code bits. - internal static readonly uint[] FastEncoderLiteralCodeInfo = + internal static readonly uint[] FAST_ENCODER_LITERAL_CODE_INFO = { 0x0000d7ee,0x0004d7ee,0x0002d7ee,0x0006d7ee,0x0001d7ee,0x0005d7ee,0x0003d7ee, 0x0007d7ee,0x000037ee,0x0000c7ec,0x00000126,0x000437ee,0x000237ee,0x000637ee, @@ -145,7 +145,7 @@ namespace SharpCompress.Compressors.Deflate64 0x003de7f1,0x000047eb }; - internal static readonly uint[] FastEncoderDistanceCodeInfo = + internal static readonly uint[] FAST_ENCODER_DISTANCE_CODE_INFO = { 0x00000f06,0x0001ff0a,0x0003ff0b,0x0007ff0b,0x0000ff19,0x00003f18,0x0000bf28, 0x00007f28,0x00001f37,0x00005f37,0x00000d45,0x00002f46,0x00000054,0x00001d55, @@ -154,23 +154,23 @@ namespace SharpCompress.Compressors.Deflate64 0x000007d5,0x000017d5,0x00000000,0x00000100 }; - internal static readonly uint[] BitMask = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767 }; - internal static readonly byte[] ExtraLengthBits = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; - internal static readonly byte[] ExtraDistanceBits = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0 }; - internal const int NumChars = 256; - internal const int NumLengthBaseCodes = 29; - internal const int NumDistBaseCodes = 30; + internal static readonly uint[] BIT_MASK = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767 }; + internal static readonly byte[] EXTRA_LENGTH_BITS = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + internal static readonly byte[] EXTRA_DISTANCE_BITS = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 0, 0 }; + internal const int NUM_CHARS = 256; + internal const int NUM_LENGTH_BASE_CODES = 29; + internal const int NUM_DIST_BASE_CODES = 30; - internal const uint FastEncoderPostTreeBitBuf = 0x0022; - internal const int FastEncoderPostTreeBitCount = 9; + internal const uint FAST_ENCODER_POST_TREE_BIT_BUF = 0x0022; + internal const int FAST_ENCODER_POST_TREE_BIT_COUNT = 9; - internal const uint NoCompressionHeader = 0x0; - internal const int NoCompressionHeaderBitCount = 3; - internal const uint BFinalNoCompressionHeader = 0x1; - internal const int BFinalNoCompressionHeaderBitCount = 3; - internal const int MaxCodeLen = 16; + internal const uint NO_COMPRESSION_HEADER = 0x0; + internal const int NO_COMPRESSION_HEADER_BIT_COUNT = 3; + internal const uint B_FINAL_NO_COMPRESSION_HEADER = 0x1; + internal const int B_FINAL_NO_COMPRESSION_HEADER_BIT_COUNT = 3; + internal const int MAX_CODE_LEN = 16; - private static readonly byte[] s_distLookup = CreateDistanceLookup(); + private static readonly byte[] S_DIST_LOOKUP = CreateDistanceLookup(); private static byte[] CreateDistanceLookup() { @@ -207,15 +207,15 @@ namespace SharpCompress.Compressors.Deflate64 int code; for (code = 0; code < 16; code++) { - for (int n = 0; n < (1 << ExtraDistanceBits[code]); n++) + for (int n = 0; n < (1 << EXTRA_DISTANCE_BITS[code]); n++) result[dist++] = (byte)code; } dist >>= 7; // from now on, all distances are divided by 128 - for (; code < NumDistBaseCodes; code++) + for (; code < NUM_DIST_BASE_CODES; code++) { - for (int n = 0; n < (1 << (ExtraDistanceBits[code] - 7)); n++) + for (int n = 0; n < (1 << (EXTRA_DISTANCE_BITS[code] - 7)); n++) result[256 + dist++] = (byte)code; } @@ -224,22 +224,22 @@ namespace SharpCompress.Compressors.Deflate64 // Return the position slot (0...29) of a match offset (0...32767) internal static int GetSlot(int pos) => - s_distLookup[((pos) < 256) ? (pos) : (256 + ((pos) >> 7))]; + S_DIST_LOOKUP[((pos) < 256) ? (pos) : (256 + ((pos) >> 7))]; // Reverse 'length' of the bits in code public static uint BitReverse(uint code, int length) { - uint new_code = 0; + uint newCode = 0; Debug.Assert(length > 0 && length <= 16, "Invalid len"); do { - new_code |= (code & 1); - new_code <<= 1; + newCode |= (code & 1); + newCode <<= 1; code >>= 1; } while (--length > 0); - return new_code >> 1; + return newCode >> 1; } } } diff --git a/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs b/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs index b57219a2..62c24b59 100644 --- a/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs +++ b/src/SharpCompress/Compressors/Deflate64/HuffmanTree.cs @@ -24,10 +24,10 @@ namespace SharpCompress.Compressors.Deflate64 internal sealed class HuffmanTree { - internal const int MaxLiteralTreeElements = 288; - internal const int MaxDistTreeElements = 32; - internal const int EndOfBlockCode = 256; - internal const int NumberOfCodeLengthTreeElements = 19; + internal const int MAX_LITERAL_TREE_ELEMENTS = 288; + internal const int MAX_DIST_TREE_ELEMENTS = 32; + internal const int END_OF_BLOCK_CODE = 256; + internal const int NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS = 19; private readonly int _tableBits; private readonly short[] _table; @@ -48,13 +48,13 @@ namespace SharpCompress.Compressors.Deflate64 public HuffmanTree(byte[] codeLengths) { Debug.Assert( - codeLengths.Length == MaxLiteralTreeElements || - codeLengths.Length == MaxDistTreeElements || - codeLengths.Length == NumberOfCodeLengthTreeElements, + codeLengths.Length == MAX_LITERAL_TREE_ELEMENTS || + codeLengths.Length == MAX_DIST_TREE_ELEMENTS || + codeLengths.Length == NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS, "we only expect three kinds of Length here"); _codeLengthArray = codeLengths; - if (_codeLengthArray.Length == MaxLiteralTreeElements) + if (_codeLengthArray.Length == MAX_LITERAL_TREE_ELEMENTS) { // bits for Literal/Length tree table _tableBits = 9; @@ -80,7 +80,7 @@ namespace SharpCompress.Compressors.Deflate64 // The data is in RFC 1951. private static byte[] GetStaticLiteralTreeLength() { - byte[] literalTreeLength = new byte[MaxLiteralTreeElements]; + byte[] literalTreeLength = new byte[MAX_LITERAL_TREE_ELEMENTS]; for (int i = 0; i <= 143; i++) literalTreeLength[i] = 8; @@ -98,8 +98,8 @@ namespace SharpCompress.Compressors.Deflate64 private static byte[] GetStaticDistanceTreeLength() { - byte[] staticDistanceTreeLength = new byte[MaxDistTreeElements]; - for (int i = 0; i < MaxDistTreeElements; i++) + byte[] staticDistanceTreeLength = new byte[MAX_DIST_TREE_ELEMENTS]; + for (int i = 0; i < MAX_DIST_TREE_ELEMENTS; i++) { staticDistanceTreeLength[i] = 5; } @@ -125,7 +125,7 @@ namespace SharpCompress.Compressors.Deflate64 nextCode[bits] = tempCode; } - uint[] code = new uint[MaxLiteralTreeElements]; + uint[] code = new uint[MAX_LITERAL_TREE_ELEMENTS]; for (int i = 0; i < _codeLengthArray.Length; i++) { int len = _codeLengthArray[i]; diff --git a/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs b/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs index 32194d78..7ce389b7 100644 --- a/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs +++ b/src/SharpCompress/Compressors/Deflate64/InflaterManaged.cs @@ -37,23 +37,23 @@ namespace SharpCompress.Compressors.Deflate64 // const tables used in decoding: // Extra bits for length code 257 - 285. - private static readonly byte[] s_extraLengthBits = + private static readonly byte[] S_EXTRA_LENGTH_BITS = { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,16 }; // The base length for length code 257 - 285. // The formula to get the real length for a length code is lengthBase[code - 257] + (value stored in extraBits) - private static readonly int[] s_lengthBase = + private static readonly int[] S_LENGTH_BASE = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,3}; // The base distance for distance code 0 - 31 // The real distance for a distance code is distanceBasePosition[code] + (value stored in extraBits) - private static readonly int[] s_distanceBasePosition = + private static readonly int[] S_DISTANCE_BASE_POSITION = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,32769,49153 }; // code lengths for code length alphabet is stored in following order - private static readonly byte[] s_codeOrder = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + private static readonly byte[] S_CODE_ORDER = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - private static readonly byte[] s_staticDistanceTreeTable = + private static readonly byte[] S_STATIC_DISTANCE_TREE_TABLE = { 0x00,0x10,0x08,0x18,0x04,0x14,0x0c,0x1c,0x02,0x12,0x0a,0x1a, 0x06,0x16,0x0e,0x1e,0x01,0x11,0x09,0x19,0x05,0x15,0x0d,0x1d, @@ -98,8 +98,8 @@ namespace SharpCompress.Compressors.Deflate64 _output = new OutputWindow(); _input = new InputBuffer(); - _codeList = new byte[HuffmanTree.MaxLiteralTreeElements + HuffmanTree.MaxDistTreeElements]; - _codeLengthTreeCodeLength = new byte[HuffmanTree.NumberOfCodeLengthTreeElements]; + _codeList = new byte[HuffmanTree.MAX_LITERAL_TREE_ELEMENTS + HuffmanTree.MAX_DIST_TREE_ELEMENTS]; + _codeLengthTreeCodeLength = new byte[HuffmanTree.NUMBER_OF_CODE_LENGTH_TREE_ELEMENTS]; _deflate64 = deflate64; //if (reader != null) //{ @@ -308,9 +308,9 @@ namespace SharpCompress.Compressors.Deflate64 // // LEN is the number of data bytes in the block. NLEN is the // one's complement of LEN. - private bool DecodeUncompressedBlock(out bool end_of_block) + private bool DecodeUncompressedBlock(out bool endOfBlock) { - end_of_block = false; + endOfBlock = false; while (true) { switch (_state) @@ -357,7 +357,7 @@ namespace SharpCompress.Compressors.Deflate64 { // Done with this block, need to re-init bit buffer for next block _state = InflaterState.ReadingBFinal; - end_of_block = true; + endOfBlock = true; return true; } @@ -378,9 +378,9 @@ namespace SharpCompress.Compressors.Deflate64 } } - private bool DecodeBlock(out bool end_of_block_code_seen) + private bool DecodeBlock(out bool endOfBlockCodeSeen) { - end_of_block_code_seen = false; + endOfBlockCodeSeen = false; int freeBytes = _output.FreeBytes; // it is a little bit faster than frequently accessing the property while (freeBytes > 65536) @@ -411,7 +411,7 @@ namespace SharpCompress.Compressors.Deflate64 else if (symbol == 256) { // end of block - end_of_block_code_seen = true; + endOfBlockCodeSeen = true; // Reset state _state = InflaterState.ReadingBFinal; return true; @@ -433,11 +433,11 @@ namespace SharpCompress.Compressors.Deflate64 } else { - if (symbol < 0 || symbol >= s_extraLengthBits.Length) + if (symbol < 0 || symbol >= S_EXTRA_LENGTH_BITS.Length) { throw new InvalidDataException("Deflate64: invalid data"); } - _extraBits = s_extraLengthBits[symbol]; + _extraBits = S_EXTRA_LENGTH_BITS[symbol]; Debug.Assert(_extraBits != 0, "We handle other cases separately!"); } _length = symbol; @@ -455,11 +455,11 @@ namespace SharpCompress.Compressors.Deflate64 return false; } - if (_length < 0 || _length >= s_lengthBase.Length) + if (_length < 0 || _length >= S_LENGTH_BASE.Length) { throw new InvalidDataException("Deflate64: invalid data"); } - _length = s_lengthBase[_length] + bits; + _length = S_LENGTH_BASE[_length] + bits; } _state = InflaterState.HaveFullLength; goto case InflaterState.HaveFullLength; @@ -475,7 +475,7 @@ namespace SharpCompress.Compressors.Deflate64 _distanceCode = _input.GetBits(5); if (_distanceCode >= 0) { - _distanceCode = s_staticDistanceTreeTable[_distanceCode]; + _distanceCode = S_STATIC_DISTANCE_TREE_TABLE[_distanceCode]; } } @@ -500,7 +500,7 @@ namespace SharpCompress.Compressors.Deflate64 { return false; } - offset = s_distanceBasePosition[_distanceCode] + bits; + offset = S_DISTANCE_BASE_POSITION[_distanceCode] + bits; } else { @@ -588,13 +588,13 @@ namespace SharpCompress.Compressors.Deflate64 { return false; } - _codeLengthTreeCodeLength[s_codeOrder[_loopCounter]] = (byte)bits; + _codeLengthTreeCodeLength[S_CODE_ORDER[_loopCounter]] = (byte)bits; ++_loopCounter; } - for (int i = _codeLengthCodeCount; i < s_codeOrder.Length; i++) + for (int i = _codeLengthCodeCount; i < S_CODE_ORDER.Length; i++) { - _codeLengthTreeCodeLength[s_codeOrder[i]] = 0; + _codeLengthTreeCodeLength[S_CODE_ORDER[i]] = 0; } // create huffman tree for code length @@ -714,15 +714,15 @@ namespace SharpCompress.Compressors.Deflate64 throw new InvalidDataException("Deflate64: unknown state"); } - byte[] literalTreeCodeLength = new byte[HuffmanTree.MaxLiteralTreeElements]; - byte[] distanceTreeCodeLength = new byte[HuffmanTree.MaxDistTreeElements]; + byte[] literalTreeCodeLength = new byte[HuffmanTree.MAX_LITERAL_TREE_ELEMENTS]; + byte[] distanceTreeCodeLength = new byte[HuffmanTree.MAX_DIST_TREE_ELEMENTS]; // Create literal and distance tables Array.Copy(_codeList, 0, literalTreeCodeLength, 0, _literalLengthCodeCount); Array.Copy(_codeList, _literalLengthCodeCount, distanceTreeCodeLength, 0, _distanceCodeCount); // Make sure there is an end-of-block code, otherwise how could we ever end? - if (literalTreeCodeLength[HuffmanTree.EndOfBlockCode] == 0) + if (literalTreeCodeLength[HuffmanTree.END_OF_BLOCK_CODE] == 0) { throw new InvalidDataException(); } diff --git a/src/SharpCompress/Compressors/Deflate64/OutputWindow.cs b/src/SharpCompress/Compressors/Deflate64/OutputWindow.cs index b5889319..2dbc4442 100644 --- a/src/SharpCompress/Compressors/Deflate64/OutputWindow.cs +++ b/src/SharpCompress/Compressors/Deflate64/OutputWindow.cs @@ -19,32 +19,32 @@ namespace SharpCompress.Compressors.Deflate64 // With Deflate64 we can have up to a 65536 length as well as up to a 65538 distance. This means we need a Window that is at // least 131074 bytes long so we have space to retrieve up to a full 64kb in lookback and place it in our buffer without // overwriting existing data. OutputWindow requires that the WindowSize be an exponent of 2, so we round up to 2^18. - private const int WindowSize = 262144; - private const int WindowMask = 262143; + private const int WINDOW_SIZE = 262144; + private const int WINDOW_MASK = 262143; - private readonly byte[] _window = new byte[WindowSize]; // The window is 2^18 bytes + private readonly byte[] _window = new byte[WINDOW_SIZE]; // The window is 2^18 bytes private int _end; // this is the position to where we should write next byte private int _bytesUsed; // The number of bytes in the output window which is not consumed. /// Add a byte to output window. public void Write(byte b) { - Debug.Assert(_bytesUsed < WindowSize, "Can't add byte when window is full!"); + Debug.Assert(_bytesUsed < WINDOW_SIZE, "Can't add byte when window is full!"); _window[_end++] = b; - _end &= WindowMask; + _end &= WINDOW_MASK; ++_bytesUsed; } public void WriteLengthDistance(int length, int distance) { - Debug.Assert((_bytesUsed + length) <= WindowSize, "No Enough space"); + Debug.Assert((_bytesUsed + length) <= WINDOW_SIZE, "No Enough space"); // move backwards distance bytes in the output stream, // and copy length bytes from this position to the output stream. _bytesUsed += length; - int copyStart = (_end - distance) & WindowMask; // start position for coping. + int copyStart = (_end - distance) & WINDOW_MASK; // start position for coping. - int border = WindowSize - length; + int border = WINDOW_SIZE - length; if (copyStart <= border && _end < border) { if (length <= distance) @@ -70,8 +70,8 @@ namespace SharpCompress.Compressors.Deflate64 while (length-- > 0) { _window[_end++] = _window[copyStart++]; - _end &= WindowMask; - copyStart &= WindowMask; + _end &= WINDOW_MASK; + copyStart &= WINDOW_MASK; } } } @@ -82,11 +82,11 @@ namespace SharpCompress.Compressors.Deflate64 /// public int CopyFrom(InputBuffer input, int length) { - length = Math.Min(Math.Min(length, WindowSize - _bytesUsed), input.AvailableBytes); + length = Math.Min(Math.Min(length, WINDOW_SIZE - _bytesUsed), input.AvailableBytes); int copied; // We might need wrap around to copy all bytes. - int tailLen = WindowSize - _end; + int tailLen = WINDOW_SIZE - _end; if (length > tailLen) { // copy the first part @@ -103,13 +103,13 @@ namespace SharpCompress.Compressors.Deflate64 copied = input.CopyTo(_window, _end, length); } - _end = (_end + copied) & WindowMask; + _end = (_end + copied) & WINDOW_MASK; _bytesUsed += copied; return copied; } /// Free space in output window. - public int FreeBytes => WindowSize - _bytesUsed; + public int FreeBytes => WINDOW_SIZE - _bytesUsed; /// Bytes not consumed in output window. public int AvailableBytes => _bytesUsed; @@ -117,32 +117,32 @@ namespace SharpCompress.Compressors.Deflate64 /// Copy the decompressed bytes to output array. public int CopyTo(byte[] output, int offset, int length) { - int copy_end; + int copyEnd; if (length > _bytesUsed) { // we can copy all the decompressed bytes out - copy_end = _end; + copyEnd = _end; length = _bytesUsed; } else { - copy_end = (_end - _bytesUsed + length) & WindowMask; // copy length of bytes + copyEnd = (_end - _bytesUsed + length) & WINDOW_MASK; // copy length of bytes } int copied = length; - int tailLen = length - copy_end; + int tailLen = length - copyEnd; if (tailLen > 0) { // this means we need to copy two parts separately // copy tailLen bytes from the end of output window - Array.Copy(_window, WindowSize - tailLen, + Array.Copy(_window, WINDOW_SIZE - tailLen, output, offset, tailLen); offset += tailLen; - length = copy_end; + length = copyEnd; } - Array.Copy(_window, copy_end - length, output, offset, length); + Array.Copy(_window, copyEnd - length, output, offset, length); _bytesUsed -= copied; Debug.Assert(_bytesUsed >= 0, "check this function and find why we copied more bytes than we have"); return copied; diff --git a/src/SharpCompress/Compressors/LZMA/DecoderStream.cs b/src/SharpCompress/Compressors/LZMA/DecoderStream.cs index c9c6fd21..1921ebab 100644 --- a/src/SharpCompress/Compressors/LZMA/DecoderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/DecoderStream.cs @@ -43,10 +43,10 @@ namespace SharpCompress.Compressors.LZMA { private static int FindCoderIndexForOutStreamIndex(CFolder folderInfo, int outStreamIndex) { - for (int coderIndex = 0; coderIndex < folderInfo.Coders.Count; coderIndex++) + for (int coderIndex = 0; coderIndex < folderInfo._coders.Count; coderIndex++) { - var coderInfo = folderInfo.Coders[coderIndex]; - outStreamIndex -= coderInfo.NumOutStreams; + var coderInfo = folderInfo._coders[coderIndex]; + outStreamIndex -= coderInfo._numOutStreams; if (outStreamIndex < 0) { return coderIndex; @@ -64,11 +64,11 @@ namespace SharpCompress.Compressors.LZMA primaryOutStreamIndex = -1; for (int outStreamIndex = 0, coderIndex = 0; - coderIndex < folderInfo.Coders.Count; + coderIndex < folderInfo._coders.Count; coderIndex++) { for (int coderOutStreamIndex = 0; - coderOutStreamIndex < folderInfo.Coders[coderIndex].NumOutStreams; + coderOutStreamIndex < folderInfo._coders[coderIndex]._numOutStreams; coderOutStreamIndex++, outStreamIndex++) { if (folderInfo.FindBindPairForOutStream(outStreamIndex) < 0) @@ -94,8 +94,8 @@ namespace SharpCompress.Compressors.LZMA private static Stream CreateDecoderStream(Stream[] packStreams, long[] packSizes, Stream[] outStreams, CFolder folderInfo, int coderIndex, IPasswordProvider pass) { - var coderInfo = folderInfo.Coders[coderIndex]; - if (coderInfo.NumOutStreams != 1) + var coderInfo = folderInfo._coders[coderIndex]; + if (coderInfo._numOutStreams != 1) { throw new NotSupportedException("Multiple output streams are not supported."); } @@ -103,23 +103,23 @@ namespace SharpCompress.Compressors.LZMA int inStreamId = 0; for (int i = 0; i < coderIndex; i++) { - inStreamId += folderInfo.Coders[i].NumInStreams; + inStreamId += folderInfo._coders[i]._numInStreams; } int outStreamId = 0; for (int i = 0; i < coderIndex; i++) { - outStreamId += folderInfo.Coders[i].NumOutStreams; + outStreamId += folderInfo._coders[i]._numOutStreams; } - Stream[] inStreams = new Stream[coderInfo.NumInStreams]; + Stream[] inStreams = new Stream[coderInfo._numInStreams]; for (int i = 0; i < inStreams.Length; i++, inStreamId++) { int bindPairIndex = folderInfo.FindBindPairForInStream(inStreamId); if (bindPairIndex >= 0) { - int pairedOutIndex = folderInfo.BindPairs[bindPairIndex].OutIndex; + int pairedOutIndex = folderInfo._bindPairs[bindPairIndex]._outIndex; if (outStreams[pairedOutIndex] != null) { @@ -153,8 +153,8 @@ namespace SharpCompress.Compressors.LZMA } } - long unpackSize = folderInfo.UnpackSizes[outStreamId]; - return DecoderRegistry.CreateDecoderStream(coderInfo.MethodId, inStreams, coderInfo.Props, pass, unpackSize); + long unpackSize = folderInfo._unpackSizes[outStreamId]; + return DecoderRegistry.CreateDecoderStream(coderInfo._methodId, inStreams, coderInfo._props, pass, unpackSize); } internal static Stream CreateDecoderStream(Stream inStream, long startPos, long[] packSizes, CFolder folderInfo, @@ -165,14 +165,14 @@ namespace SharpCompress.Compressors.LZMA throw new NotSupportedException("Unsupported stream binding structure."); } - Stream[] inStreams = new Stream[folderInfo.PackStreams.Count]; - for (int j = 0; j < folderInfo.PackStreams.Count; j++) + Stream[] inStreams = new Stream[folderInfo._packStreams.Count]; + for (int j = 0; j < folderInfo._packStreams.Count; j++) { inStreams[j] = new BufferedSubStream(inStream, startPos, packSizes[j]); startPos += packSizes[j]; } - Stream[] outStreams = new Stream[folderInfo.UnpackSizes.Count]; + Stream[] outStreams = new Stream[folderInfo._unpackSizes.Count]; int primaryCoderIndex, primaryOutStreamIndex; FindPrimaryOutStreamIndex(folderInfo, out primaryCoderIndex, out primaryOutStreamIndex); diff --git a/src/SharpCompress/Compressors/LZMA/Registry.cs b/src/SharpCompress/Compressors/LZMA/Registry.cs index 9c95a5ae..edc02854 100644 --- a/src/SharpCompress/Compressors/LZMA/Registry.cs +++ b/src/SharpCompress/Compressors/LZMA/Registry.cs @@ -25,7 +25,7 @@ namespace SharpCompress.Compressors.LZMA internal static Stream CreateDecoderStream(CMethodId id, Stream[] inStreams, byte[] info, IPasswordProvider pass, long limit) { - switch (id.Id) + switch (id._id) { case K_COPY: if (info != null) @@ -37,7 +37,7 @@ namespace SharpCompress.Compressors.LZMA case K_LZMA2: return new LzmaStream(info, inStreams.Single(), -1, limit); #if !NO_CRYPTO - case CMethodId.kAESId: + case CMethodId.K_AES_ID: return new AesDecoderStream(inStreams.Single(), info, pass, limit); #endif case K_BCJ: diff --git a/src/SharpCompress/Writers/Tar/TarWriter.cs b/src/SharpCompress/Writers/Tar/TarWriter.cs index 61e98ffd..8c782366 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.cs @@ -83,7 +83,7 @@ namespace SharpCompress.Writers.Tar TarHeader header = new TarHeader(WriterOptions.ArchiveEncoding); - header.LastModifiedTime = modificationTime ?? TarHeader.Epoch; + header.LastModifiedTime = modificationTime ?? TarHeader.EPOCH; header.Name = NormalizeFilename(filename); header.Size = realSize; header.Write(OutputStream); diff --git a/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs b/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs index 0a388651..1f1e6466 100644 --- a/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs +++ b/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs @@ -44,7 +44,7 @@ namespace SharpCompress.Writers.Zip var extralength = zip64 ? (2 + 2 + 8 + 8 + 8 + 4) : 0; var version = (byte)(zip64 ? 45 : 20); // Version 20 required for deflate/encryption - HeaderFlags flags = Equals(archiveEncoding.GetEncoding(), Encoding.UTF8) ? HeaderFlags.EFS : HeaderFlags.None; + HeaderFlags flags = Equals(archiveEncoding.GetEncoding(), Encoding.UTF8) ? HeaderFlags.Efs : HeaderFlags.None; if (!outputStream.CanSeek) { // Cannot use data descriptors with zip64: diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index f41e0b8e..89a00c3c 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -174,7 +174,7 @@ namespace SharpCompress.Writers.Zip { OutputStream.Write(new byte[] { 63, 0 }, 0, 2); //version says we used PPMd or LZMA } - HeaderFlags flags = Equals(WriterOptions.ArchiveEncoding.GetEncoding(), Encoding.UTF8) ? HeaderFlags.EFS : 0; + HeaderFlags flags = Equals(WriterOptions.ArchiveEncoding.GetEncoding(), Encoding.UTF8) ? HeaderFlags.Efs : 0; if (!OutputStream.CanSeek) { flags |= HeaderFlags.UsePostDataDescriptor; From c64282e915369c6fe0da0a1957dc01c4bc692db2 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 6 May 2018 09:07:06 +0100 Subject: [PATCH 113/124] more naming --- .../Compressors/Filters/BCJ2Filter.cs | 156 +++++++++--------- .../Compressors/Filters/BCJFilter.cs | 40 ++--- .../Compressors/Filters/Filter.cs | 104 ++++++------ 3 files changed, 150 insertions(+), 150 deletions(-) diff --git a/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs b/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs index 65af71ee..d767cd6c 100644 --- a/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs +++ b/src/SharpCompress/Compressors/Filters/BCJ2Filter.cs @@ -5,36 +5,36 @@ namespace SharpCompress.Compressors.Filters { internal class BCJ2Filter : Stream { - private readonly Stream baseStream; - private readonly byte[] input = new byte[4096]; - private int inputOffset; - private int inputCount; - private bool endReached; + private readonly Stream _baseStream; + private readonly byte[] _input = new byte[4096]; + private int _inputOffset; + private int _inputCount; + private bool _endReached; - private long position; - private readonly byte[] output = new byte[4]; - private int outputOffset; - private int outputCount; + private long _position; + private readonly byte[] _output = new byte[4]; + private int _outputOffset; + private int _outputCount; - private readonly byte[] control; - private readonly byte[] data1; - private readonly byte[] data2; + private readonly byte[] _control; + private readonly byte[] _data1; + private readonly byte[] _data2; - private int controlPos; - private int data1Pos; - private int data2Pos; + private int _controlPos; + private int _data1Pos; + private int _data2Pos; - private readonly ushort[] p = new ushort[256 + 2]; - private uint range, code; - private byte prevByte; - private bool isDisposed; + private readonly ushort[] _p = new ushort[256 + 2]; + private uint _range, _code; + private byte _prevByte; + private bool _isDisposed; - private const int kNumTopBits = 24; - private const int kTopValue = 1 << kNumTopBits; + private const int K_NUM_TOP_BITS = 24; + private const int K_TOP_VALUE = 1 << K_NUM_TOP_BITS; - private const int kNumBitModelTotalBits = 11; - private const int kBitModelTotal = 1 << kNumBitModelTotalBits; - private const int kNumMoveBits = 5; + private const int K_NUM_BIT_MODEL_TOTAL_BITS = 11; + private const int K_BIT_MODEL_TOTAL = 1 << K_NUM_BIT_MODEL_TOTAL_BITS; + private const int K_NUM_MOVE_BITS = 5; private static bool IsJ(byte b0, byte b1) { @@ -48,34 +48,34 @@ namespace SharpCompress.Compressors.Filters public BCJ2Filter(byte[] control, byte[] data1, byte[] data2, Stream baseStream) { - this.control = control; - this.data1 = data1; - this.data2 = data2; - this.baseStream = baseStream; + _control = control; + _data1 = data1; + _data2 = data2; + _baseStream = baseStream; int i; - for (i = 0; i < p.Length; i++) + for (i = 0; i < _p.Length; i++) { - p[i] = kBitModelTotal >> 1; + _p[i] = K_BIT_MODEL_TOTAL >> 1; } - code = 0; - range = 0xFFFFFFFF; + _code = 0; + _range = 0xFFFFFFFF; for (i = 0; i < 5; i++) { - code = (code << 8) | control[controlPos++]; + _code = (_code << 8) | control[_controlPos++]; } } protected override void Dispose(bool disposing) { - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; + _isDisposed = true; base.Dispose(disposing); - baseStream.Dispose(); + _baseStream.Dispose(); } public override bool CanRead => true; @@ -89,57 +89,57 @@ namespace SharpCompress.Compressors.Filters throw new NotSupportedException(); } - public override long Length => baseStream.Length + data1.Length + data2.Length; + public override long Length => _baseStream.Length + _data1.Length + _data2.Length; - public override long Position { get => position; set => throw new NotSupportedException(); } + public override long Position { get => _position; set => throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { int size = 0; byte b = 0; - while (!endReached && size < count) + while (!_endReached && size < count) { - while (outputOffset < outputCount) + while (_outputOffset < _outputCount) { - b = output[outputOffset++]; + b = _output[_outputOffset++]; buffer[offset++] = b; size++; - position++; + _position++; - prevByte = b; + _prevByte = b; if (size == count) { return size; } } - if (inputOffset == inputCount) + if (_inputOffset == _inputCount) { - inputOffset = 0; - inputCount = baseStream.Read(input, 0, input.Length); - if (inputCount == 0) + _inputOffset = 0; + _inputCount = _baseStream.Read(_input, 0, _input.Length); + if (_inputCount == 0) { - endReached = true; + _endReached = true; break; } } - b = input[inputOffset++]; + b = _input[_inputOffset++]; buffer[offset++] = b; size++; - position++; + _position++; - if (!IsJ(prevByte, b)) + if (!IsJ(_prevByte, b)) { - prevByte = b; + _prevByte = b; } else { int prob; if (b == 0xE8) { - prob = prevByte; + prob = _prevByte; } else if (b == 0xE9) { @@ -150,27 +150,27 @@ namespace SharpCompress.Compressors.Filters prob = 257; } - uint bound = (range >> kNumBitModelTotalBits) * p[prob]; - if (code < bound) + uint bound = (_range >> K_NUM_BIT_MODEL_TOTAL_BITS) * _p[prob]; + if (_code < bound) { - range = bound; - p[prob] += (ushort)((kBitModelTotal - p[prob]) >> kNumMoveBits); - if (range < kTopValue) + _range = bound; + _p[prob] += (ushort)((K_BIT_MODEL_TOTAL - _p[prob]) >> K_NUM_MOVE_BITS); + if (_range < K_TOP_VALUE) { - range <<= 8; - code = (code << 8) | control[controlPos++]; + _range <<= 8; + _code = (_code << 8) | _control[_controlPos++]; } - prevByte = b; + _prevByte = b; } else { - range -= bound; - code -= bound; - p[prob] -= (ushort)(p[prob] >> kNumMoveBits); - if (range < kTopValue) + _range -= bound; + _code -= bound; + _p[prob] -= (ushort)(_p[prob] >> K_NUM_MOVE_BITS); + if (_range < K_TOP_VALUE) { - range <<= 8; - code = (code << 8) | control[controlPos++]; + _range <<= 8; + _code = (_code << 8) | _control[_controlPos++]; } uint dest; @@ -178,24 +178,24 @@ namespace SharpCompress.Compressors.Filters { dest = (uint) - ((data1[data1Pos++] << 24) | (data1[data1Pos++] << 16) | (data1[data1Pos++] << 8) | - data1[data1Pos++]); + ((_data1[_data1Pos++] << 24) | (_data1[_data1Pos++] << 16) | (_data1[_data1Pos++] << 8) | + _data1[_data1Pos++]); } else { dest = (uint) - ((data2[data2Pos++] << 24) | (data2[data2Pos++] << 16) | (data2[data2Pos++] << 8) | - data2[data2Pos++]); + ((_data2[_data2Pos++] << 24) | (_data2[_data2Pos++] << 16) | (_data2[_data2Pos++] << 8) | + _data2[_data2Pos++]); } - dest -= (uint)(position + 4); + dest -= (uint)(_position + 4); - output[0] = (byte)dest; - output[1] = (byte)(dest >> 8); - output[2] = (byte)(dest >> 16); - output[3] = (byte)(dest >> 24); - outputOffset = 0; - outputCount = 4; + _output[0] = (byte)dest; + _output[1] = (byte)(dest >> 8); + _output[2] = (byte)(dest >> 16); + _output[3] = (byte)(dest >> 24); + _outputOffset = 0; + _outputCount = 4; } } } diff --git a/src/SharpCompress/Compressors/Filters/BCJFilter.cs b/src/SharpCompress/Compressors/Filters/BCJFilter.cs index b782d2fd..3caa492b 100644 --- a/src/SharpCompress/Compressors/Filters/BCJFilter.cs +++ b/src/SharpCompress/Compressors/Filters/BCJFilter.cs @@ -8,16 +8,16 @@ namespace SharpCompress.Compressors.Filters private static readonly int[] MASK_TO_BIT_NUMBER = {0, 1, 2, 2, 3, 3, 3, 3}; - private int pos; - private int prevMask; + private int _pos; + private int _prevMask; public BCJFilter(bool isEncoder, Stream baseStream) : base(isEncoder, baseStream, 5) { - pos = 5; + _pos = 5; } - private static bool test86MSByte(byte b) + private static bool Test86MsByte(byte b) { return b == 0x00 || b == 0xFF; } @@ -39,18 +39,18 @@ namespace SharpCompress.Compressors.Filters if ((prevPos & ~3) != 0) { // (unsigned)prevPos > 3 - prevMask = 0; + _prevMask = 0; } else { - prevMask = (prevMask << (prevPos - 1)) & 7; - if (prevMask != 0) + _prevMask = (_prevMask << (prevPos - 1)) & 7; + if (_prevMask != 0) { - if (!MASK_TO_ALLOWED_STATUS[prevMask] || test86MSByte( - buffer[i + 4 - MASK_TO_BIT_NUMBER[prevMask]])) + if (!MASK_TO_ALLOWED_STATUS[_prevMask] || Test86MsByte( + buffer[i + 4 - MASK_TO_BIT_NUMBER[_prevMask]])) { prevPos = i; - prevMask = (prevMask << 1) | 1; + _prevMask = (_prevMask << 1) | 1; continue; } } @@ -58,7 +58,7 @@ namespace SharpCompress.Compressors.Filters prevPos = i; - if (test86MSByte(buffer[i + 4])) + if (Test86MsByte(buffer[i + 4])) { int src = buffer[i + 1] | (buffer[i + 2] << 8) @@ -67,22 +67,22 @@ namespace SharpCompress.Compressors.Filters int dest; while (true) { - if (isEncoder) + if (_isEncoder) { - dest = src + (pos + i - offset); + dest = src + (_pos + i - offset); } else { - dest = src - (pos + i - offset); + dest = src - (_pos + i - offset); } - if (prevMask == 0) + if (_prevMask == 0) { break; } - int index = MASK_TO_BIT_NUMBER[prevMask] * 8; - if (!test86MSByte((byte)(dest >> (24 - index)))) + int index = MASK_TO_BIT_NUMBER[_prevMask] * 8; + if (!Test86MsByte((byte)(dest >> (24 - index)))) { break; } @@ -98,15 +98,15 @@ namespace SharpCompress.Compressors.Filters } else { - prevMask = (prevMask << 1) | 1; + _prevMask = (_prevMask << 1) | 1; } } prevPos = i - prevPos; - prevMask = ((prevPos & ~3) != 0) ? 0 : prevMask << (prevPos - 1); + _prevMask = ((prevPos & ~3) != 0) ? 0 : _prevMask << (prevPos - 1); i -= offset; - pos += i; + _pos += i; return i; } } diff --git a/src/SharpCompress/Compressors/Filters/Filter.cs b/src/SharpCompress/Compressors/Filters/Filter.cs index c0b23eb1..b7fd8159 100644 --- a/src/SharpCompress/Compressors/Filters/Filter.cs +++ b/src/SharpCompress/Compressors/Filters/Filter.cs @@ -5,109 +5,109 @@ namespace SharpCompress.Compressors.Filters { internal abstract class Filter : Stream { - protected bool isEncoder; - protected Stream baseStream; + protected bool _isEncoder; + protected Stream _baseStream; - private readonly byte[] tail; - private readonly byte[] window; - private int transformed; - private int read; - private bool endReached; - private bool isDisposed; + private readonly byte[] _tail; + private readonly byte[] _window; + private int _transformed; + private int _read; + private bool _endReached; + private bool _isDisposed; protected Filter(bool isEncoder, Stream baseStream, int lookahead) { - this.isEncoder = isEncoder; - this.baseStream = baseStream; - tail = new byte[lookahead - 1]; - window = new byte[tail.Length * 2]; + _isEncoder = isEncoder; + _baseStream = baseStream; + _tail = new byte[lookahead - 1]; + _window = new byte[_tail.Length * 2]; } protected override void Dispose(bool disposing) { - if (isDisposed) + if (_isDisposed) { return; } - isDisposed = true; + _isDisposed = true; base.Dispose(disposing); - baseStream.Dispose(); + _baseStream.Dispose(); } - public override bool CanRead => !isEncoder; + public override bool CanRead => !_isEncoder; public override bool CanSeek => false; - public override bool CanWrite => isEncoder; + public override bool CanWrite => _isEncoder; public override void Flush() { throw new NotSupportedException(); } - public override long Length => baseStream.Length; + public override long Length => _baseStream.Length; - public override long Position { get => baseStream.Position; set => throw new NotSupportedException(); } + public override long Position { get => _baseStream.Position; set => throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { int size = 0; - if (transformed > 0) + if (_transformed > 0) { - int copySize = transformed; + int copySize = _transformed; if (copySize > count) { copySize = count; } - Buffer.BlockCopy(tail, 0, buffer, offset, copySize); - transformed -= copySize; - read -= copySize; + Buffer.BlockCopy(_tail, 0, buffer, offset, copySize); + _transformed -= copySize; + _read -= copySize; offset += copySize; count -= copySize; size += copySize; - Buffer.BlockCopy(tail, copySize, tail, 0, read); + Buffer.BlockCopy(_tail, copySize, _tail, 0, _read); } if (count == 0) { return size; } - int inSize = read; + int inSize = _read; if (inSize > count) { inSize = count; } - Buffer.BlockCopy(tail, 0, buffer, offset, inSize); - read -= inSize; - Buffer.BlockCopy(tail, inSize, tail, 0, read); - while (!endReached && inSize < count) + Buffer.BlockCopy(_tail, 0, buffer, offset, inSize); + _read -= inSize; + Buffer.BlockCopy(_tail, inSize, _tail, 0, _read); + while (!_endReached && inSize < count) { - int baseRead = baseStream.Read(buffer, offset + inSize, count - inSize); + int baseRead = _baseStream.Read(buffer, offset + inSize, count - inSize); inSize += baseRead; if (baseRead == 0) { - endReached = true; + _endReached = true; } } - while (!endReached && read < tail.Length) + while (!_endReached && _read < _tail.Length) { - int baseRead = baseStream.Read(tail, read, tail.Length - read); - read += baseRead; + int baseRead = _baseStream.Read(_tail, _read, _tail.Length - _read); + _read += baseRead; if (baseRead == 0) { - endReached = true; + _endReached = true; } } - if (inSize > tail.Length) + if (inSize > _tail.Length) { - transformed = Transform(buffer, offset, inSize); - offset += transformed; - count -= transformed; - size += transformed; - inSize -= transformed; - transformed = 0; + _transformed = Transform(buffer, offset, inSize); + offset += _transformed; + count -= _transformed; + size += _transformed; + inSize -= _transformed; + _transformed = 0; } if (count == 0) @@ -115,20 +115,20 @@ namespace SharpCompress.Compressors.Filters return size; } - Buffer.BlockCopy(buffer, offset, window, 0, inSize); - Buffer.BlockCopy(tail, 0, window, inSize, read); - if (inSize + read > tail.Length) + Buffer.BlockCopy(buffer, offset, _window, 0, inSize); + Buffer.BlockCopy(_tail, 0, _window, inSize, _read); + if (inSize + _read > _tail.Length) { - transformed = Transform(window, 0, inSize + read); + _transformed = Transform(_window, 0, inSize + _read); } else { - transformed = inSize + read; + _transformed = inSize + _read; } - Buffer.BlockCopy(window, 0, buffer, offset, inSize); - Buffer.BlockCopy(window, inSize, tail, 0, read); + Buffer.BlockCopy(_window, 0, buffer, offset, inSize); + Buffer.BlockCopy(_window, inSize, _tail, 0, _read); size += inSize; - transformed -= inSize; + _transformed -= inSize; return size; } @@ -146,7 +146,7 @@ namespace SharpCompress.Compressors.Filters public override void Write(byte[] buffer, int offset, int count) { Transform(buffer, offset, count); - baseStream.Write(buffer, offset, count); + _baseStream.Write(buffer, offset, count); } protected abstract int Transform(byte[] buffer, int offset, int count); From 9f89a0844dc9d946ffd7d8b853aa248f407a81ed Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 6 May 2018 09:09:45 +0100 Subject: [PATCH 114/124] Fix namespaces to not interfere with bouncy castle users --- src/SharpCompress/Common/Rar/RarRijndael.cs | 2 -- src/SharpCompress/Crypto/CryptoException.cs | 2 +- src/SharpCompress/Crypto/DataLengthException.cs | 2 +- src/SharpCompress/Crypto/IBlockCipher.cs | 2 +- src/SharpCompress/Crypto/ICipherParameters.cs | 2 +- src/SharpCompress/Crypto/KeyParameter.cs | 2 +- src/SharpCompress/Crypto/RijndaelEngine.cs | 3 +-- 7 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/SharpCompress/Common/Rar/RarRijndael.cs b/src/SharpCompress/Common/Rar/RarRijndael.cs index 83fd4958..69e4e0fc 100644 --- a/src/SharpCompress/Common/Rar/RarRijndael.cs +++ b/src/SharpCompress/Common/Rar/RarRijndael.cs @@ -4,8 +4,6 @@ using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; -using Org.BouncyCastle.Crypto.Engines; -using Org.BouncyCastle.Crypto.Parameters; namespace SharpCompress.Common.Rar { diff --git a/src/SharpCompress/Crypto/CryptoException.cs b/src/SharpCompress/Crypto/CryptoException.cs index 7eb42a74..6e8b9f14 100644 --- a/src/SharpCompress/Crypto/CryptoException.cs +++ b/src/SharpCompress/Crypto/CryptoException.cs @@ -1,6 +1,6 @@ using System; -namespace Org.BouncyCastle.Crypto +namespace SharpCompress.Crypto { public class CryptoException : Exception diff --git a/src/SharpCompress/Crypto/DataLengthException.cs b/src/SharpCompress/Crypto/DataLengthException.cs index 8a3e1c47..b82a9e9c 100644 --- a/src/SharpCompress/Crypto/DataLengthException.cs +++ b/src/SharpCompress/Crypto/DataLengthException.cs @@ -1,6 +1,6 @@ using System; -namespace Org.BouncyCastle.Crypto +namespace SharpCompress.Crypto { public class DataLengthException : CryptoException diff --git a/src/SharpCompress/Crypto/IBlockCipher.cs b/src/SharpCompress/Crypto/IBlockCipher.cs index a4f30012..6f6542e3 100644 --- a/src/SharpCompress/Crypto/IBlockCipher.cs +++ b/src/SharpCompress/Crypto/IBlockCipher.cs @@ -1,4 +1,4 @@ -namespace Org.BouncyCastle.Crypto +namespace SharpCompress.Crypto { /// Base interface for a symmetric key block cipher. public interface IBlockCipher diff --git a/src/SharpCompress/Crypto/ICipherParameters.cs b/src/SharpCompress/Crypto/ICipherParameters.cs index 16439a25..d6e4af8b 100644 --- a/src/SharpCompress/Crypto/ICipherParameters.cs +++ b/src/SharpCompress/Crypto/ICipherParameters.cs @@ -1,4 +1,4 @@ -namespace Org.BouncyCastle.Crypto +namespace SharpCompress.Crypto { public interface ICipherParameters { diff --git a/src/SharpCompress/Crypto/KeyParameter.cs b/src/SharpCompress/Crypto/KeyParameter.cs index b69ce4e1..8658fc9c 100644 --- a/src/SharpCompress/Crypto/KeyParameter.cs +++ b/src/SharpCompress/Crypto/KeyParameter.cs @@ -1,6 +1,6 @@ using System; -namespace Org.BouncyCastle.Crypto.Parameters +namespace SharpCompress.Crypto { public class KeyParameter : ICipherParameters diff --git a/src/SharpCompress/Crypto/RijndaelEngine.cs b/src/SharpCompress/Crypto/RijndaelEngine.cs index f90be37f..06e2bbdd 100644 --- a/src/SharpCompress/Crypto/RijndaelEngine.cs +++ b/src/SharpCompress/Crypto/RijndaelEngine.cs @@ -1,7 +1,6 @@ using System; -using Org.BouncyCastle.Crypto.Parameters; -namespace Org.BouncyCastle.Crypto.Engines +namespace SharpCompress.Crypto { public class RijndaelEngine : IBlockCipher From 747180203cca3f7251525933378a59124c0a5861 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 6 May 2018 09:12:18 +0100 Subject: [PATCH 115/124] =?UTF-8?q?Rider=20isn=E2=80=99t=20quite=20good=20?= =?UTF-8?q?enough=20in=20refactoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/SharpCompress/Common/Rar/RarRijndael.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SharpCompress/Common/Rar/RarRijndael.cs b/src/SharpCompress/Common/Rar/RarRijndael.cs index 69e4e0fc..824cb64e 100644 --- a/src/SharpCompress/Common/Rar/RarRijndael.cs +++ b/src/SharpCompress/Common/Rar/RarRijndael.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Text; +using SharpCompress.Crypto; namespace SharpCompress.Common.Rar { From 2919ec293ad9e34da59899cdda37f54525778ba2 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Sun, 6 May 2018 09:39:36 +0100 Subject: [PATCH 116/124] mark for 0.21 --- src/SharpCompress/SharpCompress.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 2d34941d..050b7b43 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -2,9 +2,9 @@ SharpCompress - Pure C# Decompression/Compression en-US - 0.20.0 - 0.20.0 - 0.20.0 + 0.21.0 + 0.21.0 + 0.21.0 Adam Hathcock net45;net35;netstandard1.0;netstandard1.3;netstandard2.0 true From 3f94c1a50dd1b31f600307b5ad98704dd32e89e4 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Tue, 8 May 2018 14:10:49 +0100 Subject: [PATCH 117/124] Remove lingering uses of non disposing stream --- src/SharpCompress/IO/NonDisposingStream.cs | 3 +- .../Readers/IReaderExtensions.cs | 4 -- src/SharpCompress/Readers/Zip/ZipReader.cs | 2 +- src/SharpCompress/Writers/AbstractWriter.cs | 12 ++-- tests/SharpCompress.Test/ReaderTests.cs | 5 +- tests/SharpCompress.Test/TestBase.cs | 60 +++++-------------- .../SharpCompress.Test/Zip/ZipReaderTests.cs | 3 +- 7 files changed, 29 insertions(+), 60 deletions(-) diff --git a/src/SharpCompress/IO/NonDisposingStream.cs b/src/SharpCompress/IO/NonDisposingStream.cs index 1b1b4939..319f9461 100644 --- a/src/SharpCompress/IO/NonDisposingStream.cs +++ b/src/SharpCompress/IO/NonDisposingStream.cs @@ -3,7 +3,7 @@ using System.IO; namespace SharpCompress.IO { - internal class NonDisposingStream : Stream + public class NonDisposingStream : Stream { public NonDisposingStream(Stream stream, bool throwOnDispose = false) { @@ -15,6 +15,7 @@ namespace SharpCompress.IO protected override void Dispose(bool disposing) { + GC.SuppressFinalize(this); if (ThrowOnDispose) { throw new InvalidOperationException(); diff --git a/src/SharpCompress/Readers/IReaderExtensions.cs b/src/SharpCompress/Readers/IReaderExtensions.cs index 4100a607..65ef90ed 100644 --- a/src/SharpCompress/Readers/IReaderExtensions.cs +++ b/src/SharpCompress/Readers/IReaderExtensions.cs @@ -93,10 +93,6 @@ namespace SharpCompress.Readers using (FileStream fs = File.Open(destinationFileName, fm)) { reader.WriteEntryTo(fs); - //using (Stream s = reader.OpenEntryStream()) - //{ - // s.TransferTo(fs); - //} } reader.Entry.PreserveExtractionOptions(destinationFileName, options); } diff --git a/src/SharpCompress/Readers/Zip/ZipReader.cs b/src/SharpCompress/Readers/Zip/ZipReader.cs index 5b534610..871f8283 100644 --- a/src/SharpCompress/Readers/Zip/ZipReader.cs +++ b/src/SharpCompress/Readers/Zip/ZipReader.cs @@ -10,7 +10,7 @@ namespace SharpCompress.Readers.Zip { private readonly StreamingZipHeaderFactory _headerFactory; - internal ZipReader(Stream stream, ReaderOptions options) + private ZipReader(Stream stream, ReaderOptions options) : base(options, ArchiveType.Zip) { Volume = new ZipVolume(stream, options); diff --git a/src/SharpCompress/Writers/AbstractWriter.cs b/src/SharpCompress/Writers/AbstractWriter.cs index d331f034..f6656a8b 100644 --- a/src/SharpCompress/Writers/AbstractWriter.cs +++ b/src/SharpCompress/Writers/AbstractWriter.cs @@ -6,7 +6,7 @@ namespace SharpCompress.Writers { public abstract class AbstractWriter : IWriter { - private bool isDisposed; + private bool _isDisposed; protected AbstractWriter(ArchiveType type, WriterOptions writerOptions) { @@ -29,7 +29,7 @@ namespace SharpCompress.Writers protected virtual void Dispose(bool isDisposing) { - if (isDisposing && !WriterOptions.LeaveStreamOpen) + if (isDisposing) { OutputStream.Dispose(); } @@ -37,20 +37,20 @@ namespace SharpCompress.Writers public void Dispose() { - if (!isDisposed) + if (!_isDisposed) { GC.SuppressFinalize(this); Dispose(true); - isDisposed = true; + _isDisposed = true; } } ~AbstractWriter() { - if (!isDisposed) + if (!_isDisposed) { Dispose(false); - isDisposed = true; + _isDisposed = true; } } } diff --git a/tests/SharpCompress.Test/ReaderTests.cs b/tests/SharpCompress.Test/ReaderTests.cs index 3652dabf..dcf3ad58 100644 --- a/tests/SharpCompress.Test/ReaderTests.cs +++ b/tests/SharpCompress.Test/ReaderTests.cs @@ -20,7 +20,10 @@ namespace SharpCompress.Test foreach (var path in testArchives) { using (var stream = new NonDisposingStream(new ForwardOnlyStream(File.OpenRead(path)), true)) - using (var reader = ReaderFactory.Open(stream)) + using (var reader = ReaderFactory.Open(stream, new ReaderOptions() + { + LeaveStreamOpen = true + })) { UseReader(this, reader, expectedCompression); stream.ThrowOnDispose = false; diff --git a/tests/SharpCompress.Test/TestBase.cs b/tests/SharpCompress.Test/TestBase.cs index 6f6d314b..1d04fd01 100644 --- a/tests/SharpCompress.Test/TestBase.cs +++ b/tests/SharpCompress.Test/TestBase.cs @@ -12,46 +12,26 @@ namespace SharpCompress.Test { public class TestBase { - protected string SOLUTION_BASE_PATH = null; + private string SOLUTION_BASE_PATH; protected string TEST_ARCHIVES_PATH; protected string ORIGINAL_FILES_PATH; protected string MISC_TEST_FILES_PATH; public string SCRATCH_FILES_PATH; protected string SCRATCH2_FILES_PATH; - protected IEnumerable GetRarArchives() - { - yield return Path.Combine(TEST_ARCHIVES_PATH, "Rar.none.rar"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Rar.rar"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Rar.solid.rar"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Rar.multi.part01.rar"); - } - protected IEnumerable GetZipArchives() - { - yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.bzip2.dd.zip"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.bzip2.zip"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd-.zip"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.dd.zip"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate.zip"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.deflate64.zip"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.dd.zip"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.lzma.zip"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.none.zip"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.ppmd.dd.zip"); - yield return Path.Combine(TEST_ARCHIVES_PATH, "Zip.ppmd.zip"); - } - protected IEnumerable GetTarArchives() - { - yield return Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"); - } - protected IEnumerable GetTarBz2Archives() - { - yield return Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.bz2"); - } - protected IEnumerable GetTarGzArchives() - { - yield return Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"); - } + + public TestBase() + { + var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf("SharpCompress.Test", StringComparison.OrdinalIgnoreCase); + SOLUTION_BASE_PATH = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory.Substring(0, index)); + + TEST_ARCHIVES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Archives"); + ORIGINAL_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Original"); + MISC_TEST_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "MiscTest"); + SCRATCH_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Scratch"); + SCRATCH2_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Scratch2"); + } + public void ResetScratch() { if (Directory.Exists(SCRATCH_FILES_PATH)) @@ -271,17 +251,5 @@ namespace SharpCompress.Test //file is not locked return false; } - - public TestBase() - { - var index = AppDomain.CurrentDomain.BaseDirectory.IndexOf("SharpCompress.Test", StringComparison.OrdinalIgnoreCase); - SOLUTION_BASE_PATH = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory.Substring(0, index)); - - TEST_ARCHIVES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Archives"); - ORIGINAL_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Original"); - MISC_TEST_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "MiscTest"); - SCRATCH_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Scratch"); - SCRATCH2_FILES_PATH = Path.Combine(SOLUTION_BASE_PATH, "TestArchives", "Scratch2"); - } } } diff --git a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs index e30c0eed..7e2558b0 100644 --- a/tests/SharpCompress.Test/Zip/ZipReaderTests.cs +++ b/tests/SharpCompress.Test/Zip/ZipReaderTests.cs @@ -1,6 +1,7 @@ using System; using System.IO; using SharpCompress.Common; +using SharpCompress.IO; using SharpCompress.Readers; using SharpCompress.Readers.Zip; using SharpCompress.Writers; @@ -295,7 +296,7 @@ namespace SharpCompress.Test.Zip stream = new MemoryStream(stream.ToArray()); File.WriteAllBytes(Path.Combine(SCRATCH_FILES_PATH, "foo.zip"), stream.ToArray()); - using (IReader zipReader = ZipReader.Open(stream)) + using (IReader zipReader = ZipReader.Open(new NonDisposingStream(stream, true))) { while (zipReader.MoveToNextEntry()) { From be971cb6f72824b0a130b0e36dde126a43bb38a2 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 14 May 2018 16:08:31 +0100 Subject: [PATCH 118/124] Allow forced encoding to override default encoding --- src/SharpCompress/Common/ArchiveEncoding.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/SharpCompress/Common/ArchiveEncoding.cs b/src/SharpCompress/Common/ArchiveEncoding.cs index 0b4328e7..14466c7c 100644 --- a/src/SharpCompress/Common/ArchiveEncoding.cs +++ b/src/SharpCompress/Common/ArchiveEncoding.cs @@ -49,6 +49,11 @@ namespace SharpCompress.Common #if NETSTANDARD1_0 return Decode(bytes, 0, bytes.Length); #else + //allow forced to override this. + if (Forced != null) + { + return Decode(bytes, 0, bytes.Length); + } var extendedAsciiEncoding = Encoding.GetEncoding(437); return extendedAsciiEncoding.GetString(bytes, 0, bytes.Length); #endif From 473f5d8189b64c94b771dce1ca4a2a60f22ec649 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Mon, 14 May 2018 16:20:57 +0100 Subject: [PATCH 119/124] Make GetDecoder use GetEncoding for forced --- src/SharpCompress/Common/ArchiveEncoding.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SharpCompress/Common/ArchiveEncoding.cs b/src/SharpCompress/Common/ArchiveEncoding.cs index 14466c7c..ad33ee74 100644 --- a/src/SharpCompress/Common/ArchiveEncoding.cs +++ b/src/SharpCompress/Common/ArchiveEncoding.cs @@ -76,7 +76,7 @@ namespace SharpCompress.Common public Func GetDecoder() { - return CustomDecoder ?? ((bytes, index, count) => (Default ?? Encoding.UTF8).GetString(bytes, index, count)); + return CustomDecoder ?? ((bytes, index, count) => GetEncoding().GetString(bytes, index, count)); } } } \ No newline at end of file From 2314776f55f208315659804f50944cbbd63c95ab Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Tue, 15 May 2018 08:28:11 +0100 Subject: [PATCH 120/124] Also check for CustomDecoder --- src/SharpCompress/Common/ArchiveEncoding.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SharpCompress/Common/ArchiveEncoding.cs b/src/SharpCompress/Common/ArchiveEncoding.cs index ad33ee74..c18b65c9 100644 --- a/src/SharpCompress/Common/ArchiveEncoding.cs +++ b/src/SharpCompress/Common/ArchiveEncoding.cs @@ -49,8 +49,8 @@ namespace SharpCompress.Common #if NETSTANDARD1_0 return Decode(bytes, 0, bytes.Length); #else - //allow forced to override this. - if (Forced != null) + //allow forced and custom to override this. + if (CustomDecoder != null || Forced != null) { return Decode(bytes, 0, bytes.Length); } From 87e57e3a9af7082366a99417b8638827727459db Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Tue, 15 May 2018 09:14:56 +0100 Subject: [PATCH 121/124] Mark for 0.21.1 --- .circleci/config.yml | 2 +- src/SharpCompress/SharpCompress.csproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f9bd67fe..9150de77 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: microsoft/dotnet:2.0.7-sdk-2.1.105 + - image: microsoft/dotnet:2.0.7-sdk-2.1.200 steps: - checkout - run: diff --git a/src/SharpCompress/SharpCompress.csproj b/src/SharpCompress/SharpCompress.csproj index 050b7b43..b331798c 100644 --- a/src/SharpCompress/SharpCompress.csproj +++ b/src/SharpCompress/SharpCompress.csproj @@ -2,9 +2,9 @@ SharpCompress - Pure C# Decompression/Compression en-US - 0.21.0 - 0.21.0 - 0.21.0 + 0.21.1 + 0.21.1 + 0.21.1 Adam Hathcock net45;net35;netstandard1.0;netstandard1.3;netstandard2.0 true From 3a747ba87e11e43cc97dbb280e5d149524a2e1ed Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Wed, 16 May 2018 08:51:33 +0100 Subject: [PATCH 122/124] Update USAGE with new stream handling --- USAGE.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/USAGE.md b/USAGE.md index 93f89b51..162b30ba 100644 --- a/USAGE.md +++ b/USAGE.md @@ -1,8 +1,8 @@ # SharpCompress Usage -## Stream Rules +## Stream Rules (changed with 0.21) -When dealing with Streams, the rule should be that you don't close a stream you didn't create. This, in effect, should mean you should always put a Stream in a using block to dispose it. +When dealing with Streams, the rule should be that you don't close a stream you didn't create. This, in effect, should mean you should always put a Stream in a using block to dispose it. However, the .NET Framework often has classes that will dispose streams by default to make things "easy" like the following: @@ -23,13 +23,12 @@ using (var reader = new StreamReader(fileStream)) } ``` -To deal with the "correct" rules as well as the expectations of users, I've decided on this: - -* When writing, leave streams open. -* When reading, close streams +To deal with the "correct" rules as well as the expectations of users, I've decided to always close wrapped streams as of 0.21. 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. + ## Samples Also, look over the tests for more thorough [examples](https://github.com/adamhathcock/sharpcompress/tree/master/tests/SharpCompress.Test) From 7b7af612bafab0551b16a4b90e9f39fd9e581495 Mon Sep 17 00:00:00 2001 From: itn3000 Date: Wed, 23 May 2018 09:46:36 +0900 Subject: [PATCH 123/124] add example for custom file encoding --- USAGE.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/USAGE.md b/USAGE.md index 162b30ba..a7a7f0d6 100644 --- a/USAGE.md +++ b/USAGE.md @@ -127,3 +127,20 @@ using (var writer = WriterFactory.Open(stream, ArchiveType.Tar, new WriterOption writer.WriteAll("D:\\temp", "*", SearchOption.AllDirectories); } ``` + +### Extract zip which has non-utf8 encoded filename(cp932) + +```C# +var opts = new SharpCompress.Readers.ReaderOptions(); +var encoding = Encoding.GetEncoding(932); +opts.ArchiveEncoding = new SharpCompress.Common.ArchiveEncoding(); +opts.ArchiveEncoding.CustomDecoder = (data, x, y) => +{ + return encoding.GetString(data); +}; +var tr = SharpCompress.Archives.Zip.ZipArchive.Open("test.zip", opts); +foreach(var entry in tr.Entries) +{ + Console.WriteLine($"{entry.Key}"); +} +``` \ No newline at end of file From 540618c0620fcf4b0261bf655c74023a2e9c784d Mon Sep 17 00:00:00 2001 From: Julien Lebosquain Date: Sun, 27 May 2018 00:50:16 +0200 Subject: [PATCH 124/124] Implemented ReadByte/WriteByte on streams to improve performance --- src/SharpCompress/Common/EntryStream.cs | 10 ++++++++++ .../Common/Tar/TarReadOnlySubStream.cs | 16 ++++++++++++++++ .../Compressors/BZip2/BZip2Stream.cs | 10 ++++++++++ .../Compressors/BZip2/CBZip2InputStream.cs | 4 ++++ .../Compressors/BZip2/CBZip2OutputStream.cs | 5 +++++ .../Compressors/Deflate/DeflateStream.cs | 18 ++++++++++++++++++ .../Compressors/Deflate/ZlibStream.cs | 18 ++++++++++++++++++ .../Compressors/LZMA/Bcj2DecoderStream.cs | 16 ++++++++++++++++ .../Compressors/LZMA/LZipStream.cs | 8 ++++++++ .../Compressors/Xz/Filters/Lzma2Filter.cs | 5 +++++ src/SharpCompress/Crypto/Crc32Stream.cs | 15 ++++++++++++++- .../IO/CountingWritableSubStream.cs | 8 +++++++- src/SharpCompress/IO/ListeningStream.cs | 18 ++++++++++++++++++ src/SharpCompress/IO/NonDisposingStream.cs | 10 ++++++++++ src/SharpCompress/IO/ReadOnlySubStream.cs | 14 ++++++++++++++ tests/SharpCompress.Test/ForwardOnlyStream.cs | 8 +++++++- tests/SharpCompress.Test/Zip/Zip64Tests.cs | 6 ++++++ 17 files changed, 186 insertions(+), 3 deletions(-) diff --git a/src/SharpCompress/Common/EntryStream.cs b/src/SharpCompress/Common/EntryStream.cs index 4697ff8c..8d40cabd 100644 --- a/src/SharpCompress/Common/EntryStream.cs +++ b/src/SharpCompress/Common/EntryStream.cs @@ -66,6 +66,16 @@ namespace SharpCompress.Common return read; } + public override int ReadByte() + { + int value = _stream.ReadByte(); + if (value == -1) + { + _completed = true; + } + return value; + } + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); diff --git a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs index d7ea3040..25300840 100644 --- a/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs +++ b/src/SharpCompress/Common/Tar/TarReadOnlySubStream.cs @@ -72,6 +72,22 @@ namespace SharpCompress.Common.Tar return read; } + public override int ReadByte() + { + if (BytesLeftToRead <= 0) + { + return -1; + } + int value = Stream.ReadByte(); + if (value != -1) + { + --BytesLeftToRead; + ++_amountRead; + } + return value; + + } + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); diff --git a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs index 4566bf03..c0f040a9 100644 --- a/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs +++ b/src/SharpCompress/Compressors/BZip2/BZip2Stream.cs @@ -67,6 +67,11 @@ namespace SharpCompress.Compressors.BZip2 return stream.Read(buffer, offset, count); } + public override int ReadByte() + { + return stream.ReadByte(); + } + public override long Seek(long offset, SeekOrigin origin) { return stream.Seek(offset, origin); @@ -82,6 +87,11 @@ namespace SharpCompress.Compressors.BZip2 stream.Write(buffer, offset, count); } + public override void WriteByte(byte value) + { + stream.WriteByte(value); + } + /// /// Consumes two bytes to test if there is a BZip2 header /// diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs index 4168db4b..68d924b4 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2InputStream.cs @@ -1077,6 +1077,10 @@ namespace SharpCompress.Compressors.BZip2 { } + public override void WriteByte(byte value) + { + } + public override bool CanRead => true; public override bool CanSeek => false; diff --git a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs index 38922294..e95ddb36 100644 --- a/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs +++ b/src/SharpCompress/Compressors/BZip2/CBZip2OutputStream.cs @@ -1929,6 +1929,11 @@ namespace SharpCompress.Compressors.BZip2 return 0; } + public override int ReadByte() + { + return -1; + } + public override long Seek(long offset, SeekOrigin origin) { return 0; diff --git a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs index a771d66e..e095f460 100644 --- a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs +++ b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs @@ -282,6 +282,15 @@ namespace SharpCompress.Compressors.Deflate return _baseStream.Read(buffer, offset, count); } + public override int ReadByte() + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + return _baseStream.ReadByte(); + } + /// /// Calling this method always throws a . /// @@ -340,6 +349,15 @@ namespace SharpCompress.Compressors.Deflate _baseStream.Write(buffer, offset, count); } + public override void WriteByte(byte value) + { + if (_disposed) + { + throw new ObjectDisposedException("DeflateStream"); + } + _baseStream.WriteByte(value); + } + #endregion public MemoryStream InputBuffer => new MemoryStream(_baseStream._z.InputBuffer, _baseStream._z.NextIn, diff --git a/src/SharpCompress/Compressors/Deflate/ZlibStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibStream.cs index 6a644f39..c82040ef 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibStream.cs @@ -270,6 +270,15 @@ namespace SharpCompress.Compressors.Deflate return _baseStream.Read(buffer, offset, count); } + public override int ReadByte() + { + if (_disposed) + { + throw new ObjectDisposedException("ZlibStream"); + } + return _baseStream.ReadByte(); + } + /// /// Calling this method always throws a . /// @@ -321,6 +330,15 @@ namespace SharpCompress.Compressors.Deflate _baseStream.Write(buffer, offset, count); } + public override void WriteByte(byte value) + { + if (_disposed) + { + throw new ObjectDisposedException("ZlibStream"); + } + _baseStream.WriteByte(value); + } + #endregion System.IO.Stream methods } } \ No newline at end of file diff --git a/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs b/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs index 3dc78c3a..4bdac69b 100644 --- a/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs +++ b/src/SharpCompress/Compressors/LZMA/Bcj2DecoderStream.cs @@ -193,6 +193,22 @@ namespace SharpCompress.Compressors.LZMA return count; } + public override int ReadByte() + { + if (_mFinished) + { + return -1; + } + + if (!_mIter.MoveNext()) + { + _mFinished = true; + return -1; + } + + return _mIter.Current; + } + public IEnumerable Run() { const uint kBurstSize = (1u << 18); diff --git a/src/SharpCompress/Compressors/LZMA/LZipStream.cs b/src/SharpCompress/Compressors/LZMA/LZipStream.cs index 5cedeb6c..588386f5 100644 --- a/src/SharpCompress/Compressors/LZMA/LZipStream.cs +++ b/src/SharpCompress/Compressors/LZMA/LZipStream.cs @@ -110,6 +110,8 @@ namespace SharpCompress.Compressors.LZMA public override int Read(byte[] buffer, int offset, int count) => _stream.Read(buffer, offset, count); + public override int ReadByte() => _stream.ReadByte(); + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); public override void SetLength(long value) => throw new NotImplementedException(); @@ -120,6 +122,12 @@ namespace SharpCompress.Compressors.LZMA _writeCount += count; } + public override void WriteByte(byte value) + { + _stream.WriteByte(value); + ++_writeCount; + } + #endregion /// diff --git a/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs b/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs index 66d02e91..cd2869c9 100644 --- a/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs +++ b/src/SharpCompress/Compressors/Xz/Filters/Lzma2Filter.cs @@ -51,5 +51,10 @@ namespace SharpCompress.Compressors.Xz.Filters { return BaseStream.Read(buffer, offset, count); } + + public override int ReadByte() + { + return BaseStream.ReadByte(); + } } } diff --git a/src/SharpCompress/Crypto/Crc32Stream.cs b/src/SharpCompress/Crypto/Crc32Stream.cs index 13cbc6e2..35dd85bd 100644 --- a/src/SharpCompress/Crypto/Crc32Stream.cs +++ b/src/SharpCompress/Crypto/Crc32Stream.cs @@ -47,6 +47,12 @@ namespace SharpCompress.Crypto hash = CalculateCrc(table, hash, buffer, offset, count); } + public override void WriteByte(byte value) + { + stream.WriteByte(value); + hash = CalculateCrc(table, hash, value); + } + public override bool CanRead => stream.CanRead; public override bool CanSeek => false; public override bool CanWrite => stream.CanWrite; @@ -98,9 +104,16 @@ namespace SharpCompress.Crypto unchecked { for (int i = offset, end = offset + count; i < end; i++) - crc = (crc >> 8) ^ table[(crc ^ buffer[i]) & 0xFF]; + { + crc = CalculateCrc(table, crc, buffer[i]); + } } return crc; } + + private static uint CalculateCrc(uint[] table, uint crc, byte b) + { + return (crc >> 8) ^ table[(crc ^ b) & 0xFF]; + } } } diff --git a/src/SharpCompress/IO/CountingWritableSubStream.cs b/src/SharpCompress/IO/CountingWritableSubStream.cs index c08235c6..17ddf6bb 100644 --- a/src/SharpCompress/IO/CountingWritableSubStream.cs +++ b/src/SharpCompress/IO/CountingWritableSubStream.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace SharpCompress.IO @@ -49,5 +49,11 @@ namespace SharpCompress.IO writableStream.Write(buffer, offset, count); Count += (uint)count; } + + public override void WriteByte(byte value) + { + writableStream.WriteByte(value); + ++Count; + } } } \ No newline at end of file diff --git a/src/SharpCompress/IO/ListeningStream.cs b/src/SharpCompress/IO/ListeningStream.cs index 1cc694e0..a5a76dd1 100644 --- a/src/SharpCompress/IO/ListeningStream.cs +++ b/src/SharpCompress/IO/ListeningStream.cs @@ -47,6 +47,19 @@ namespace SharpCompress.IO return read; } + public override int ReadByte() + { + int value = Stream.ReadByte(); + if (value == -1) + { + return -1; + } + + ++currentEntryTotalReadBytes; + listener.FireCompressedBytesRead(currentEntryTotalReadBytes, currentEntryTotalReadBytes); + return value; + } + public override long Seek(long offset, SeekOrigin origin) { return Stream.Seek(offset, origin); @@ -61,5 +74,10 @@ namespace SharpCompress.IO { Stream.Write(buffer, offset, count); } + + public override void WriteByte(byte value) + { + Stream.WriteByte(value); + } } } \ No newline at end of file diff --git a/src/SharpCompress/IO/NonDisposingStream.cs b/src/SharpCompress/IO/NonDisposingStream.cs index 319f9461..2c9e72c4 100644 --- a/src/SharpCompress/IO/NonDisposingStream.cs +++ b/src/SharpCompress/IO/NonDisposingStream.cs @@ -44,6 +44,11 @@ namespace SharpCompress.IO return Stream.Read(buffer, offset, count); } + public override int ReadByte() + { + return Stream.ReadByte(); + } + public override long Seek(long offset, SeekOrigin origin) { return Stream.Seek(offset, origin); @@ -58,5 +63,10 @@ namespace SharpCompress.IO { Stream.Write(buffer, offset, count); } + + public override void WriteByte(byte value) + { + Stream.WriteByte(value); + } } } \ No newline at end of file diff --git a/src/SharpCompress/IO/ReadOnlySubStream.cs b/src/SharpCompress/IO/ReadOnlySubStream.cs index a8449c41..8747019a 100644 --- a/src/SharpCompress/IO/ReadOnlySubStream.cs +++ b/src/SharpCompress/IO/ReadOnlySubStream.cs @@ -51,6 +51,20 @@ namespace SharpCompress.IO return read; } + public override int ReadByte() + { + if (BytesLeftToRead <= 0) + { + return -1; + } + int value = Stream.ReadByte(); + if (value != -1) + { + --BytesLeftToRead; + } + return value; + } + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); diff --git a/tests/SharpCompress.Test/ForwardOnlyStream.cs b/tests/SharpCompress.Test/ForwardOnlyStream.cs index d9cf85e3..c20b9b99 100644 --- a/tests/SharpCompress.Test/ForwardOnlyStream.cs +++ b/tests/SharpCompress.Test/ForwardOnlyStream.cs @@ -44,6 +44,11 @@ namespace SharpCompress.Test return stream.Read(buffer, offset, count); } + public override int ReadByte() + { + return stream.ReadByte(); + } + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); @@ -55,7 +60,8 @@ namespace SharpCompress.Test } public override void Write(byte[] buffer, int offset, int count) - {throw new NotSupportedException(); + { + throw new NotSupportedException(); } } } \ No newline at end of file diff --git a/tests/SharpCompress.Test/Zip/Zip64Tests.cs b/tests/SharpCompress.Test/Zip/Zip64Tests.cs index d1e105a6..dba69199 100644 --- a/tests/SharpCompress.Test/Zip/Zip64Tests.cs +++ b/tests/SharpCompress.Test/Zip/Zip64Tests.cs @@ -205,6 +205,9 @@ namespace SharpCompress.Test.Zip public override int Read(byte[] buffer, int offset, int count) { return stream.Read(buffer, offset, count); } + public override int ReadByte() + { return stream.ReadByte(); } + public override long Seek(long offset, SeekOrigin origin) { throw new NotImplementedException(); } @@ -213,6 +216,9 @@ namespace SharpCompress.Test.Zip public override void Write(byte[] buffer, int offset, int count) { stream.Write(buffer, offset, count); } + + public override void WriteByte(byte value) + { stream.WriteByte(value); } } } } \ No newline at end of file